Claude Code permissions: allow, ask and deny rules, permission modes, and what wins
Claude Code permissions decide which tool calls run without asking. You write allow, ask and deny rules (for example Bash(npm run *) or Read(./.env)) in a settings file or with /permissions, and pick a permission mode as the baseline. Rules are checked deny first, then ask, then allow, and Claude Code enforces them itself: what you write in CLAUDE.md or a prompt never changes what is allowed.
How Claude Code permissions work
In Manual mode (the default permission mode), Claude Code asks before actions that can change your system:
| Tool type | Asks first? |
|---|---|
| Reads and searches | No, inside the working directory and added directories |
| Bash commands | Yes, except a built-in set of read-only commands |
| File edits and writes | Yes |
| Web fetch | Yes, except preapproved documentation domains |
| Web search | Yes |
Answering "Yes, and don't ask again" saves a rule. To manage rules directly, run /permissions (it shows each rule and the settings file it comes from) or edit a settings file. The docs are explicit that "permission rules are enforced by Claude Code, not by the model": CLAUDE.md shapes what Claude tries, it does not grant or revoke access.
Allow, ask and deny rules
json
{
"permissions": {
"allow": ["Bash(npm run *)", "Bash(git commit *)", "Edit(src/**)"],
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)", "WebFetch"]
}
}
- Allow runs the call without asking.
- Ask always prompts, even if an allow rule also matches.
- Deny blocks it. A bare tool name in deny, such as
WebFetch, removes the tool from Claude's context entirely.
Rule syntax
| Rule | Matches |
|---|---|
Bash |
Every shell command |
Bash(npm run build) |
Exactly that command |
Bash(npm run *) |
Every npm script: put the * after the subcommand |
Read(./.env) |
Reading .env in the current directory |
Edit(src/**) |
Edits anywhere under src/ |
WebFetch(domain:example.com) |
Fetches to that domain |
mcp__github__* |
Every tool of the github MCP server |
mcp__github__create_issue |
One MCP tool |
Write the command you want to allow and replace the parts that vary with *. Matching is by the text before the first *, so Bash(git log*) and Bash(git log *) differ in what they accept.
What wins: evaluation order and settings levels
"Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome." Specificity doesn't matter: a broad deny like Bash(aws *) blocks aws s3 ls even if an allow rule names it exactly.
Across settings files the same holds: if any level denies a tool, no other level can allow it. A user deny blocks a project allow, a project deny blocks a user allow, and managed settings sit above everything, including command line flags.
Hooks don't bypass rules either: a deny or ask rule still applies even when a PreToolUse hook returns allow, while a hook that exits with code 2 blocks the call before the rules are checked.
Permission modes: the baseline
| Mode | Runs without asking |
|---|---|
default (Manual) |
Reads only |
acceptEdits |
Reads, file edits, common file commands |
plan |
Reads; Claude plans without editing (plan mode) |
auto |
Everything, reviewed by a background classifier |
dontAsk |
Pre-approved tools and reads; everything else denied |
bypassPermissions |
Everything; for isolated containers and VMs only |
Cycle modes with Shift+Tab, or start with --permission-mode. Rules sit on top of the mode, and deny rules hold in every mode.
Permissions vs sandboxing
Permissions decide which tools, files and domains Claude Code may use. Sandboxing adds OS-level limits on what shell commands can reach on disk and on the network. Use both: the sandbox still holds if a prompt injection steers Claude toward something your rules didn't anticipate.
Permissions in CI and the Agent SDK
Unattended runs can't answer prompts. In CI, give claude -p an exact --allowedTools list and --permission-mode dontAsk, so anything else is denied instead of hanging or silently widening; see Claude Code GitHub Actions and CI. In the Agent SDK, allowed_tools pre-approves tools (it does not restrict them) and disallowed_tools blocks them.
Permissions on the Claude Certified Architect exam
Expect permissions inside Domain 3 (Claude Code configuration) and Domain 1 (Agent SDK tool access) of the CCAR-F exam. The recurring trap: trying to enforce a hard limit through CLAUDE.md or a prompt, when only rules, hooks and modes are enforced by the tool.
More in all articles.
Sources: Configure permissions; Choose a permission mode; Run Claude Code programmatically; Agent SDK permissions.
FAQ
How do I allow a command in Claude Code without being asked?
Add an allow rule such as Bash(npm run *) to permissions.allow in .claude/settings.json, or approve it with Yes, and don't ask again, or use /permissions.
What is the order of Claude Code permission rules?
Deny, then ask, then allow. The first match in that order decides, and a more specific allow rule cannot carve an exception out of a broader deny rule.
Can CLAUDE.md change what Claude Code is allowed to do?
No. Permission rules are enforced by Claude Code, not by the model. Instructions in CLAUDE.md or a prompt shape what Claude tries, not what Claude Code allows.
Do project settings override my personal deny rules?
No. A deny at any level blocks the call, and managed settings can't be overridden by any other level, including command line flags.