Run Claude Managed Agents in Prized Sandboxes
Run each Claude Managed Agents session's tools in a Prized sandbox of its own: a self-hosted environment, a spawn script for Anthropic's worker, and the limits.
On this page9
Claude Managed Agents can run an agent's tools on machines you control, through a self-hosted environment. This guide gives each session its own sandbox: Claude runs on Anthropic's side, and the sandbox runs the agent's tool calls and holds its files.
How it works
- You start a session on a self-hosted environment, and Anthropic queues it.
ant beta:worker poll, on a machine you keep running, claims the session and runsspawn.sh.spawn.shcreates a sandbox, installs Anthropic'santCLI in it without root, and startsant beta:worker run, which runs the session's tool calls.- A minute after the session goes idle, the worker exits, and
spawn.shsaves the output and deletes the sandbox. - A message that wakes the session again starts the cycle over, in a new sandbox that gets the session's files back.
Nothing connects in: the poller and the worker call out to api.anthropic.com over HTTPS.
Before you start
- An agent and a self-hosted environment. Create an agent with Anthropic's quickstart. In the Claude Console, choose Environments → New → Self-hosted, then Generate environment key on the environment's page.
- A Prized API key with Full access, or Custom with Sandboxes: Write (Sandboxes: Quickstart).
- A poller machine that stays on, Linux or macOS, with
ant,jq, andprizedv0.21.0 or later (CLI).
1. Set the keys
On the poller machine:
export ANTHROPIC_ENVIRONMENT_ID=env_…
export ANTHROPIC_ENVIRONMENT_KEY=sk-ant-oat01-…
export PRIZED_API_KEY=dcp_…The poller needs the environment key, never your Claude API key.
2. Save the spawn script
Save this as spawn.sh in the folder you run the poller from, and run chmod +x spawn.sh.
#!/bin/bash
# spawn.sh: `ant beta:worker poll --on-work ./spawn.sh` runs this once per work item (a session's run until it goes idle), with the session's ids and the environment key in ANTHROPIC_* variables and the work item as JSON on stdin.
set -euo pipefail
ANT_VERSION=1.40.0
MAX_MIN=60 # the sandbox's lifetime in minutes: up to 1440 after a top-up or a plan
ANTHROPIC_WORK_SECRET=$(jq -r '.secret // empty')
DIR="sessions/$ANTHROPIC_SESSION_ID" # the session's working directory as work.tgz, and a log per work item
OUT="$DIR/$ANTHROPIC_WORK_ID"
mkdir -p "$DIR"
# Run the session in the background, so the poller can claim the next one now.
(
# One run of a session at a time, so a follow-up starts from the files the run before it saved.
# A run ends with its sandbox, so a lock older than that was left by a run that was killed.
until mkdir "$DIR/lock" 2>/dev/null; do
find "$DIR/lock" -maxdepth 0 -mmin +$((MAX_MIN + 10)) -exec rmdir {} \; 2>/dev/null || true
sleep 5
done
trap 'rmdir "$DIR/lock"' EXIT
SECONDS=0 # from here, the sandbox's lifetime
SBX=$(prized sandbox create --no-wait --template prized-base-v1 --max-runtime "$MAX_MIN" --label "anthropic-session=$ANTHROPIC_SESSION_ID")
echo "sandbox: $SBX"
trap 'prized sandbox rm "$SBX" || true; rmdir "$DIR/lock"' EXIT # until the session is over, a failed step deletes the sandbox
# Hand the worker its keys in a file, through stdin: other users of this machine can read a command line.
for v in ANTHROPIC_ENVIRONMENT_ID ANTHROPIC_ENVIRONMENT_KEY ANTHROPIC_SESSION_ID ANTHROPIC_WORK_ID ANTHROPIC_WORK_SECRET; do printf 'export %s=%q\n' "$v" "${!v}"; done |
prized sandbox cp - "$SBX:~/worker.env"
# A session that went idle and woke again carries on with its files.
if [ -e "$DIR/work.tgz" ]; then
prized sandbox cp "$DIR/work.tgz" "$SBX:~/work.tgz"
prized sandbox exec "$SBX" -- 'tar xzf ~/work.tgz -C ~/work'
fi
# Install the ant CLI without root, and start the worker. It stops 5 minutes before the sandbox's lifetime ends, to leave time to save its output.
prized sandbox exec "$SBX" -- "mkdir -p ~/.local/bin && curl -fsSL https://github.com/anthropics/anthropic-cli/releases/download/v$ANT_VERSION/ant_${ANT_VERSION}_linux_amd64.tar.gz | tar -xz -C ~/.local/bin ant"
prized sandbox exec "$SBX" -- "nohup bash -c '. ~/worker.env; timeout $((MAX_MIN * 60 - 300 - SECONDS)) ~/.local/bin/ant beta:worker run --workdir /home/prized/work; touch ~/worker.done' > ~/worker.log 2>&1 &"
# The worker exits a minute after the session goes idle, and leaves ~/worker.done behind. Check every 30 seconds.
until prized sandbox exec "$SBX" -- 'test -e ~/worker.done' || [ "$SECONDS" -ge $((MAX_MIN * 60 - 240)) ]; do sleep 30; done
# Save the working directory and the worker's log, then delete the sandbox. If saving fails, the sandbox and the session's lock stay until its lifetime ends.
trap - EXIT
prized sandbox exec "$SBX" -- 'tar czf ~/work.tgz -C ~/work .'
prized sandbox cp "$SBX:~/work.tgz" "$DIR/work.tgz"
prized sandbox cp "$SBX:~/worker.log" "$OUT.worker.log"
prized sandbox rm "$SBX" || true
rmdir "$DIR/lock" # after the delete, so a follow-up's create finds the sandbox's slot free
) > "$OUT.log" 2>&1 < /dev/null &MAX_MINis each sandbox's lifetime in minutes: 60, the most the free tier allows. After a top-up or a plan, it can be up to 1,440 (24 hours). The worker is stopped 5 minutes before the end, so its output can still be saved.ANT_VERSIONpins theantrelease; newer ones are on GitHub.- Sessions run side by side, a sandbox each: the script returns at once, so the poller claims the next session while this one runs. A follow-up to one session waits until its previous run has saved.
- Output goes to
sessions/<session id>/: the working directory aswork.tgz, replaced whole when a later run of the session saves, and for each work item the script's log as<work id>.logand the worker's as<work id>.worker.log. - If saving fails, for example on an archive over the 100 MB a copy takes, the sandbox is kept until its lifetime ends, and the session's next run waits until then: copy what you need with
prized sandbox cp, then delete the sandbox andsessions/<session id>/lock. Its id is at the top of the work item's log.
3. Start the poller
ant beta:worker poll --on-work ./spawn.sh4. Run a session
Start a session on the environment and send it Write the output of "uname -a" to hello.txt in your working directory. While it runs, prized sandbox ls shows its sandbox, labelled anthropic-session=sesn_…. About a minute after the session goes idle, hello.txt is in sessions/sesn_…/work.tgz.
Keys in the sandbox
The environment key and the session's secret reach the sandbox in ~/worker.env, which the worker loads, so the agent's tool calls can read them. The key reaches only its environment's queue: use one environment per trust boundary (Security model). Never pass your Claude API key or your Prized API key into a sandbox.
Limits
| Limit | In a sandbox |
|---|---|
| Working directory | /home/prized/work: tools run as the user prized, without root, so there is no /workspace. If the agent uses skills, tell it in its system prompt that they are in /home/prized/work/skills |
| Memory stores | Not supported: they need a writable /mnt/memory |
| Files and repositories | Anthropic does not mount them on self-hosted environments; fetch them in spawn.sh before the worker starts (Stage files for a session) |
| Lifetime | 24 hours, or 60 minutes on the free tier; a session still running then loses its sandbox |
| At once | 4 sandboxes of up to 2 vCPU and 4 GB on the free tier; paid quotas are on Limits |
| Network | Outbound internet, with no allow or deny lists yet |
| Region | us-west-2 (Oregon) |
Prized Sandboxes and Claude Managed Agents are both in beta.
Troubleshooting
| Symptom | Fix |
|---|---|
The poller exits with 401 Unauthorized and OAuth access token is invalid | The environment key is wrong, revoked, or for another environment than ANTHROPIC_ENVIRONMENT_ID: generate a new one |
The poller exits with on-work script "./spawn.sh" is unrunnable | Run chmod +x spawn.sh, and start the poller in the script's folder |
| A session stays queued | Start the poller. From your own machine, with your Claude API key, ant beta:environments:work stats --environment-id env_… then shows workers_polling of 1 or more |
| A follow-up waits, and its log stays empty | The session's previous run is still going, or its save failed (If saving fails above). A lock left by a run that was killed, for example by a reboot, clears itself a little over MAX_MIN + 10 minutes after that run started |
A session's log starts with a prized: error | Prized refused the sandbox, so that session has none; the ↳ line says what to change. MAX_MIN above 60 needs a top-up or a plan. An error after the sandbox: line while saving keeps the sandbox: see If saving fails above |
A session goes idle at once, and its log says cannot create the memory store's folder | Start the session without memory stores |