5. Permissions and isolation
"Claude Code decides what Claude may do in layers. A permission mode sets the baseline for the session: ask before almost everything (Manual), accept file edits, plan without editing, let a classifier review risky actions (auto), deny anything not pre-approved (dontAsk), or skip checks entirely (bypass, for containers only). Permission rules in settings then allow, ask about, or deny specific calls. They're checked deny first, and a deny in any settings file beats an allow in any other. /permissions is where you see and edit them. The catch: a Bash rule matches the text of the command, so a determined /bin/rm or bash -c gets past it. The hard boundary is the sandbox, which the operating system enforces around every shell command. /sandbox turns it on."
The sentence that marks you as an architect: rules decide what Claude may try, the sandbox decides what a command can actually touch.
7 pictures, in order.
Use the arrow keys or the dots. Tap a picture to see it full size.
The facts someone can check.
The six permission modes
| Mode (config value) | Runs without asking | Use it for |
|---|---|---|
default, labeled Manual |
Reads only | Sensitive work, unfamiliar code |
acceptEdits |
Reads, file edits, and mkdir, touch, rm, rmdir, mv, cp, sed inside the working directories |
Iterating, then reviewing in git diff |
plan |
Reads and exploring. No source edits until you approve the plan | Exploring before changing anything |
auto |
Everything, with a classifier reviewing risky actions | Long tasks, cutting prompt fatigue |
dontAsk |
Reads and pre-approved tools. Anything that would prompt is denied | CI and scripts |
bypassPermissions |
Everything, including protected paths | Containers and VMs only |
- Switching. Shift+Tab cycles modes in the CLI. From auto, the cycle runs Manual, acceptEdits, plan, and back.
bypassPermissionsjoins only if the session started with it enabled.dontAskis never in the cycle: start with--permission-mode dontAsk. - Starting mode. From v2.1.283, auto is the built-in starting mode for interactive terminal and VS Code sessions.
claude -pand Agent SDK runs start in Manual, and from v2.1.302 a call that needs approval is simply denied unless a rule, hook, or callback approves it. - A repo can't put you in auto or bypass.
defaultMode: "auto"or"bypassPermissions"in.claude/settings.jsonor.claude/settings.local.jsonis ignored. Put it in~/.claude/settings.jsonor managed settings. - Plan mode blocks source edits until you approve the plan. The approval offers "Yes, and use auto mode", "Yes, manually approve edits", or "No, keep planning". Ctrl+G opens the plan in your editor first. Typing
/planbefore a prompt also enters it. - Protected paths such as
.git,.claude,.vscode,.husky, shell startup files, and.mcp.jsonare never auto-approved for writes, except in bypassPermissions. An allow rule doesn't change that. - Deny rules block in every mode, bypassPermissions included. Allow rules have no effect in bypassPermissions.
Permission rules: the syntax
Every rule is Tool or Tool(specifier), and the specifier means something different per tool.
| Rule | What it matches |
|---|---|
Bash |
Every Bash command. As a deny, it removes the tool from Claude's context entirely |
Bash(npm run *) |
npm run build, npm run test --watch, and plain npm run |
Bash(ls *) |
ls -la and ls, but not lsof: the space before * is part of the rule |
Read(./.env), Edit(/src/**) |
gitignore patterns. // is absolute, ~/ is home, / is relative to the settings file, ./ to the current folder |
WebFetch(domain:example.com) |
That host only. domain:*.example.com covers subdomains but not the bare domain |
mcp__github__* |
Every tool on the github MCP server |
Agent(Explore) |
The Explore subagent |
- Put the
*after the subcommand.Bash(git log *)allows onlygit log.Bash(git *)allows all of git, andBash(git * main)letsgit push origin mainthrough. - Compound commands are split.
Bash(safe *)doesn't approvesafe && other. Every part has to match on its own. Approving a compound command saves at most 5 rules, one per part. - A Bash rule matches the text Claude writes, after stripping a fixed set of wrappers like
timeoutandnohup.Bash(rm *)in deny stopsrm -rf build/, not/bin/rm -rf build/orbash -c 'rm -rf build/'. The docs say it plainly: a deny rule covers the usual invocation and isn't a security boundary around the program. - A Read deny also blocks Edit and Write on the same path, but only for Claude's own tools and file commands Claude Code recognizes. A Python script that opens the file itself isn't covered. That's the sandbox's job.
How rules decide
- Evaluation order is deny, then ask, then allow. The first match decides. Specificity changes nothing: a broad
Bash(aws *)deny beats a narrowBash(aws s3 ls)allow. - No match means the permission mode decides.
- An ask rule prompts even in auto mode and bypassPermissions.
Settings precedence, highest first
| Level | File | Notes |
|---|---|---|
| Managed | managed-settings.json, MDM, or server-managed settings |
Nothing overrides it, not even --allowedTools |
| Command line | --settings for one session |
--disallowedTools can add limits beyond managed |
| Project local | .claude/settings.local.json |
Where "Yes, don't ask again" saves, at the repo root |
| Shared project | .claude/settings.json, committed |
Its allow rules wait until you accept workspace trust |
| User | ~/.claude/settings.json |
Your rules for every project |
- Rule lists merge.
permissions.allowfrom every file is combined, and a deny at any level blocks an allow at any other level, in both directions. - Locks for an organization:
allowManagedPermissionRulesOnlymakes managed settings the only source of rules.permissions.disableBypassPermissionsMode: "disable"andpermissions.disableAutoMode: "disable"remove those modes. - Workspace trust. A cloned repo's allow rules and extra directories don't apply until you accept the trust dialog. Its deny and ask rules apply at once, since they only restrict.
Auto mode
A second model, the classifier, reviews actions instead of you. The order for every action:
- Your allow, ask, and deny rules resolve first.
- Read-only actions and file edits in the working directory are approved outright.
- Everything else, mostly shell commands and network calls, goes to the classifier.
- A block sends Claude the reason, and the action shows under Recently denied in
/permissions, whererretries it.
- Blocked by default: download-and-run like
curl | bash, sending sensitive data out, production deploys, force push,git reset --hard, granting IAM or repo permissions, printing a live credential. Allowed by default: local file work, installing what your lock files declare, read-only HTTP, pushing to a branch of the repo you're in. - 3 blocks in a row, or 20 in a session, pause auto mode and Claude Code goes back to prompting. Neither number is configurable.
- The classifier never sees tool results. It reads your messages, the tool calls, and CLAUDE.md, so hostile text in a file or web page can't argue with it.
- Broad allow rules are dropped on entry:
Bash(*), wildcarded interpreters likeBash(python*), package-manager run rules, and Agent and Monitor allow rules. They come back when you leave auto mode. - "Don't push" in chat counts, but only until compaction drops that message. A deny rule is the durable version.
- Trusted infrastructure goes in
autoMode.environmentin user or managed settings, never project settings.claude auto-mode defaultsprints the built-in rules;claude auto-mode configprints what the classifier actually uses.
The sandbox
| Access | Default inside the sandbox | Change it with |
|---|---|---|
| Writes | Working directory, a per-user temp directory, added directories. Protected config files stay blocked | filesystem.allowWrite, denyWrite |
| Reads | Most of the machine, including ~/.ssh and ~/.aws/credentials |
filesystem.denyRead, credentials |
| Network | No direct route. A local proxy checks each host against allowed domains, which start empty | network.allowedDomains, deniedDomains |
| Environment | Inherited from Claude Code, secrets included | credentials |
- Off by default. Turn it on with
/sandbox(saved to.claude/settings.local.json) orsandbox.enabled: truein settings. - macOS uses Seatbelt, Linux and WSL2 use bubblewrap and socat. Native Windows runs commands unsandboxed.
- What it covers: Bash, PowerShell, and Monitor commands and every process they start. What it doesn't: Read, Edit, and WebFetch (rules govern those), hooks, local MCP servers, the status line, commands you type after
!, and anything inexcludedCommands. - Two sandbox modes. Auto-allow runs sandboxed commands without a prompt, even in Manual mode. Regular permissions keeps every prompt. The boundary is the same in both.
- The escape hatch. When a command fails inside, Claude can ask to retry it with
dangerouslyDisableSandbox.allowUnsandboxedCommands: falseturns that off. - If the sandbox can't start, commands run unsandboxed unless
failIfUnavailable: true. - For an organization, deliver three keys in managed settings:
enabled: true,failIfUnavailable: true,allowUnsandboxedCommands: false. That last one also makes the sandbox admin-required, so a repo's settings can't loosen it.
/fewer-permission-prompts
A bundled skill. It scans your transcripts for common read-only Bash and MCP calls and adds a prioritized allowlist to the project's .claude/settings.json. It's the deliberate, shareable version of clicking "don't ask again" a few hundred times.
What /permissions actually does
Opens a dialog listing every rule with the settings file it came from. You can add or remove rules, manage working directories, review recent auto mode denials, and view the auto mode classifier rules on the Auto mode tab. Since v2.1.234 you can open it while Claude is working, and changes apply from Claude's next tool call. Its alias is /allowed-tools.
Rules vs the sandbox
Permission rules are judged before anything runs, from the command string. The sandbox is enforced by the operating system on the running process, so it holds even when an allowed command does more than its name suggests, or when a prompt injection gets past the model. Use both: rules to shape what Claude attempts and keep prompts sensible, the sandbox for what must never be reachable.
Answer out loud, then flip the card.
Mark each one honestly. Cards you miss stay flagged for review, in this browser.
A customer says "we added Bash(curl *) to deny, so Claude can't reach the internet." Is that enough?
No. A Bash rule matches the command text Claude writes. /usr/bin/curl, sh -c 'curl ...', or a Python script that opens a socket all get past it, and WebFetch is a separate tool with its own rules. For a real network boundary, turn on the sandbox: allowed domains start empty, and every connection goes through a proxy that checks the host. At org scale, set allowManagedDomainsOnly in managed settings so only the organization's domain list counts.
Developers say they're drowning in permission prompts. What do you recommend, in order?
First, run /fewer-permission-prompts to turn the read-only calls they keep approving into a shared allowlist in project settings. Second, turn on the sandbox in auto-allow mode, so sandboxed commands run without prompts inside an OS boundary. Third, auto mode, which is already the default in current versions, with autoMode.environment listing the repos, buckets, and domains the company trusts so routine work stops getting blocked. Never bypassPermissions on a real machine.
Security wants a guarantee that nobody uses bypassPermissions, and that one team can't use auto mode. How?
Managed settings: permissions.disableBypassPermissionsMode: "disable" and permissions.disableAutoMode: "disable". Managed settings sit above every other level, including command-line flags, so a developer can't undo them. Deliver them by MDM as managed-settings.json or through server-managed settings.
Project settings allow Bash(npm *). A developer's user settings deny Bash(npm publish *). Can Claude run npm publish? npm test?
npm publish is denied: rules from every file are pooled and deny is checked first, so a user-level deny blocks a project-level allow. npm test runs, because only the allow matches it.
What's the difference between auto mode and the sandbox's auto-allow mode?
Auto mode is a permission mode: a classifier model reviews actions across all tools before they run. Auto-allow is a sandbox mode: Bash commands run without a prompt because the OS boundary already contains them. They're independent and combine well. This session runs auto mode with the sandbox on: when a sandboxed command needs a new host, Claude names the host on the command and the classifier reviews the two together.
A CI job should let Claude run the test suite and nothing else, unattended. What's the setup?
claude -p "run the test suite" --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read", inside the CI runner's container. dontAsk denies anything that would prompt, so the run never hangs on a question. Don't reach for bypassPermissions: it skips checks rather than limiting them.
Someone runs claude -p over a repo they just cloned. What does the repo get to do?
-p never shows the workspace trust dialog, and it treats the folder as trusted for hooks, the env block, and .mcp.json servers, which all run. The repo's permissions.allow rules don't apply; Claude Code prints a "not trusted" warning instead. To be safe, pass --setting-sources user or --bare, or --settings '{"disableAllHooks": true}', and reject unknown servers with disabledMcpjsonServers.
Why use plan mode instead of just telling Claude "don't edit anything yet"?
Plan mode is enforced by Claude Code: edits stay blocked until you approve the plan, whatever Claude decides. "Don't edit yet" is an instruction Claude tries to follow. Same lesson as Module 1: guidance versus enforcement. The approval step also gives you a clean handoff into auto mode or manual edit review.
Wrong answers that sound right.
Before you tap, say why each one is wrong.
Three moves in a fresh project.
Facts checked against code.claude.com/docs/en/permissions, permission-modes, sandboxing, settings, auto-mode-config, security, and commands on 2026-10-25. Claude Code ships fast: confirm anything that matters against the official docs.