Context and controlGuide 6 of 12
Permissions in Claude Code
How to define which actions the agent can run, which ones need confirmation, and which ones must stay blocked.
Updated 6 min read
// on this page
The question that defines this article is simple: how much do you trust what Claude is about to do without asking? Claude Code splits that answer into two layers that are worth understanding separately: the modes (default behavior) and the rules (specific exceptions).
The 6 permission modes
| Mode | What it allows | When to use it |
|---|---|---|
| default, shown as “Manual” since v2.1.200 | Read-only without asking; any edit or command requires approval | Sensitive work, unfamiliar repos |
| acceptEdits | Reads and edits files without interruptions | You’re iterating on code you actively review yourself afterwards |
| plan | Read-only, neither edits nor runs anything | Exploring a new codebase or planning a refactor before touching anything |
| auto | Every action, with an automatic classifier checking in the background | Long tasks where you want to reduce the fatigue of approving everything |
| dontAsk | Only runs explicitly pre-approved tools | CI/CD pipelines and automated scripts |
| bypassPermissions | Everything, with no checks | Only in isolated containers or VMs — never on your main machine |
How to change it:
# During a session: Shift+Tab cycles through the available modes.
# bypassPermissions only enters the cycle if you started with
# --dangerously-skip-permissions.
claude --permission-mode plan
claude --permission-mode acceptEdits
claude --permission-mode auto
claude --permission-mode dontAsk # meant for CI
claude --permission-mode bypassPermissions # only in isolated environments
Two values aren’t accepted from .claude/settings.json or settings.local.json: auto is ignored, and bypassPermissions makes the session start in Manual. That’s deliberate: they’re decisions for whoever launches the session, not for a file committed to the repo.
Custom rules: deny → ask → allow
On top of the modes, there’s a system of granular per-tool rules. They’re evaluated in this order, and the first matching rule wins:
01. Does it match a deny rule?
02. Does it match an ask rule?
03. Does it match an allow rule?
deny always wins: bypassPermissions can’t override a deny from managed configuration, and acceptEdits can’t lift a denied Edit(...).
Syntax: Tool or Tool(specifier).
{
"permissions": {
"allow": [
"Bash(npm test*)", // any command starting with "npm test"
"Bash(docker compose *)",
"Bash(git log*)"
],
"deny": [
"Read(./.env)",
"Read(~/.ssh/*)",
"WebFetch(domain:vault.mycompany.internal)"
]
}
}
A few details worth keeping in mind:
- The space before
*matters: a ruleBash(ls *)requires a space or end of string afterls(it doesn’t matchlsof);Bash(ls*)matches both.Bash(ls:*)is the modern syntax equivalent toBash(ls *). - Shell operators are evaluated separately: a
Bash(safe-command *)rule doesn’t enablesafe-command && dangerous-command, because Claude Code understands&&,||, and;and evaluates each subcommand. Read/Edituse.gitignore-style syntax://pathis absolute from the filesystem root,~/pathfrom your home,/pathis relative to the project root (careful, not the filesystem root —/Users/alice/fileis relative to the project, not a real absolute path), andpathor./pathis relative to the current directory.- An important limit:
denyrules onRead/Editblock Claude’s internal tools, but they don’t block what you do throughBash(acat .envin Bash isn’t stopped by aRead(./.env)rule). For real OS-level blocking you need a sandbox, which combines thedenyrules with its own allowlist of network domains.
What each mode adds on top of the rules
The modes are the master switch; the rules are the fine filter. An example of how they interact:
- In plan, even with
Edit(/src/**)inallow, the mode doesn’t let it edit anything — it’s more restrictive than permissive rules. - In auto, entering the mode automatically discards dangerous
allowrules likeBash(*)orAgent(*), because they’d give arbitrary execution before the classifier had evaluated them. Specific rules (Bash(npm test)) are kept. On exit, they’re restored. - In dontAsk, even
askrules turn into denial — anything not explicitly inallowis rejected. Ideal for CI.
This is one of the corners of Claude Code that evolves fastest. Before setting a permission policy for a whole team, it’s worth confirming the current behavior in the official documentation.
Related documentation: complete rule syntax and settings.json reference, where all these rules live on disk.