Skip to main content

Sandboxes and Execution

Give an agent a sandbox and it gets one more tool, execute: a shell command run inside an isolated container, not on your machine. By the end of this page you will have an agent that runs Python in Docker against a file from its workspace, writes a result back, and leaves a record of every command.
Every output on this page is what the code printed when it was run, with Docker on Linux and gpt-5.4-mini. The model writes its own commands, so the commands and the wording will differ on your run.

A first run in Docker

1

Install the Docker extra

Docker must be running. The first run pulls the default image, python:3.12-slim, which can take a minute; later runs start in seconds.
2

Put a file in the workspace

The agent’s workspace is ./workspace/files/ by default. Create workspace/files/sales.csv:
3

Run the agent

workspace/files/total.txt now holds 450. The command ran in a container; the file it wrote came back to your workspace.

How it works

1

The tool appears only with a sandbox

execute is added when governance is on (the default) and the configured provider can run commands. With governance turned off ("enabled": False), or with provider none, the agent has no way to run a command.
2

Each command is authorized twice

The tool call is sandbox.execute; the command inside the sandbox is process.exec, on the sandbox surface (or host for the local provider). The development profiles allow sandboxed commands; strict-production allows none without a rule of yours (Policies).
3

One sandbox per run

The first command opens a session; later commands in the same run reuse it, so files persist between them. When the run ends — success, error, a pause for approval, or a cancel — the session is closed and the container removed. A resumed run opens a fresh one.
4

Workspace files go in, text outputs come back

Before each command, workspace files that changed since the last copy are uploaded into the sandbox’s working directory (/workspace). After it, text files the command created or changed are copied back as governed workspace writes. The sandbox never mounts your workspace.
The model sends command (run with sh -c in the working directory) and an optional timeout in seconds: 60 by default, at most the agent’s tool_call_timeout (180 by default). A command that exits non-zero or times out comes back to the model as an error, with its exit code, stdout and stderr.

With your own strict policy

A strict policy allows nothing it does not name, the sandbox included. These three rules let the agent use execute and run commands only inside the sandbox:
The process.exec decision is the model’s command. The last one is the runtime’s own: after a command, it lists what changed in the sandbox to copy it back to the workspace. That listing is governed too, as sandbox.workspace.sync (part of sandbox.*), and it is not judged as a command the agent asked for, so rules on shell commands do not apply to it. If no rule names it, as in a strict policy that allows commands but not the sync, it follows the decision for the command that triggered it: the trace shows an allow with the reason follows_command and the command’s request id, not a rule match. A rule you write for it still decides, so a deny rule stops the copy-back (the model is told so, and the command’s files stay in the sandbox). Every file it copies back is still decided by the workspace file rules, below. Leave out sandbox.* and the sandbox cannot start; leave out the process.exec rule and every command is refused. sandbox.network.configure is part of sandbox.* here: to keep the network behind a person, add an ask rule for it, as the development profiles do (Policy reference). Under these three rules alone no workspace file is copied into the sandbox or back: each copy is checked as a read or a write of that file, and a strict policy refuses what it does not name. To let files through, add the two file rules, with a path target to narrow them:

Rules on what a command runs

A process.exec rule can also name a command by its first words, with a command matcher: deny rm, ask before git push, allow only git status and git log. Only a plain chain of commands (git status && rm -rf build) is split and matched command by command. Anything else, such as a redirect, $(...), a variable, a glob, eval or sh -c, is one unreadable command: no rule allows it, and where the policy has command rules it is asked about (refused in strict), so eval "rm -rf ~" does not pass a deny on rm. Wrappers are not looked inside: sudo rm is a command named sudo. Command rules decide when to ask; what a command can touch is the sandbox’s to decide. See Rules on shell commands.

Files between the workspace and the sandbox

What is copied, and what is not: Limits: 500 files, 2 MB per file and 20 MB per copy. To copy less, give glob patterns over workspace paths (* crosses folders; no include copies everything not excluded). The patterns apply both ways: a file outside them is not copied in, and a file a command creates or changes outside them is not copied back either (it is skipped, with the reason “outside the bridge’s include/exclude patterns”):
With enable_workspace_files: False nothing is copied. With the local provider nothing is copied either: commands already run on the machine, so point its working_dir at the workspace directory instead.

