Sandboxing Bash in Claude Code: OS-level limits on files and network
TL;DR
The sandbox limits where shell commands can write and connect, enforced by the OS, so it protects you after you click Allow. By default it still reads most of the machine: block key folders and keep excludedCommands narrow.
Permission prompts only protect you at the moment of approval. Once you allow npm install, an unknown package's postinstall script can read ~/.ssh or send data out, and nobody asks again. The sandboxed Bash tool adds a boundary enforced by the operating system around every shell command Claude runs, limiting where files can be written and where network connections can go. It's built on the open-source @anthropic-ai/sandbox-runtime package.
Mechanism by OS
| OS | Mechanism |
|---|---|
| macOS | Seatbelt, built in |
| Linux, WSL2 | bubblewrap for the filesystem + a network namespace, socat relaying traffic through a proxy |
| Native Windows | No sandbox; use WSL2 |
The sandbox covers Bash, PowerShell and Monitor commands and their child processes. Built-in tools (Read, Edit, WebFetch…), hooks and MCP servers run outside the sandbox.
Turning it on
/sandbox # opens the Mode, Overrides, Config panel
// settings.json
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"network": { "allowedDomains": ["github.com", "*.npmjs.org"] }
}
}
failIfUnavailable makes Claude Code exit instead of silently running unsandboxed when a dependency is missing. The sandbox is off by default.
What's allowed by default?
- Writes: the working directory, a per-user temp directory, folders added with
--add-dir. Protected paths such as.claudesettings,.bashrc,.gitconfigand.git/hooksstay write-denied. - Reads: nearly the whole machine, including
~/.sshand~/.aws/credentials. This is the most commonly misunderstood point: you must block them yourself withsandbox.filesystem.denyReadorsandbox.credentials. - Network: no direct route out. All connections go through a local proxy that only allows domains in
allowedDomains(empty by default).
The sandbox reduces prompts
In auto-allow mode, sandboxed commands run without prompting because the boundary contains the damage. Ask rules (e.g. Bash(git push *)) still prompt and deny rules still block. For domains outside the list, your permission mode decides: Manual prompts you, auto mode only allows it if the classifier approves.
Escape hatches and pitfalls
- Unsandboxed retry: when a command fails in the sandbox, Claude may retry with
dangerouslyDisableSandbox. Turn this off withallowUnsandboxedCommands: false. excludedCommands(e.g."docker compose *") run with full access. A broad pattern for an interpreter (python *) is a hole: Claude could write a script and run it outside the sandbox.- Incompatibilities: Docker;
jestneeds--no-watchman; on macOSgitover SSH fails; on Linux Chromium and rootless Podman often fail. - Commands you type at the
!prompt usually run unsandboxed. UDP, QUIC and ICMP can't get out.
A suggested config for a web project
{
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": false,
"network": { "allowedDomains": ["registry.npmjs.org", "github.com"] },
"filesystem": { "denyRead": ["~/.ssh", "~/.aws"] }
}
}
How to apply it
- Dev: Turn on the sandbox with a minimal domain list; remember to block reads of key folders.
- Tech Lead: Put the sandbox config in managed settings for the whole team, with
failIfUnavailableso nobody accidentally runs unprotected. AuditexcludedCommandsas you would admin rights. - PM/Security: Combine the sandbox with auto mode: the classifier decides "should this happen", the sandbox limits "how bad can it get if it does".
Sources & references
- Configure the sandboxed Bash tool — Claude Code Docs