Claude Code Permissions
Five permission modes, one keystroke to cycle them, and a clean way to match the mode to the task you are on. Here is the full rule syntax and when to use each.
Problem: Claude Code asking for permission on every file edit and every command kills your flow and burns time.
Quick Win: Press Shift+Tab to cycle through permission modes on the fly:
# Press Shift+Tab to cycle:
normal → auto-accept edits → plan mode → normalYou can rebind this if Shift+Tab clashes with your terminal. Now you control the flow without touching a config file. Match the mode to the task and the interruption cycle stops.
All Permission Modes
Claude Code ships five permission modes, each built for a different kind of work. Three core modes cycle via Shift+Tab, and two more live in config.
Normal Mode (default)
Normal mode prompts before any potentially dangerous operation. You see confirmation dialogs for:
- File edits and changes
- Terminal command execution
- System operations
- Directory changes
Security over speed. Use it for:
- Production code
- Codebases you do not know well
- Learning new techniques
- High-risk operations
Auto-Accept Mode (acceptEdits)
Auto-accept mode drops the permission prompts for file edits for the rest of the session. Claude runs approved operations straight through.
Turn it on by pressing Shift+Tab until the UI shows "auto-accept edit on".
Best for:
- Big refactoring sessions
- Working through a well-defined plan
- Research and documentation work
- Repetitive edits across many files
Plan Mode (plan)
Plan mode locks Claude to read-only operations. Claude can analyse freely, but nothing on disk changes.
Perfect for:
- First-pass codebase exploration
- Architecture analysis
- Planning complex features
- Code review sessions
Don't Ask Mode (dontAsk)
Don't Ask mode auto-denies any tool use unless the tool is already pre-approved via /permissions or a permissions.allow rule. No prompts. Anything not on the allow list gets silently denied.
Best for:
- CI/CD pipelines with no human around to confirm
- Locked-down environments with a known set of allowed operations
- Running Claude against a strict, pre-built permission policy
Bypass Permissions Mode (bypassPermissions)
Bypass mode skips every permission check. Claude runs any tool without prompting.
# CLI flag equivalent:
claude --dangerously-skip-permissionsOnly turn this on inside fully isolated environments like containers, VMs, or ephemeral CI runners where Claude can't cause lasting damage. The point of the mode is automation in places where the environment itself is the safety boundary.
Warning: Admins can turn this mode off entirely by setting disableBypassPermissionsMode to "disable" in managed settings. If your org blocks it, neither the setting nor the CLI flag will work.
Setting a Persistent Default Mode
Instead of cycling modes every session, pin your preferred default in settings.json:
{
"defaultMode": "acceptEdits"
}Valid values: default, acceptEdits, plan, dontAsk, bypassPermissions. The preference sticks across sessions so you always start where you want.
Managing Permissions with /permissions
Rather than hand-editing JSON files, the built-in /permissions command opens an interactive view:
# Launch the interactive permissions UI
/permissionsFrom there you can:
- See which tools are currently allowed and denied
- Grant access to specific tools or patterns
- Block tools you want off-limits
- See which settings file each rule comes from
- Make changes without restarting Claude Code
Permission Rule Syntax
Rules follow the format Tool or Tool(specifier). They live in the permissions object of your settings.json:
{
"permissions": {
"allow": ["Bash(npm run *)"],
"deny": ["Bash(rm *)"],
"ask": ["Bash(git push *)"]
}
}Three rule types drive behaviour:
- Allow rules let Claude run the tool without asking
- Ask rules prompt for confirmation on every use
- Deny rules block the tool entirely
Rules evaluate in order: deny first, then ask, then allow. The first matching rule wins, so a deny always beats an allow.
Matching All Uses of a Tool
Use just the tool name to cover every invocation:
| Rule | Effect |
|---|---|
Bash | Matches all Bash commands |
WebFetch | Matches all web fetch requests |
Read | Matches all file reads |
Edit | Matches all file edits |
Bash(*) is the same as Bash and matches all Bash commands.
Bash Wildcard Patterns
Bash rules take glob patterns with *. Wildcards can sit anywhere in the rule:
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)",
"Bash(git * main)",
"Bash(* --version)",
"Bash(* --help *)"
],
"deny": ["Bash(git push *)"]
}
}Word boundary semantics: the space in front of * matters. Bash(ls *) matches ls -la but not lsof. Bash(ls*) matches both.
Shell operator awareness: Claude Code knows about shell operators. A rule like Bash(safe-cmd *) will not match safe-cmd && malicious-cmd. That stops chained-command exploitation.
Warning: Bash patterns that try to constrain command arguments are fragile. A rule like Bash(curl http://github.com/ *) is meant to limit curl to GitHub URLs, but it won't catch variations (options before the URL, different protocols, variable expansion). For real URL filtering, block Bash network tools and use WebFetch with domain rules instead.
Read and Edit Patterns
Read and Edit rules use gitignore-style path patterns, four kinds:
| Pattern | Meaning | Example |
|---|---|---|
//path | Absolute path from filesystem root | Read(//Users/alice/secrets/**) |
~/path | Path from home directory | Read(~/Documents/*.pdf) |
/path | Relative to the settings file | Edit(/src/**/*.ts) |
path or ./path | Relative to current directory | Read(*.env) |
Important: a pattern like /Users/alice/file is not an absolute path. It resolves relative to your settings file. Use //Users/alice/file for a true absolute path.
In gitignore patterns, * matches files in a single directory while ** matches recursively. To allow all file access, drop the parentheses entirely: Read, Edit, or Write.
WebFetch Domain Rules
Control which domains Claude can fetch from:
{
"permissions": {
"allow": ["WebFetch(domain:docs.anthropic.com)"],
"deny": ["WebFetch(domain:internal.company.com)"]
}
}MCP Tool Patterns
Control MCP server access at the server or tool level:
| Rule | Effect |
|---|---|
mcp__puppeteer | Matches any tool from the puppeteer server |
mcp__puppeteer__* | Same as above (wildcard syntax) |
mcp__puppeteer__puppeteer_navigate | Matches only the navigate tool specifically |
Task (Subagent) Rules
Control which subagents Claude can spawn using Task(AgentName):
{
"permissions": {
"deny": ["Task(Explore)"]
}
}Agent names include Explore, Plan, and Verify. The --disallowedTools CLI flag can also disable specific agents at startup.
Extending Permissions with Hooks
PreToolUse hooks run before the permission system and can approve, deny, or rewrite tool calls at runtime. That gives you programmatic control on top of static rules.
How Permissions Work with Sandboxing
Permissions and sandboxing stack as two complementary security layers (defense-in-depth):
- Permissions control which tools Claude can use and which files or domains it can touch. They apply to every tool (Bash, Read, Edit, WebFetch, MCP, and the rest).
- Sandboxing adds OS-level enforcement on top, limiting what Bash commands can reach at the filesystem and network level. It applies only to Bash commands and their child processes.
Use both together for the strongest posture:
- Permission deny rules stop Claude from even trying to access restricted resources
- Sandbox restrictions keep Bash commands inside defined boundaries, even when a prompt injection bypasses Claude's decision-making
- Filesystem restrictions in the sandbox use
ReadandEditdeny rules (not separate sandbox config) - Network restrictions mix
WebFetchpermission rules with the sandbox'sallowedDomainslist
Turn on sandboxing with the /sandbox command. On macOS it works out of the box using Seatbelt. On Linux and WSL2, install bubblewrap and socat first.
Managed Permission Settings
For orgs that need central control, admins can ship managed settings files that users and projects cannot override:
| Setting | Effect |
|---|---|
allowManagedPermissionRulesOnly | When true, only permission rules from managed settings apply. User and project rules are ignored. |
disableBypassPermissionsMode | Set to "disable" to block bypassPermissions mode and the --dangerously-skip-permissions CLI flag. |
Managed settings file locations:
- macOS:
/Library/Application Support/ClaudeCode/managed-settings.json - Linux/WSL:
/etc/claude-code/managed-settings.json - Windows:
C:\Program Files\ClaudeCode\managed-settings.json
These are system-wide paths (not user home directories) and need admin privileges. They use the same format as regular settings files but take the highest precedence in the settings hierarchy.
Development Scenario Strategies
Early Development (Use Normal Mode)
Starting a new project or poking around unfamiliar code:
- Keep every permission manual
- Review each suggested change
- Watch how Claude approaches the problem
- Build trust in the AI's calls
Active Development (Use Auto-Accept)
Inside an intensive coding session:
- Turn on auto-accept for trusted file types
- Allow common commands (
npm,git status) - Keep prompts on for system operations
- Ride an uninterrupted workflow
Code Review (Use Plan Mode)
When you are analyzing an existing codebase:
- Flip to plan mode for safety
- Let Claude explore without touching files
- Produce analysis and recommendations
- Switch modes only when you are ready to implement
Common Permission Pitfalls
Over-permissioning: avoid bypassPermissions mode unless you are inside a fully isolated container or VM. dontAsk mode with explicit allow rules is the safer "hands-off" option.
Under-permissioning: clicking "Allow" for the hundredth time defeats the point. Use /permissions to pre-approve repeat operations, or flip to acceptEdits during active development.
Mode Confusion: check your current mode before you start work. The indicator lives in the UI. Set defaultMode in settings.json if you always want the same starting mode.
Blanket Permissions: do not allow all Bash commands. Use specific patterns like Bash(npm run *) to keep the scope tight. And remember: deny rules always beat allow rules.
Fragile Argument Patterns: do not lean on Bash rules to restrict command arguments (like pinning curl to certain URLs). Use WebFetch domain rules for real URL filtering.
What's Next
Five permission modes for five kinds of work. default for safety, acceptEdits for productivity, plan for exploration, dontAsk for automation, and bypassPermissions for isolated environments. Pick the mode that fits the task, and layer sandboxing on top for defense-in-depth.
Related reads:
- Automate permission calls with hooks and the Permission Hook
- Custom keybindings for faster mode cycling
- Feedback loops for faster iteration
- Todo workflows for task tracking
- Git integration for version control
- Configuration basics for settings.json and deeper setup
Stop configuring. Start building.
Claude Code Scheduled Tasks
Desktop tasks for durable automation, CLI /loop for session polling, and the real patterns teams run against error logs, briefings, and PRs.
Claude Code Auto Mode
How auto mode evaluates each tool call with a background AI reviewer, what it blocks by default, and the setup that actually makes it usable.