Claude Code permissions are rules in your settings files that say which tools Claude can use without asking (allow), which always need confirmation (ask), and which are blocked (deny). Rules are checked in the order deny, then ask, then allow, and a permission mode such as Manual, acceptEdits, plan or auto sets the baseline for everything the rules don't cover.
This guide covers the rule syntax, the modes, the /permissions dialog, managed policies, and a safe starting configuration. All of it is based on the official permissions documentation and permission modes page.
How do Claude Code permissions work?
In Manual mode, Claude Code asks before it does anything that changes your machine. Since v2.1.283, interactive terminal and VS Code sessions start in auto mode instead (see the modes below), while claude -p starts in Manual. In Manual mode the defaults look like this:
| Tool type | Example | Asks first? | "Yes, and don't ask again" |
|---|---|---|---|
| Read-only | File reads, Grep | No, inside your working directories | Not needed |
| Bash commands | Shell execution | Yes, except a built-in set of read-only commands | Saved per repository and command |
| File modification | Edit and write files | Yes | Until the session ends |
| Web fetch | WebFetch | Yes, except some preapproved documentation domains | Saved per repository and domain |
| Web search | WebSearch | Yes | Saved per repository |
The built-in read-only commands include ls, cat, pwd, head, tail, grep, find, wc, diff and read-only forms of git. That set is not configurable, but you can add an ask or deny rule to make one of them prompt.
One point the docs stress: permission rules are enforced by Claude Code, not by the model. Telling Claude "never run git push" in CLAUDE.md shapes what it tries, but only a rule, a mode or a hook actually stops it.
What is the permission rule syntax?
A rule is either a tool name, Tool, or a tool with a specifier, Tool(specifier).
| Rule | Matches |
|---|---|
Bash |
Every Bash command |
Bash(npm run build) |
Exactly npm run build |
Bash(npm run *) |
npm run build, npm run test --watch, and bare npm run |
Read(./.env) |
Reading .env in the current directory |
WebFetch(domain:example.com) |
Fetches to example.com |
mcp__github__* |
Every tool from the github MCP server |
Agent(Explore) |
The Explore subagent |
Rules go in the permissions block of a settings file:
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)"
],
"deny": [
"Bash(git push *)"
]
}
}
How Bash wildcards behave:
- Put the
*after the subcommand.Bash(git log *)allows onlygit logcommands;Bash(git *)allows every git command. Claude Code warns at startup about an allow rule with a*before the subcommand. - The space matters.
Bash(ls *)matchesls -labut notlsof.Bash(ls*)matches both. :*is an alternative trailing wildcard, soBash(ls:*)equalsBash(ls *).- Compound commands are split. A rule like
Bash(safe-cmd *)does not approvesafe-cmd && other-cmd. Each part must match on its own, and a deny rule applies if any part matches. - Some wrappers are stripped, such as
timeout,time,niceandnohup, soBash(npm test *)also matchestimeout 30 npm test.
A Bash rule matches the text Claude writes, so it is not a hard security boundary. Bash(git push *) in deny stops git push origin main but not git -C . push origin main, and Bash(curl *) doesn't stop /usr/bin/curl. For enforcement that doesn't depend on command text, use the sandbox or a PreToolUse hook.
How do Read, Edit, WebFetch and MCP rules work?
Read and Edit rules use gitignore-style patterns. The leading characters decide where a path anchors:
| Pattern | Meaning | Example |
|---|---|---|
//path |
Absolute path from the filesystem root | Read(//Users/alice/secrets/**) |
~/path |
From your home directory | Read(~/Documents/*.pdf) |
/path |
Relative to the settings source (the project, in project settings) | Edit(/src/**/*.ts) |
path or ./path |
Relative to the current directory | Read(*.env) |
The common trap: /Users/alice/file is not an absolute path. A single leading slash anchors at the settings source, so in user settings Read(/secrets/**) means ~/.claude/secrets/**. Use // for real absolute paths.
Other details worth knowing:
- Edit rules cover every built-in tool that edits files. Rules written as
Write(path)orGlob(path)are accepted but never match, so useEdit(...)andRead(...). - A Read deny also blocks Edit and Write on that path.
- Read and Edit denies are best-effort for shell commands. They cover recognized commands such as
cat,headandsed, but notgrep -r pattern .or a script that opens files itself. The docs recommend the sandbox for OS-level blocking. - WebFetch rules match hostnames.
WebFetch(domain:*.example.com)matches subdomains but notexample.comitself, so add both if you need both. - MCP rules use the tool name.
mcp__puppeteer__*covers a whole server, andmcp__puppeteer__puppeteer_navigatecovers one tool.
What are the Claude Code permission modes?
The mode sets what runs without a prompt before any rule is considered:
| Mode | Runs without asking | Suited to |
|---|---|---|
default (shown as Manual) |
Reads only | Sensitive work, unfamiliar code |
acceptEdits |
Reads, file edits and common filesystem commands like mkdir and mv |
Iterating on code you review |
plan |
Reads; edits wait until you approve a plan | Exploring before changing anything |
auto |
Everything, with a background classifier checking actions | Long tasks, fewer prompts |
dontAsk |
Reads and pre-approved tools; anything else is denied | Locked-down CI and scripts |
bypassPermissions |
Everything | Isolated containers and VMs only |
Ways to pick a mode:
- During a session, press
Shift+Tabto cycle modes. The status bar shows the current one. - For one session, start with
claude --permission-mode plan(or any mode name). - As a default, set
permissions.defaultModein a settings file.autoandbypassPermissionsdon't take effect from project or local settings, so put those in~/.claude/settings.json.
Since v2.1.283, interactive terminal and VS Code sessions start in auto mode when no flag or setting picks another mode, while claude -p runs start in Manual. Auto mode needs a supported model, and administrators can turn it off. A few things are never auto-approved in any mode, including tools matched by an explicit ask rule and recognized rm commands aimed at critical paths. Writes to protected paths such as .git, .vscode and the .claude folder are not pre-approved by allow rules, and they prompt in Manual and acceptEdits modes. For plan mode specifically, see the Claude Code plan mode guide.
How do you manage rules with /permissions?
Run /permissions to see every rule and the settings file it comes from. You can add or remove rules there, even while Claude is working; the change applies from Claude's next tool call.
When you answer a prompt with "Yes, and don't ask again" for a Bash command or a fetch domain, Claude Code saves an allow rule to .claude/settings.local.json at the repository root. That rule then applies in every session in the repository, including subdirectories and worktrees. Approving a compound command like git status && npm test saves separate rules for the parts that needed approval.
Two more controls are handy:
- Comments on prompts. On most prompts, move to Yes or No and press
Tabto attach a note. A comment on No tells Claude why you refused, and it keeps working. - Extra directories. By default Claude can access only the folder where you started it. Add more with
--add-dir,/add-dir, oradditionalDirectoriesin settings.
How do managed policies and precedence work?
Permission rules follow normal settings precedence, with one strong guarantee: if a tool is denied at any level, no other level can allow it. A user-level deny beats a project allow, and a managed deny can't be overridden by --allowedTools.
For organizations, administrators deploy managed settings that users can't override. The keys that matter for permissions:
permissions.denyin managed settings blocks a tool on every machine.allowManagedPermissionRulesOnlymakes managed settings the only source of permission rules.permissions.disableBypassPermissionsModeset to"disable"removes bypass mode and makes Claude Code reject--dangerously-skip-permissions. It works from any scope, so you can set it for yourself too.permissions.disableAutoModeset to"disable"removes auto mode.
Project files have a safety step of their own. permissions.allow rules and additionalDirectories in a committed .claude/settings.json apply only after you accept the workspace trust dialog for that folder, and the dialog lists what they would grant. Deny and ask rules apply immediately because they only restrict.
What is a safe default permission setup?
A reasonable starting point for a project, adapted from the official example settings files:
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git diff *)"
],
"ask": [
"Bash(git push *)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}
Guidelines that follow from the docs:
- Allow the commands you run all day, like your test and lint scripts, with narrow patterns. Prefer
Bash(npm run *)overBash(npm *). - Put irreversible actions in
ask, such as pushes and deploys. - Deny secrets by path, then add the sandbox so shell commands can't read them either.
- Don't allow environment runners broadly.
Bash(npx *)orBash(docker exec *)approves whatever follows. WriteBash(devbox run npm test)style rules instead. - Use bypass mode only inside a container or VM, and run it as a non-root user.
- Reach for a hook when you need logic, such as "allow all Bash except these commands." A hook that exits with code 2 blocks the call even when an allow rule matches.
Keep your reusable prompts with your rules
Permission rules make a project safer to hand to an agent; good prompts and skills make it more useful. Kipt stores your prompts and Agent Skills as Markdown in a private GitHub repo you own, and installs every skill into Claude Code or Codex with one npx skills add command, so the same workflows follow you to each project.