The sandbox runs shell commands with operating-system restrictions and answers a question fundamentally different from permissions. The permission flow decides whether to ask the user before a run; the sandbox decides what a process can physically read, write and connect to. These are two independent layers, and they must not be confused: you can allow a command without asking and at the same time hard-limit what it has access to. The sandbox is supported on macOS, Linux and WSL2.
The sandbox configuration describes three boundaries: the file system, the network and credentials. For the file system you set where writing is allowed (allowWrite), what cannot be read (denyRead) and what may be read (allowRead); with isolation on, the current working directory and the session temp directory are writable by default, the rest is granted explicitly. For the network you set an allowlist of domains and unix sockets. For credentials you list files and environment variables with a deny or mask mode.
The crucial subtlety - the sandbox has its own path syntax, not matching permission rules. Here /tmp/build is an ordinary absolute path, a tilde is home, a dot-slash or a path with no prefix is relative to the project root for project settings and relative to ~/.claude for user settings. For overlapping read rules the more specific path wins: you can deny all of home and open only one subdirectory. Path arrays from different scopes are merged in the process.
Auto-allow ties the sandbox to permissions. With autoAllowBashIfSandboxed set to true a command that was successfully run in the sandbox can execute without the ordinary prompt - but the sandbox restrictions are not weakened, only the need to ask changes. With false the sandbox is the same, but shell commands go through the ordinary permission flow. Plan mode meanwhile has its own, stricter logic and must not unexpectedly expand via auto-allow.
The sandbox has an escape hatch, and it must be controlled consciously. If a command is incompatible with the sandbox, Claude may retry it with the input dangerouslyDisableSandbox:true - then it executes outside and returns to the ordinary permission flow. Such a retry can be fully forbidden with the key allowUnsandboxedCommands: false - in /sandbox this is strict sandbox mode. If the escape hatch is left, the practical control is an explicit ask rule for Bash(dangerouslyDisableSandbox:true), so that leaving the sandbox is always confirmed.
It helps to see a whole sandbox configuration once, to understand how the three boundaries add up. Below is an example with writing to build, denying the reading of keys, a domain allowlist and a deny on tokens. You return to this form when configuring isolation: the network is opened only to the hosts needed by the package manager, VCS and tests, and sockets need special care, because the Docker daemon socket effectively grants power over host containers.
Credentials are protected by two modes. deny forbids reading a file or removes a variable from the sandboxed command's environment; there is no built-in universal denylist - secrets are listed yourself. mask shows the process a sentinel instead of the secret, and a proxy substitutes the real value only on a request to an allowed host; this needs a correct TLS-termination configuration, and mask authority is accepted only from user, managed or CLI, but not from a checkout. This is an enterprise scheme - you start with deny until the proxy and logs are verified.
Disabling file isolation sharply changes the threat model, and this must be remembered. The sandbox then keeps network isolation but gives commands access to the host file system - a process can change the shell startup, executables in PATH or user settings; project settings cannot disable it themselves. And most importantly: the sandbox is not proof of safety - it limits one class of consequences, but Claude can still read hostile instructions in an allowed file or propose a harmful change. Minimal permissions, review and credential protection are needed.
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"allowUnsandboxedCommands": false, // strict: no escape hatch
"filesystem": {
"allowWrite": ["./build", "/tmp/project-build"],
"denyRead": ["~/.ssh", "~/.aws"],
"allowRead": ["."]
},
"network": { "allowedDomains": ["registry.npmjs.org", "github.com"] },
"credentials": {
"files": [{ "path": "~/.aws/credentials", "mode": "deny" }],
"envVars": [{ "name": "GITHUB_TOKEN", "mode": "deny" }]
}
}
}