The more permission dialogs you click through, the less you read them. Claude Code’s OS-level Bash sandbox (filesystem isolation plus network isolation) cut those prompts by about 84% in Anthropic’s internal usage, and it keeps a prompt-injected agent inside boundaries the kernel enforces. Mid-2026 added sandbox.credentials, managed lockdown keys, and a documented isolation ladder. One catch still trips people: /sandbox wraps Bash only. Read, Edit, MCP, and hooks stay on the host until you climb that ladder.
Definitions
Permission rules - allow / ask / deny checks that decide whether a tool may run at all (Bash, Read, Edit, WebFetch, MCP, and others). Evaluated before the tool runs. See permission rules block and the tools inventory and ToolName(specifier) rules.
Bash sandbox (/sandbox) - OS-level filesystem and network restrictions on Bash commands and every child process they spawn. Seatbelt on macOS; bubblewrap on Linux and WSL2. Native Windows is unsupported; use WSL2 or a container.
Sandbox runtime - @anthropic-ai/sandbox-runtime, which wraps the entire Claude Code process (file tools, MCP, hooks) in the same Seatbelt/bubblewrap primitives.
MCP - Model Context Protocol; the standard for third-party tool servers the agent can call. Each server is code you run alongside Claude Code.
Approval fatigue - rubber-stamping dialogs until they stop being a control and become noise. Sandboxing replaces per-command “may I?” with boundaries you declare once.
Why prompts are not enough
Claude Code starts read-only. Safe commands such as ls, cat, and git status run without a prompt; most mutating Bash still needs approval. Fine as a default. Broken as your only control after fifty approvals in an hour.
Prompt injection makes the gap concrete. Malicious text in a README, a fetched page, or a dependency can steer the agent toward curl, credential reads, or shell-config writes. Claude Code mitigates some of that: it sanitizes input, never auto-approves network commands, keeps WebFetch results in an isolated context, and asks about (rather than runs) any command its rules do not match. No model is injection-proof. The useful question is: if the agent is compromised, what can it still touch?
Anthropic’s answer is two boundaries that must both hold:
| Boundary | What it stops |
|---|---|
| Filesystem isolation | Writing outside the allowed paths (e.g. ~/.bashrc, system binaries); with credentials / denyRead, also reading secrets |
| Network isolation | Connecting to hosts outside the domain allowlist (exfiltration, malware download) |
Skip network isolation and an agent that can still read ~/.ssh can ship the keys out. Skip filesystem isolation and it can backdoor a tool that later gets network access. You need both.
The isolation ladder (2026)
Match the threat to the boundary. Climb when you skip prompts or touch untrusted code. The garden note Sandbox ladder is the one-liner version of this section.
| Approach | What is isolated | When to use it |
|---|---|---|
Sandboxed Bash (/sandbox) | Bash + children only | Everyday local work; fewer prompts inside a known repo |
@anthropic-ai/sandbox-runtime | Whole Claude process (tools, MCP, hooks) | Unattended runs without Docker; MCP you do not fully trust |
| Dev container / custom container | Full development environment | Team standard; --dangerously-skip-permissions with firewall allowlist |
| Virtual machine | Full OS / kernel separation | Untrusted repos; compliance needs strong separation |
| Claude Code on the web | Anthropic-managed VM; Git via scoped proxy | No local setup; clone from GitHub in an isolated cloud VM |
--dangerously-skip-permissions and long auto-mode sessions remove the human gate. Anthropic’s guidance: put those sessions in a container, a VM, or the sandbox runtime. Bash sandbox alone is not that boundary. Plan mode’s “read-only” promise is behavioral, not sandboxed. OS isolation is the hard layer.
Scope gap that bites
| Mechanism | Covers Bash children | Covers Read / Edit | Covers MCP / hooks |
|---|---|---|---|
| Permission rules | Pattern-matched only | Yes | Yes (per tool) |
/sandbox | Yes (OS-enforced) | No | No |
| Sandbox runtime / container / VM | Yes | Yes (inside boundary) | Yes |
Claude Code recognizes common file commands inside Bash (cat, sed, grep) and applies your Read/Edit deny rules to them too. A Python one-liner that opens ~/.ssh/id_rsa is not recognized, so it walks past those rules. OS sandbox plus sandbox.credentials closes that hole for Bash; permission denies close it for the Read tool. Set both.
Enable and configure /sandbox
/sandbox
Pick auto-allow (sandboxed Bash runs without prompts; out-of-bound access falls back to the normal permission flow) or regular permissions (isolation stays, every command still asks). On Linux/WSL2 install bubblewrap and socat first. macOS uses Seatbelt with nothing extra. WSL1 and native Windows are unsupported.
Defaults that matter:
- Write: working directory + session
$TMPDIRonly - Read: nearly the whole machine, including credential files, unless you deny them
- Network: no domains pre-allowed; the first time a command needs a new host, Claude Code prompts (from v2.1.191, approving keeps that host for the rest of the session)
- Escape hatch: on sandbox failure Claude may retry with
dangerouslyDisableSandbox(still goes through permissions). SetallowUnsandboxedCommands: falsefor strict mode
Project-local mode lands in .claude/settings.local.json (not committed). User-wide: sandbox.enabled: true in ~/.claude/settings.json. Orgs use managed settings.
Close the credential hole (v2.1.187+)
Default read policy was the quiet failure mode through early 2026: writes locked, reads wide open. sandbox.credentials groups file denies and env scrubbing. Deny entries merge across scopes - project, user, and managed; a project or user scope cannot loosen a deny that managed settings set.
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"filesystem": {
"denyRead": ["~/"],
"allowRead": ["."]
},
"credentials": {
"files": [
{ "path": "~/.aws/credentials", "mode": "deny" },
{ "path": "~/.ssh", "mode": "deny" }
],
"envVars": [
{ "name": "GITHUB_TOKEN", "mode": "deny" },
{ "name": "NPM_TOKEN", "mode": "deny" }
]
},
"network": {
"allowedDomains": ["registry.npmjs.org", "api.github.com"]
}
},
"permissions": {
"deny": ["Read(~/.ssh/**)", "Read(~/.aws/**)", "Read(./.env*)"]
}
}
Notes:
- There is no built-in credential deny list. Empty
credentialsprotects nothing. mask(v2.1.199+) substitutes a sentinel inside the sandbox and injects the real secret only on listedinjectHosts. It requiresnetwork.tlsTerminate. Mask entries in project settings are ignored; use user, managed, or CLI settings.- Broad allowlists like
github.comstill leave room for domain fronting - smuggling traffic to a blocked host behind an allowed hostname. The default proxy only checks the hostname the client claims; it does not look inside the encrypted traffic. If you need content inspection, terminate TLS at a custom proxy. - Never allow
/var/run/docker.sockthroughallowUnixSockets. That is host access via Docker.
To scrub Anthropic and cloud-provider credentials from the environment of every subprocess (sandboxed or not), set CLAUDE_CODE_SUBPROCESS_ENV_SCRUB.
Org lockdown
Managed settings can require the sandbox and refuse soft fallback:
| Key | Effect |
|---|---|
sandbox.enabled: true | Sandbox on for every developer |
failIfUnavailable: true | Missing bubblewrap / unsupported platform → hard fail, not warn-and-continue |
allowUnsandboxedCommands: false | Ignores dangerouslyDisableSandbox retries |
allowManagedDomainsOnly | Only managed domain allowlists apply |
allowManagedReadPathsOnly | Developers cannot widen allowRead locally |
excludedCommands still merges from every scope, so a developer can always append tools that run outside the sandbox. Keep the managed list narrow (docker * is a common necessary exception).
Cloud sessions add another tier. Each Claude Code on the web run is an Anthropic-managed VM with network controls, a Git proxy that keeps real tokens outside the sandbox, branch-restricted push, audit logs, and automatic cleanup. Remote Control - the phone/web UI that drives Claude Code on your own machine - is different: it talks to a local process, with no cloud VM and no Anthropic-managed sandbox.
What still sits outside the sandbox
Treat these as product limits, not bugs:
- MCP servers. Put them on allowlists. Anthropic does not security-audit third-party servers. Prefer servers you wrote or trust; gate them with permission rules.
/security-reviewand PR review actions. Useful first pass. Outside evaluations (including Semgrep’s) find inconsistency and blind spots on multi-file and business-logic issues. Pair with deterministic static and dynamic analysis (SAST/DAST) tools.- Windows WebDAV. Avoid enabling WebDAV or
\\*paths; they can trigger remote requests that bypass the permission system. - Home-directory trust. Claude Code asks you to trust a folder before working in it. Starting in
~extends that trust to everything under your home directory for the session - that trust resets the next time you launch Claude Code. Start from a project subdirectory instead. - Data plane. Isolation does not change what is sent to the model API. Sandbox controls local blast radius, not training or privacy policy.
Practical playbook
- Trusted personal repo, interactive:
/sandboxauto-allow + credential denies + narrowallowedDomains - Team laptop fleet: managed
enabled+failIfUnavailable+allowUnsandboxedCommands: false+sandbox.credentialsfor AWS/SSH/tokens - Unattended / skip-permissions: container, VM, or
npx @anthropic-ai/sandbox-runtime claudewith write/network allowlists that include the project,~/.claude, andapi.anthropic.com - Untrusted repository: dedicated VM or Claude Code on the web; do not rely on Bash sandbox alone
- Always: review suggested commands; deny
curl/wgetif your threat model requires it; audit with/permissions; keep SAST in CI
Slash commands for the control surface: /sandbox and /permissions. Details in the commands cheatsheet.
Bottom line
Permission rules decide whether a tool runs. The Bash sandbox decides what a running shell can touch. Containers and VMs decide whether the whole agent (including Read and MCP) shares your host. Turn on /sandbox, deny credential reads, and climb the ladder the moment you remove the human from the approval loop.