Security and limits
This page states, plainly, what protects your platform when untrusted code runs in the sandbox — and what does not. The big clouds tend to bury this; we put it on one page, including the parts that are not flattering.
The security model in one paragraph
Model-generated code runs in a pod that is created before it is needed, used exactly once, and destroyed. The pod runs as a non-root user, holds no cluster credentials, and its network is cut off except for DNS. If the code loops forever, a hard timeout kills the whole process group. If it floods output, the pod's memory limit kills the pod — not the node. Nothing the code does in one run can affect the next run, because there is no next run in that pod.
One pod, one run
A sandbox pod is never used twice. Two consecutive executions always report different sandbox_id values. This is the property that AWS and Azure session-based sandboxes make optional (sessions persist and are reused); here it is the only mode. There is no state to scrub between calls because the entire environment is thrown away.
Files written during a run are destroyed with the pod. If your workflow needs state across runs, keep it in your agent's session, not in the sandbox.
No network egress (for code snippets)
Snippet pods (the ones behind run_python and /v1/execute) can make DNS lookups (port 53, UDP and TCP) and nothing else. An HTTP fetch from model-generated code fails:
curl -s -X POST "$SANDBOX/v1/execute" \
-H "Content-Type: application/json" \
-d '{"code":"import urllib.request\nurllib.request.urlopen(\"https://example.com\", timeout=5)","timeout_s":15}'
You should see (traceback trimmed):
{"sandbox_id":"sandbox-2m8rt","stdout":"","stderr":"Traceback (most recent call last):\n ...\nurllib.error.URLError: <urlopen error ...>","exit_code":1,"duration_ms":5041}
The name resolves; the connection never opens. Malicious code cannot call home, download payloads, or reach internal services.
DNS is allowed so name resolution failures don't masquerade as timeouts. Honest caveat: an open DNS channel can, in principle, be used to tunnel small amounts of data out via crafted lookups. AWS's equivalent product shipped with the same gap and had it publicly disclosed by researchers before it was patched. We are telling you up front instead.
Tool-call pods are different — on purpose
Sandboxed tool calls (/v1/call) run your own code from the agent's own image, so by default they can reach the public internet — a weather tool that cannot fetch weather is useless. What they can never reach:
169.254.0.0/16— cloud metadata endpoints (where instance credentials live),10.0.0.0/8,172.16.0.0/12,192.168.0.0/16— private and cluster-internal networks.
And the pod starts with an empty platform environment: no model API key, no store token, no messaging credential. The missing credentials are the security boundary — a prompt-injected tool call has nothing to steal. Internet egress for tool pods is a platform setting (toolCalls.allowInternetEgress, on by default).
Non-root, no capabilities
Sandbox pods run under Kubernetes' restricted security profile: a non-root user, no privilege escalation, all Linux capabilities dropped, and the default seccomp filter (a kernel-level syscall allowlist). The pod also has no ServiceAccount credentials, so code inside it cannot talk to the Kubernetes API at all.
Honest caveat: the default runtime is plain runc — standard container isolation, enforced by the shared Linux kernel. AWS uses micro-VMs and Azure uses Hyper-V isolation, which are stronger against kernel exploits. Clusters that have gVisor installed can enable it for sandbox pods via the runtimeClassName setting; it is empty by default.
Timeouts kill, memory limits contain
- Every run has a hard wall-clock limit (default 20 s, max 60 s for snippets). At the deadline the entire process group is killed; you get
exit_code: -1andstderr: "timed out after <N>s". Awhile True: passcannot hold a pod. - Output (stdout and stderr) is buffered in the pod's memory. A print flood hits the pod's 512 MiB memory limit and the pod is killed — the node, and everyone else's workloads, are unaffected.
All limits in one table
| Limit | Value |
|---|---|
| Language | Python only |
Snippet timeout (/v1/execute) | default 20 s, max 60 s |
Tool-call timeout (/v1/call) | default 30 s, max 120 s |
| Warm pool size | 3 pods (platform-managed) |
| Wait when the pool is empty | 30 s, then 503 no warm sandbox pod available, try again shortly |
| Snippet pod resources | 1 CPU, 512 MiB memory (hard limits) |
| Tool-call pod resources | 2 CPU, 1 GiB memory (hard limits) |
| Tool-call request body | 1 MiB |
| Tool-call response | 8 MiB |
| Tool pod readiness deadline | 110 s (scheduling + image pull; there is no warm pool for tool calls) |
| Input file paths | must stay inside the working directory |
| Pod reuse | never |
| Files after a run | destroyed with the pod |
What this model does not do — the honest list
- The sandbox service itself has no authentication.
sandboxdis protected purely by network position: it is reachable only inside the cluster, and agents receive its address through the reservedSANDBOX_URLenvironment variable, which cannot be overridden — so an agent cannot be repointed at a fake sandbox. It must never be exposed outside the cluster. - Tool sandboxing can be switched off. Setting
TOOL_SANDBOX=falseon an agent runs tool code inside the agent's own pod, next to its credentials. Only do this if you fully trust every tool. The reverse failure is safe: if the sandbox is unreachable, the agent fails closed — it errors rather than silently running the code beside its credentials. - Kernel isolation, not hardware isolation, by default. See the runc note above.
- DNS egress is open in snippet pods. See the warning above.
Summary checklist
| Property | Snippet pods (run_python) | Tool-call pods |
|---|---|---|
| Used more than once | Never | Never |
| Internet access | No (DNS only) | Yes by default; metadata + private ranges always blocked |
| Platform credentials inside | None | None |
| Runs as root | No | No |
| Hard timeout | 20 s default, 60 s max | 30 s default, 120 s max |
| Warm pool (fast start) | Yes (~1 s) | No (pays pod start + image pull) |