How to Sandbox an AI Coding Agent (and What Fails)
Built-in agent sandboxes are configured from files the agent can edit. The three isolation tiers, the checklist that holds, and commands to verify your own.
Short answer: if the agent can edit the file that configures its own sandbox, you do not have a sandbox. Run the agent in a container started from outside the workspace, holding no long-lived credentials, with a scoped short-lived token and a deny-by-default egress list. That covers most real incidents for $0. Move to hypervisor isolation only when you execute other people’s code, share a host between tenants, or a bad run can reach production.
Symptoms and risk signals
- Sandbox settings live in a file inside the directory the agent is editing, such as
.claude/settings.json. - The agent runs with
--dangerously-skip-permissionsor an equivalent auto-approve mode. - A cloned third-party repository is opened directly with an agent enabled.
envinside the agent’s environment returns any long-lived token.- You cannot state, without checking, whether the agent can reach the public internet.
The core failure: the switch is inside the box
Four open issues on anthropics/claude-code describe the same structural problem. Titles were fetched and confirmed verbatim on 2026-08-22; these are user reports and open issues, not confirmed vendor behaviour.
| Issue | What it reports |
|---|---|
| #86504 | Project-scope settings.json can bypass sandbox isolation via sandbox.enabled: false |
| #84863 | Filesystem reads are unrestricted, and the agent can silently break its own sandbox enforcement by editing settings.json |
| #87381 | Sandbox network allowlist bypassed via in-sandbox relayed CONNECT, while direct CONNECT is correctly blocked |
| #83760 | A denied tool call was executed anyway — a PowerShell tool ran despite “deny” |
Read the first two together and the shape appears: isolation is controlled by a file in the workspace, and the agent’s job is editing files in the workspace. #86504 makes it worse than self-inflicted — a cloned repository can carry {"sandbox": {"enabled": false}}, so isolation is off before the agent does anything.
#83760 matters because it removes the human fallback. The user clicked deny, the model received the denial, and the tool ran. Any design that ends in “the operator will catch it” depends on that path working.
The three isolation tiers
The distinction that matters is who can change the rules, not which technology is fashionable.
| Tier | Enforced by | Agent can reconfigure it? |
|---|---|---|
| Process (allowlists, permission prompts) | The same process running the agent | Often yes, via workspace files |
| Container (Docker, gVisor, Kata) | Host kernel, configured at start | No |
| MicroVM (Firecracker and equivalents) | Hypervisor, own guest kernel | No |
The jump that buys the most safety is process → container, because that is where configuration moves out of the agent’s reach. Container → microVM narrows the kernel attack surface, which matters when running code you did not write.
A Cursor forum answer describes its allowlist as “best-effort, not a security boundary” — an honest description of the whole first tier. One practitioner on Hacker News put the practical conclusion plainly: “I’ve been running Claude Code with --dangerously-skip-permissions in a Docker container… I definitely wouldn’t want to run it unsandboxed.”
The checklist that holds
Ordered by risk reduction per unit of effort.
- Remove long-lived credentials from the agent’s environment. No cloud keys, no production database URL, no org-wide personal access token. Highest-value change, costs nothing.
- Issue one scoped, short-lived credential. A repository-scoped token that expires beats a durable broad one even inside good isolation.
- Start the container from outside the workspace. Mount only the working directory. The point is not kernel hardening; it is that the config lives where the agent cannot edit it.
- Deny egress by default, then allow what the job needs. Usually a package registry and one API. #87381 shows allowlists getting relayed around, so keep the list short enough to reason about.
- Make the workspace disposable. If recovery means deleting a directory and re-cloning, most incidents become annoying rather than expensive.
Steps 1 and 5 do most of the work.
Verify your sandbox
Do not evaluate isolation by reading vendor pages. Test the boundary you have, in a throwaway workspace.
# Run inside the agent's environment. Prerequisite: a disposable workspace.
# 1. Credentials in reach — target output is nothing.
env | grep -Ei 'token|secret|key|password|_api'
# 2. Filesystem containment — should fail or be empty if confined.
ls -la / 2>&1 | head -5
cat ~/.aws/credentials 2>&1 | head -1
# 3. Egress — should fail if the allowlist is real.
curl -s -o /dev/null -w '%{http_code}\n' --max-time 5 https://example.com
Expected results for a contained environment: no output from step 1, permission errors or an empty result from step 2, and a timeout or non-200 from step 3. A 200 in step 3 means unrestricted outbound, which is the exfiltration path regardless of how well the filesystem is confined.
Then ask the agent itself to do the same things. The difference between what the shell can do and what the agent can do is the part vendor documentation will not tell you.
What each tier doesn’t stop
Credentials that walk in through the front door. A .env mounted with the workspace or an inherited shell environment defeats every tier. Isolation intact, secret gone.
Relayed egress. #87381 reports direct CONNECT blocked and relayed CONNECT allowed. Anything the agent can start inside the boundary can potentially proxy for it.
Config drift. If any containment setting lives in the repository, a rebase, merge, or clone can change it silently. Keep containment config outside the version control the agent touches.
Spend. A perfectly contained agent can still burn your budget inside the box. Containment answers what it can reach, never what it cost — that is a separate meter, covered in why your LLM spend limit doesn’t actually stop spending.
The vendor documentation gap
If you outsource containment to a managed sandbox, the next question is what it may connect to. A documentation check across E2B, Modal, Daytona and Runloop found egress policy undocumented on all four.
The trackers show the same gap: Daytona #4463 is an open request for native egress traffic routing, and #3055 is a user working out network access for a local deployment. Reasonable requests — and a signal that the control you assume exists may not.
When it’s actually something else
If the agent is doing destructive things inside a correctly configured boundary, the problem is permission design rather than isolation: look at auto-approve modes and #83760-class behaviour, not at your container runtime.
If the concern is cost rather than damage, isolation is the wrong lever entirely. Containment does not meter anything.
Reference
Get the next verdict before it's everywhere.
One email when a new lab post or cost table ships. No spam, no confirmation step — unsubscribe anytime.