Sandboxing¶
What each backend actually enforces, and when the difference matters.
A task runs commands that a model wrote after reading documentation you do
not control. Some quickstarts still say curl ... | bash. The agent will
follow that instruction.
The three backends¶
| Backend | Filesystem | Network | Use for |
|---|---|---|---|
docker |
container, workspace bind-mounted | internal network with no route out; the proxy sidecar is the only exit | anything, including CI |
seatbelt (macOS) |
reads of your home denied, writes confined to the workspace | all egress denied except the proxy port | local development |
local |
none | none; the proxy variables are advisory | tasks and projects you wrote yourself |
auto prefers Docker, then Seatbelt, then local. Every result records which
backend ran and whether it was enforced, so a number can always be traced back
to the conditions that produced it.
Why local is refused¶
REFUSING: no enforced execution backend is available, so the docs allowlist
and the page-read record cannot be guaranteed.
With no boundary, a command can reach any host it likes. That breaks the
central claim, because a documentation page fetched by curl never appears in
the trace and the "last page read before failure" stops being true.
--allow-unenforced exists for the case where you wrote both the task and
the project. Results from it are still recorded as unenforced.
What Docker does¶
The sandbox container attaches only to a network created with --internal,
which has no route off the host. The single reachable address is the sidecar,
which sits on both that network and a normal bridge. The container also runs
with --cap-drop ALL, no-new-privileges, and a pid limit.
The isolation does not depend on the agent honouring HTTP_PROXY. From inside,
a direct dial to a raw IP address fails:
socket.create_connection(("93.184.215.14", 443), timeout=10)
# OSError: [Errno 101] Network is unreachable
A task that needs a different base names one, which is how a suite mixes a Python quickstart and a Node one in a single run:
--image sets the default for tasks that do not name one. See
image.
The default is python:3.12-slim, which has no curl and no Node. Success
scripts that need curl should install it or use Python instead.
What Seatbelt does¶
macOS ships sandbox-exec, which applies a policy in the kernel. quickstarted
generates a profile that denies everything by default, then allows reads of the
system prefix, reads and writes inside the workspace, and outbound connections
to one loopback port: the harness proxy.
(deny default)
(allow file-read*)
(deny file-read* (subpath "/Users/you"))
(allow file-write* (subpath "/var/folders/.../quickstarted-abc123"))
(allow network-outbound (remote ip "localhost:53821"))
A bypass attempt dies in the kernel:
Apple has deprecated sandbox-exec, and it remains functional. Treat it as
good hygiene for local work and use containers for projects you do not control.
What never enters the sandbox¶
The executor builds the environment from scratch. Only PATH, HOME,
TMPDIR, locale variables, and the proxy settings are passed through. No API
key reaches a command under any backend, and a test asserts it.
More detail on the network half: the egress proxy.