What Are Claude Code Permissions?
Claude Code permissions are rules that govern which tools the agent can use and which files, commands, or domains it can touch. They live in settings.json files under a permissions object with three arrays -- allow, deny, ask -- plus a defaultMode that sets the overall safety level for a session.
According to the official Claude Code documentation, rules are evaluated in a fixed order: deny first, then ask, then allow. The first matching rule wins, so a deny rule always beats a conflicting allow rule. This precedence is how managed settings enforce organizational policy that project and user settings cannot override.
Allow
Permit tool calls without prompting. Use for safe, routine operations like npm run build, git status, or reads inside the working directory.
Ask
Force a confirmation prompt even if allow rules would match. Use for commands you want to double-check, like git push or production migrations.
Deny
Block tool calls outright. Use for destructive patterns like rm -rf /, git push --force, and reads against .env files.
defaultMode
The baseline for tools that no rule matches. Six values: default, acceptEdits, plan, auto, dontAsk, and bypassPermissions.
The Three Tool Tiers
Claude Code groups tools into three tiers based on risk. Each tier has a different default approval behavior, and the "Yes, don't ask again" option on a prompt persists for a different scope.
| Tier | Example | Approval | "Don't ask again" scope |
|---|---|---|---|
| Read-only | Read, Grep, Glob | No prompt | N/A |
| Bash | Shell execution | Prompt each new command | Per project + command prefix |
| File modification | Edit, Write | Prompt first time | Until session end |
Bash is the sharpest edge: Claude Code saves a rule per project and per command prefix, so "Yes, don't ask again" on npm test writes Bash(npm test *) to .claude/settings.local.json and the prompt never returns for that project.
All 6 Permission Modes Explained
Most guides cover three or four modes. Claude Code actually ships six as of the April 2026 release. Switch between them mid-session with Shift+Tab, or pin a default in settings.json with defaultMode.
default
SAFEStandard behavior. Prompts on the first use of each Bash command or file edit. Use for exploratory work in an unfamiliar repo or any time a mistake would be expensive.
acceptEdits
PRODUCTIVITYAuto-approves file edits and common filesystem Bash commands inside the working directory: mkdir, touch, rm, rmdir, mv, cp, and sed. Other Bash commands still prompt. Best for review-after workflows where you read the diff before committing.
plan
READ-ONLYClaude reads and explores but cannot edit or execute. It ends with a proposed plan. Prefix a single prompt with /plan to enter this mode for just that turn. Useful for research, architecture work, and anything you want to review before touching code.
auto
RESEARCH PREVIEWAuto-approves tool calls with a background classifier that checks each action against your declared infrastructure in the autoMode.environment array. Denials land in the "Recently denied" tab inside /permissions and can be retried with a keystroke.
dontAsk
ALLOWLISTAuto-denies every tool call that no allow rule covers. Zero prompts, zero surprises. Best for CI pipelines where you want the run to fail loudly if Claude tries something outside the approved allowlist.
bypassPermissions
DANGEROUSSkips permission prompts for everything except writes to .git, .claude (outside /commands, /agents, and /skills), .vscode, .idea, and .husky. No prompt injection protection. Only safe inside a container, VM, or ephemeral sandbox.
Using the /permissions Command
The fastest way to manage permissions is the built-in /permissions command. Run it inside any Claude Code session to see every active rule, the settings.json file each one came from, and a tab for actions that auto mode recently denied.
Add a rule
Type a rule in the input and press enter. Example: allow Bash(git commit *) writes to your local project settings immediately.
Remove a rule
Select the rule in the list and press d. Claude Code deletes it from the source settings.json.
Review denied actions
Switch to the "Recently denied" tab to see what auto mode rejected. Press r on any entry to mark it for retry; when you exit the dialog, Claude Code resumes the conversation with permission to proceed.
CLI flags
Outside a session, use --allowedTools and --disallowedTools to add rules for a single run. --dangerously-skip-permissions flips defaultMode to bypassPermissions for that invocation.
Permission Rule Syntax Reference
Every rule follows the format Tool or Tool(specifier). The specifier grammar depends on the tool. Bash supports shell-style wildcards. Read and Edit follow gitignore patterns. WebFetch uses domain: prefixes. MCP uses double-underscore paths.
Bash patterns
| Rule | Matches |
|---|---|
Bash(npm run build) | Exactly npm run build |
Bash(npm *) | Any command starting with npm |
Bash(ls *) | ls -la, but NOT lsof |
Bash(ls*) | Both ls -la AND lsof |
Bash(* install) | Any command ending with install |
Bash(git * main) | git checkout main, git push origin main |
The space before * matters. Bash(ls *) enforces a word boundary and will not match lsof. This is the single most common Bash permissions mistake. The dialog writes the space-separated form when you click "Yes, don't ask again", so copy that style.
Read and Edit patterns (gitignore syntax)
| Pattern | Meaning | Example |
|---|---|---|
//path | Absolute path from filesystem root | Read(//Users/alice/secrets/**) |
~/path | Path from home directory | Read(~/.ssh/**) |
/path | Relative to project root | Edit(/src/**/*.ts) |
path | Relative to current directory | Read(*.env) |
A leading single slash is NOT an absolute path. Use a double slash for absolute. * matches one directory; ** matches recursively. Windows paths normalize to POSIX before matching, so a drive letter like C:\ becomes /c/.
WebFetch, MCP, and Agent
WebFetch(domain:example.com)
Only allows fetches against the given domain. Does not block Bash network tools like curl or wget -- pair with a Bash deny rule or enable the sandbox for real URL filtering.
mcp__server__tool
Target a specific MCP tool. mcp__puppeteer matches every tool from the puppeteer server; mcp__puppeteer__* is equivalent; mcp__puppeteer__puppeteer_navigate matches a single tool.
Agent(Name)
Controls which subagents Claude can dispatch. Add Agent(Explore) to the deny array to disable the built-in Explore agent, or name a custom agent defined in .claude/agents/.
Compound Commands, Process Wrappers, and Read-Only Shortcuts
Bash rules go through three preprocessing steps before matching: compound splitting, wrapper stripping, and read-only auto-approval. Each step has edge cases that break otherwise sensible rules.
Compound commands
Shell operators &&, ||, ;, |, |&, &, and newlines split a command into subcommands. Each subcommand must match a rule independently. Bash(safe *) does not cover safe && evil.
Stripped wrappers
Claude Code strips timeout, time, nice, nohup, stdbuf, and bare xargs before matching. So Bash(npm test *) matches timeout 30 npm test automatically.
Non-stripped wrappers
direnv exec, devbox run, mise exec, npx, and docker exec are NOT stripped. A rule like Bash(devbox run *) grants devbox run rm -rf .. Write specific rules like Bash(devbox run npm test) instead.
Built-in read-only set
Commands like ls, cat, head, tail, grep, find, wc, diff, stat, du, and read-only forms of git run without prompts in every mode. Not configurable. Add an ask or deny rule if you need a prompt on one.
Symlinks check both paths
Allow rules require BOTH the symlink and its target to match. Deny rules trigger if EITHER matches. A symlink at ./project/key pointing to ~/.ssh/id_rsa is blocked when Read(~/.ssh/**) is in deny.
Settings Precedence Order
Permission rules merge across five settings levels. Higher levels override lower ones, and a deny at any level blocks a tool no matter what lower levels do. Organizations use this to enforce policy that individual developers cannot disable.
| Priority | Source | Typical Use |
|---|---|---|
| 1 (highest) | Managed settings | Org-wide policy; cannot be overridden |
| 2 | Command line flags | --allowedTools, --disallowedTools |
| 3 | .claude/settings.local.json | Personal project overrides (gitignored) |
| 4 | .claude/settings.json | Team rules, shared via git |
| 5 (lowest) | ~/.claude/settings.json | Personal defaults across every project |
Deny-wins across levels. If user settings allow Bash(git push) but project settings deny it, the push is blocked. A managed-settings deny cannot be overridden by --allowedTools on the command line.
5 Production-Ready Permission Configs
Copy any of these into ~/.claude/settings.json or a project .claude/settings.json. Each one solves a specific friction point.
1. Daily developer config
Pre-approves the commands you run every day without opening dangerous territory. Keep defaultMode at default so anything unfamiliar still prompts.
{
"permissions": {
"defaultMode": "default",
"allow": [
"Bash(npm *)",
"Bash(pnpm *)",
"Bash(bun *)",
"Bash(git status)",
"Bash(git log *)",
"Bash(git diff *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(jest *)",
"Bash(vitest *)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force*)",
"Read(./.env)",
"Read(./.env.*)"
]
}
}2. CI pipeline config (dontAsk mode)
Pin the allowlist tightly and fail loudly on anything else. Perfect for GitHub Actions runs that should never prompt and should never improvise.
{
"permissions": {
"defaultMode": "dontAsk",
"allow": [
"Read",
"Grep",
"Glob",
"Edit(/src/**)",
"Write(/src/**)",
"Bash(npm ci)",
"Bash(npm test)",
"Bash(npm run build)",
"Bash(npm run lint)"
]
}
}3. Sandbox config (bypassPermissions)
Container or VM only. Skips every prompt and unblocks fully autonomous loops. Never use outside an isolated environment.
{
"permissions": {
"defaultMode": "bypassPermissions",
"deny": [
"Bash(rm -rf /*)",
"Bash(curl * | sh)",
"Bash(curl * | bash)",
"Bash(wget * | sh)"
]
}
}4. Managed org policy (admin only)
Deployed through MDM or an enterprise managed settings file. Locks bypass mode off, pins approved MCP servers, and blocks network exfil patterns regardless of what developers put in their own settings.
{
"permissions": {
"defaultMode": "default",
"disableBypassPermissionsMode": "disable",
"deny": [
"Bash(curl *)",
"Bash(wget *)",
"Bash(git push --force*)",
"Read(//etc/**)",
"WebFetch(domain:paste.ee)",
"WebFetch(domain:transfer.sh)"
]
},
"allowManagedPermissionRulesOnly": false
}5. Multi-repo access with additionalDirectories
Let Claude read and edit across sibling repos without re-invoking with --add-dir every session. Permission rules apply to the additional directories the same way they apply to the launch directory.
{
"permissions": {
"defaultMode": "acceptEdits",
"additionalDirectories": [
"~/work/monorepo/packages/api",
"~/work/monorepo/packages/web",
"~/work/shared-types"
],
"deny": [
"Edit(~/work/shared-types/dist/**)",
"Read(~/work/monorepo/**/.env)"
]
}
}The complete playbook
Every permission rule, every mode, and the security reasoning behind each decision -- in the full KaiShips Guide.
This post covers permissions. The guide covers CLAUDE.md architecture, hooks, memory systems, subagents, cron automation, MCP servers, and 5 more chapters of production-tested configs.
Get the KaiShips Guide -- $29Permissions vs Sandboxing: Defense in Depth
Permissions decide what Claude tries to do. Sandboxing decides what the operating system lets it actually do. They are complementary, and the official docs recommend running both for security-sensitive workflows.
Permissions
Apply to every tool. Model-aware: a Read(./.env) deny rule stops the Read tool but NOT cat .env in Bash. Use for behavior control and policy.
Sandboxing
OS-level enforcement on the Bash tool and its child processes. Prevents cat .env, curl evil.com, and other Bash-level attempts to reach restricted resources.
When sandboxing is on, sandboxed Bash commands can run without prompting even with ask: Bash(*) in permissions, because the sandbox boundary substitutes for the prompt. Pair WebFetch(domain:...) rules with sandbox allowedDomains for full network isolation.
Troubleshooting Common Permission Issues
Most permission pain traces back to five recurring patterns. Each one has a clean fix once you know where to look.
Rule added but still prompts
Your allow rule is being shadowed by a higher-precedence ask or deny rule. Run /permissions to see every active rule in priority order and find the conflict.
Bash(ls *) does not match the command
Probably a compound command. A subcommand after &&, |, or a newline is matched independently. Write a rule for every subcommand or approve the full string explicitly.
Read(./.env) deny still leaks the file
The rule only blocks the Read tool. Claude can still run cat .env through Bash. Add a matching Bash deny rule or enable the sandbox to block at the OS level.
Auto mode denies a safe action
Auto mode flags anything outside its trusted environment as exfiltration. Add your infrastructure to autoMode.environment with specific prose like "Source control: github.com/my-org". Open /permissions and press r to retry the denied action after updating settings.
--dangerously-skip-permissions still prompts
Writes to .git, .claude (outside /commands, /agents, /skills), .vscode, .idea, and .husky still prompt even in bypassPermissions. The protection is intentional and cannot be turned off.
Keep Going
Permissions are one layer of Claude Code control. Pair them with hooks for deterministic enforcement, CLAUDE.md for probabilistic guidance, and MCP for external integrations.
Hooks Guide
Deterministic automation at every lifecycle event. PreToolUse hooks extend the permission system.
CLAUDE.md Setup
The instructions file Claude reads on every session. Great for guidelines, weak for enforcement.
Best Configuration
Full settings.json walkthrough with model routing, env vars, and statusline.
Cron Jobs
Headless runs with dontAsk or bypassPermissions so scheduled Claude Code tasks never hang on a prompt.
Frequently Asked Questions
What are Claude Code permissions?
Claude Code permissions are rules that control which tools the agent can use and which files, commands, or domains it can access. They are configured in settings.json using allow, deny, and ask arrays, plus a defaultMode that sets the overall safety level for a session. Rules are evaluated deny first, then ask, then allow, with the first match winning.
How do I allow all commands in Claude Code without prompts?
Start Claude Code with --dangerously-skip-permissions or set defaultMode to bypassPermissions in your settings.json. This mode skips permission prompts except for writes to .git, .claude (outside /commands, /agents, and /skills), .vscode, .idea, and .husky. Only use this mode in isolated environments like containers or VMs.
What is the difference between acceptEdits and bypassPermissions?
acceptEdits auto-approves file edits and common filesystem Bash commands like mkdir, touch, rm, mv, and cp inside the working directory. bypassPermissions auto-approves everything, including arbitrary Bash commands and network calls. acceptEdits keeps Bash safety checks; bypassPermissions removes them entirely.
How do I permanently allow a Bash command?
Add it to the allow array in .claude/settings.json or ~/.claude/settings.json. For example, "Bash(npm run *)" allows every npm run command without prompting. You can also press "Yes, don't ask again" on a permission prompt and Claude Code will write the rule to your local project settings automatically.
Do Claude Code permissions apply to Bash subprocesses?
No. A Read(./.env) deny rule blocks the Read tool but does not prevent cat .env in Bash. For OS-level enforcement that blocks all processes from accessing a path, enable the Claude Code sandbox in addition to permission rules. Permissions and sandboxing are complementary layers.
What is the order of Claude Code settings precedence?
Managed settings override everything, then command line flags, then .claude/settings.local.json, then .claude/settings.json, then ~/.claude/settings.json. If any level denies a tool, no lower level can allow it. A managed-settings deny cannot be overridden by --allowedTools or by any user rule.