Back to blog

April 21, 2026

Security & Configuration

Claude Code Permissions: Complete Config Guide (2026)

Claude Code permissions control every tool, file, and command the agent can touch. Six modes, three rule types, and a precedence order that trips up most teams. This is the complete 2026 reference with production configs you can paste today.

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.

TierExampleApproval"Don't ask again" scope
Read-onlyRead, Grep, GlobNo promptN/A
BashShell executionPrompt each new commandPer project + command prefix
File modificationEdit, WritePrompt first timeUntil 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

SAFE

Standard 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

PRODUCTIVITY

Auto-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-ONLY

Claude 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 PREVIEW

Auto-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

ALLOWLIST

Auto-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

DANGEROUS

Skips 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

RuleMatches
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)

PatternMeaningExample
//pathAbsolute path from filesystem rootRead(//Users/alice/secrets/**)
~/pathPath from home directoryRead(~/.ssh/**)
/pathRelative to project rootEdit(/src/**/*.ts)
pathRelative to current directoryRead(*.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.

PrioritySourceTypical Use
1 (highest)Managed settingsOrg-wide policy; cannot be overridden
2Command line flags--allowedTools, --disallowedTools
3.claude/settings.local.jsonPersonal project overrides (gitignored)
4.claude/settings.jsonTeam rules, shared via git
5 (lowest)~/.claude/settings.jsonPersonal 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 -- $29

Permissions 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.

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.