Short answer: set "sandbox": { "enabled": true } in settings.json and list the hosts a command may reach under sandbox.network.allowedDomains. Commands then run confined to your project folder and those hosts, which makes it safe to stop approving each one.
Permission rules decide whether a command may run. The sandbox decides what a command can reach once it runs: which folders it can read and write, and which hosts it can talk to. Together they let you say "run whatever you need, but only in here".
Why turn the sandbox on?
Two reasons, and most people come for the second:
- Containment. A shell command that goes wrong cannot touch your SSH keys or post your code somewhere, because it cannot reach them.
- Fewer prompts. A command that cannot reach anything you did not allow does not need your approval. With
autoAllowBashIfSandboxed, sandboxed commands stop asking.
What is the minimal setup?
In ~/.claude/settings.json or the project's .claude/settings.json:
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"failIfUnavailable": true,
"network": {
"allowedDomains": ["registry.npmjs.org", "github.com"]
},
"filesystem": {
"denyRead": ["~/.ssh", "~/.aws"]
}
}
}The project directory is readable and writable already. Everything else is what you add.
Which keys matter?
| Key | What it does |
|---|---|
sandbox.enabled | Runs shell commands inside the sandbox |
sandbox.autoAllowBashIfSandboxed | Stops asking for commands that run sandboxed |
sandbox.failIfUnavailable | On a machine that cannot sandbox, stop instead of quietly running unconfined |
sandbox.allowUnsandboxedCommands | Off refuses a command's attempt to run outside. Leave it off if the sandbox is meant to be a boundary |
sandbox.excludedCommands | Commands that genuinely cannot work inside. Each one is a hole |
sandbox.network.allowedDomains | Hosts it may reach. Your WebFetch(domain:...) allow rules are merged in |
sandbox.network.deniedDomains | Hosts it may never reach. Denied wins over allowed |
sandbox.filesystem.allowRead / allowWrite | Extra paths beyond the project |
sandbox.filesystem.denyRead / denyWrite | Paths it may never touch. Deny is checked first |
How do I allow only certain websites?
List them in sandbox.network.allowedDomains. Anything not on the list is blocked for sandboxed commands. Add the package registries your build needs, or installs will fail in ways that look like network trouble.
Note that in-process tools such as WebFetch are governed by your permission rules, not by the sandbox's network list. Keep both in step.
What about running inside Docker?
A container often cannot give the sandbox everything it needs. enableWeakerNestedSandbox exists for that case, and the word "weaker" is meant seriously: treat the container itself as the main boundary.
Which setting makes the sandbox pointless?
A sandbox that cannot start on your machine protects nothing. Without failIfUnavailable, Claude Code carries on unconfined and you may never notice. Turn it on, and check once that the sandbox actually starts where you run it.
How do the sandbox and bypass mode go together?
bypassPermissions (nothing asks, nothing is checked) belongs only inside a sandbox with no network. That is the one combination where skipping prompts is reasonably safe. See Run Claude Code headless in CI for the unattended case.
Where can you check this?
How do you set it up without mistakes?
Config Studio for Claude Code shows the sandbox keys together, because the filesystem and network halves affect each other, and writes only what you set. Run This machine first and it tells you whether the sandbox will work where you plan to use it. For the rules side, see Claude Code permissions: allow, ask and deny.