The network is off

A sandbox has no network unless its manifest asks for it, and the policy allows that. Here the agent looks at its own sandbox:
No network, and none of your process’s variables: LLM_API_KEY is not there. The variables listed come from the python:3.12-slim image itself (GPG_KEY is the ID of the key Python’s releases are signed with). To turn the network on, ask for it in sandbox_manifest. The development profiles ask a person first (rule ask_sandbox_network), so the run pauses before its first command:
"allow" opens the whole internet. To allow only named hosts, use "allowed_hosts": ["pypi.org", "*.pythonhosted.org"] on a provider that enforces a host allowlist; Docker, E2B, Vercel and local cannot, and the agent is refused when it is built rather than given more network than it asked for. Which providers enforce what is on Sandbox providers.

What the manifest can ask for

sandbox_config says who runs the sandbox; sandbox_manifest says what it is. Each thing it asks for is a policy question when the session opens: A setting the chosen provider does not apply is refused when the agent is built, naming the provider and the setting: a workspace mount works on Docker only, a host allowlist on Modal and http only, CPU and memory limits everywhere but E2B. There is no list of paths a sandboxed command may read or write: it reaches the sandbox’s own disk and, on Docker, the workspace mount; filesystem_policy was removed, since no provider applied it. lifecycle does not change the agent’s own sandbox, which is closed when its run ends: cleanup ("always", "on_success", "never") applies only to a one-off command run through the sandbox service without a session, and nothing acts on snapshot yet. A manifest with an unknown field, or one that sets provider or sandbox_id, is refused when the agent is built.

Secrets stay out

A sandbox gets only the environment you give it in sandbox_manifest["environment"]["plain"] — never your process’s environment, and never your model key or provider credentials, which stay in your process with the policy and the telemetry. Docker also never mounts the Docker socket, system directories or credential folders such as ~/.ssh or ~/.aws, whatever the policy says (Security model).
Put nothing secret in plain: the model’s commands can read it and print it. environment.secret_refs is refused when the agent is built, since no built-in provider delivers secrets to a command. Keep a secret in a tool that runs in your process, and let the model call the tool.

What a command’s record holds

Each command is in the run’s trajectory under the tool call that ran it (call["executions"]): call["workspace_sync"] lists what was copied in, written back and skipped (with the reason), and trajectory["totals"]["executions"] counts sessions, commands, failures and timeouts. The trace also has the session itself, as sandbox_session_created and sandbox_session_closed events: provider, the provider’s own name for the sandbox (sandbox_ref, the container ID for Docker), image, network, working directory, and on close the number of commands, how long it was open, and lost. See Observability.

A sandbox that dies

A sandbox can die under a run: the provider stopped it, its lifetime ran out, someone removed it, or a command ignored its time limit and the sandbox was stopped. That is not reported as a slow command. The model is told:
The next command opens a fresh sandbox, and the trace records the old session closed with lost: true. A run that pauses for approval, or whose process dies, continues where it stopped (Durable runs).

Options

Every agent setting is in the agent settings reference; every capability in the policy reference.

When things go wrong

The tool is not there. Check that governance is not turned off ("enabled": False) and that governance_config has a sandbox_config whose provider runs commands (none does not). Do not add "enabled": True to fix it: without a profile that switches to interactive-dev. A model that asks for it anyway gets:
The command ran longer than the timeout the model gave it. It is killed inside the sandbox and the model receives:
The model can retry with a longer timeout, up to tool_call_timeout.
The command failed; the model sees its stderr and usually corrects itself:
The sandbox has no network, as intended. Either build an image with what your tasks need and name it ("sandbox_manifest": {"image": "..."}), or allow the network in the manifest (above).
Raised when the agent is built with allowed_hosts on a provider that cannot keep it:
Use "default": "allow", or Modal or your own http service.
With "pull": "never", the image must already be on the Docker host. The command’s call fails with:

Next

Sandbox providers

Docker, E2B, Daytona, Modal, Vercel, your own service, or this machine.

Policies

Allow, ask or deny each command, image and network request.

Approvals

Pause a run for a person, and resume it later.

Budgets

Cap dollars, tokens, calls and sandbox seconds.

Security model

What is enforced, and where the boundaries are.

Privacy

What the record of a command keeps.