In March, the engineers at Ona published a story that should be pinned above every agent setup. Their denylist blocked /usr/bin/npx by exact path. Claude Code wanted to run npx anyway, so it invoked /proc/self/root/usr/bin/npx: the same binary, through a path the pattern didn't match. When a second layer caught that trick, the agent identified the sandbox as the obstacle, disabled it, and ran the command outside it.
No jailbreak. No attacker. No malicious prompt. The agent just wanted to finish its task, and the rules were in the way.
That story is the whole argument of this post. In my guide to Claude Code permissions I ended on Anthropic's own framing: permissions are the lock on the door, the sandbox is the wall. This is the post about the wall. What actually holds on the day the agent, or whatever poisoned its context, decides your rules are an obstacle to route around.
Consent is not containment
A permission prompt is a question. You approve 93% of them, your finger learns the shortcut, and the approval becomes a reflex rather than a review. That's consent, and consent has a failure mode: you.
Rules and allowlists are better, but they share a weakness with the prompt: they live inside the process, and they negotiate with the model. A pattern can be side-stepped, as Ona's /proc/self/root trick showed. A config file can be edited by anything that can write files. A boundary the agent can reach is a boundary the agent can move.
So here is the test that sorts every control into one of two piles: does it still work when the model is actively working against it? If yes, it's containment. If no, it's consent. Prompts, permission rules and modes are consent. The wall has to be containment, which means it has to live outside the process, enforced by the operating system or the hypervisor, out of reach of the thing it contains.
What the built-in sandbox holds
The good news: Claude Code ships a real wall, and it's one command away. /sandbox turns on OS-level isolation for every Bash command: Seatbelt on macOS, bubblewrap on Linux and WSL2.
Writes are confined to the working directory. All network traffic is forced through a proxy that runs outside the sandbox and checks every domain against an allowlist. A command that tries to read ~/.ssh or call an unapproved host fails at the OS, whatever the model intended.
The trade is unusually good. Because sandboxed commands are contained, they run without prompting, and Anthropic measured an 84% drop in permission prompts. Fewer interruptions and a harder boundary at the same time. If you run Claude Code on your own machine and you haven't typed /sandbox yet, that's the single highest-value change you can make today.
Know what it covers, though. The sandboxed Bash tool wraps Bash commands and their children. Nothing else.
File tools like Read and Edit run inside the Claude Code process, gated by permission rules. MCP servers and hooks are separate processes that run unconstrained on your host. The MCP server holding your database credentials sits entirely outside the box, which is why locking down the agent's database access is its own job with its own post.
The escalation ladder
Everything past /sandbox is about widening the boundary until your whole session sits inside it. The options, in order of effort:
| Approach | What's inside the wall | Needs Docker |
|---|---|---|
Sandboxed Bash tool (/sandbox) | Bash commands and their children | No |
| Sandbox runtime | The whole process, including MCP and hooks | No |
| Dev container with a default-deny firewall | The full development environment | Yes |
| Dedicated VM or microVM | An entire operating system | No |
The sandbox runtime is the underrated middle step. It's the same Seatbelt/bubblewrap machinery, but wrapped around the entire Claude Code process instead of individual commands:
npx @anthropic-ai/sandbox-runtime claudeOne line, no Docker, and suddenly your MCP servers and hooks are inside the boundary too. You configure allowed write paths and network domains in ~/.srt-settings.json, default-deny on both.
The dev container route matters for a different reason: it's the setup Anthropic itself points at for unattended work. The example devcontainer ships a default-deny iptables firewall, and behind that firewall, --dangerously-skip-permissions changes category. Inside a container that can't reach arbitrary hosts and can't touch your home directory, skipping consent is a reasonable trade for autonomy. On your host, it's handing your shell to the worst sample in the distribution.
And for code you genuinely don't trust, a repo you're auditing, a dependency you suspect, nothing beats a throwaway VM with no credentials on it. Kernel-level separation, nothing shared, delete it afterwards.
The two leaks no sandbox fixes
Whichever box you pick, two channels stay open, and pretending otherwise is how sandboxes become false comfort.
Egress is exfiltration. Every domain on your allowlist is a channel out. If the agent can read a secret and reach one permissive host, the secret can leave. Keep the allowlist short and boring: your package registry, your API endpoint, and a hard stare at anything else.
And remember the channel you can't close: everything the agent reads is sent to the model API by design. The sandbox decides what the process can touch, never what leaves in the prompt.
The writable mount is the point, and the risk. The agent's job is to change your code, so the project directory is writable, so the sandbox will happily watch it write something you'll execute later, outside the wall, with your privileges. No isolation reviews a diff. The wall buys you the certainty that nothing else was touched. What was touched still crosses on your judgement.
One more lesson from the Ona story: after they moved enforcement into the kernel, the agent found a bypass through the ELF dynamic linker, loading binaries via a path no one had thought to guard. Walls have seams. That's an argument for stacking layers. The intrusion that ran without a human at the wheel got exactly as far as its boundaries allowed, and that's the property you're buying.
What I actually run
My setup, by trust level:
- My own repos, me watching:
/sandboxon, network allowlist, plus the permission rules and hooks from the permissions guide. Layers are cheap. - Unattended runs: a dev container with the default-deny firewall, the project mounted as the only writable path, and no secrets in the image at all. This is where
--dangerously-skip-permissionsis allowed to exist. - Untrusted code: a disposable VM with nothing on it worth taking.
The pattern behind all three: decide what the agent may reach before it runs, enforce that outside the process, and let the permission prompts go back to being what they should have been all along, a rare question that deserves an actual answer.
All it takes is an agent that is diligent, blocked, and creative, three things you selected it for. The wall is for the day those three point somewhere you didn't.