Permissions — Real-World Examples
Permissions control what Claude can do without asking. Good permission config means no interruptions during normal work, and strong safety guards for dangerous operations.
The Mental Model
Section titled “The Mental Model”Think of permissions in two categories:
Allow list = things Claude does constantly Pre-approve these so work flows without interruptions.
Deny list = things that are irreversible or risky Block these even if Claude thinks they’re the right move.
Everything else falls through to your defaultMode — which determines whether to ask you, auto-approve, or block.
Real Example 1: Node.js / TypeScript Project
Section titled “Real Example 1: Node.js / TypeScript Project”Situation: You want Claude to freely run npm commands, git operations, and file edits — but never force-push, delete files, or touch migrations.
{ "permissions": { "allow": [ "Bash(npm run *)", "Bash(npm test)", "Bash(npm test -- *)", "Bash(npm install)", "Bash(npm install *)", "Bash(npx *)", "Bash(git status)", "Bash(git diff *)", "Bash(git log *)", "Bash(git add *)", "Bash(git commit *)", "Bash(git checkout *)", "Bash(git branch *)", "Bash(git pull)", "Bash(git push origin *)", "Bash(gh pr *)", "Bash(gh issue *)", "Read", "Edit", "Write", "Glob", "Grep" ], "deny": [ "Bash(git push --force *)", "Bash(git push -f *)", "Bash(git reset --hard *)", "Bash(git clean -f *)", "Bash(rm -rf *)", "Bash(sudo *)", "Edit(.env)", "Edit(.env.*)", "Edit(prisma/migrations/**)", "Edit(package-lock.json)" ] }, "defaultMode": "default"}Result: Claude runs tests, edits code, commits, and opens PRs without a single prompt — but you see a confirmation dialog before force pushes or database migration changes.
Real Example 2: Frontend React Project
Section titled “Real Example 2: Frontend React Project”Situation: Claude works on components and styles, but you don’t want it touching build config or CI/CD files.
{ "permissions": { "allow": [ "Bash(npm run *)", "Bash(npm test *)", "Bash(npx *)", "Bash(git *)", "Bash(gh pr *)", "Read", "Edit(src/**)", "Edit(public/**)", "Edit(tests/**)", "Write(src/**)", "Write(tests/**)", "Glob", "Grep", "WebFetch(domain:developer.mozilla.org)", "WebFetch(domain:react.dev)", "WebFetch(domain:tailwindcss.com)" ], "deny": [ "Edit(.github/**)", "Edit(*.config.js)", "Edit(*.config.ts)", "Edit(webpack.config.*)", "Edit(vite.config.*)", "Edit(.eslintrc*)", "Bash(rm -rf *)", "Bash(sudo *)" ] }}Result: Claude freely works within src/ and tests/, but config files are locked — no accidental Webpack changes.
Real Example 3: CI / Headless Automation
Section titled “Real Example 3: CI / Headless Automation”Situation: Claude runs in a GitHub Actions workflow. You want it to make code fixes and commit, but never interact with deployments or infrastructure.
{ "permissions": { "allow": [ "Read", "Edit", "Write", "Glob", "Grep", "Bash(npm run lint *)", "Bash(npm run lint -- --fix)", "Bash(npm test)", "Bash(git add *)", "Bash(git commit *)", "Bash(git push origin *)", "Bash(git diff *)", "Bash(git log *)" ], "deny": [ "Bash(npm run deploy *)", "Bash(kubectl *)", "Bash(aws *)", "Bash(gcloud *)", "Bash(terraform *)", "Bash(rm *)", "Bash(sudo *)", "WebFetch", "WebSearch" ] }, "defaultMode": "dontAsk"}"defaultMode": "dontAsk" means: if a tool isn’t pre-approved, Claude cannot use it (no prompting — CI has no human to answer). This makes CI usage predictable.
Real Example 4: Read-Only Code Reviewer
Section titled “Real Example 4: Read-Only Code Reviewer”Situation: You want Claude in strict read-only mode for auditing — it should never modify anything.
{ "permissions": { "allow": [ "Read", "Glob", "Grep", "Bash(git log *)", "Bash(git diff *)", "Bash(git blame *)", "Bash(git show *)" ], "deny": [ "Edit", "Write", "Bash(git commit *)", "Bash(git push *)", "Bash(git add *)", "Bash(npm *)" ] }, "defaultMode": "plan"}Or just use plan mode flag:
claude --permissions plan "Review the auth system for security issues"Real Example 5: Per-Agent Permissions
Section titled “Real Example 5: Per-Agent Permissions”Situation: Main session has normal permissions. The researcher agent is read-only. The deployer agent has deploy access but nothing else.
.claude/agents/researcher.md:
---name: "researcher"tools: - Read - Grep - Glob - WebSearch - WebFetch---Read-only research agent. Never modifies files..claude/agents/deployer.md:
---name: "deployer"tools: - Bash(npm run deploy:*) - Bash(npm run smoke-test:*) - Read---Deployment-only agent. Cannot edit source files.Claude’s main session can edit code but can’t deploy. The deployer agent can deploy but can’t edit code. Separation of concerns enforced at the tool level.
Bash Pattern Matching Reference
Section titled “Bash Pattern Matching Reference”Understanding how Bash patterns work:
"Bash" # allow ALL bash commands"Bash(npm *)" # npm run, npm test, npm install, npm anything"Bash(npm run *)" # npm run build, npm run dev, npm run test:watch"Bash(npm run build)" # ONLY npm run build (exact match)"Bash(git *)" # all git subcommands"Bash(git push origin *)" # git push origin main, git push origin HEAD, etc."Bash(* --version)" # node --version, npm --version, etc."Bash(cat *)" # cat any fileDeny patterns work the same way:
"Bash(rm -rf *)" # blocks rm -rf anything"Bash(git push --force *)"# blocks force push to any remote/branch"Bash(sudo *)" # blocks all sudo commandsFile Path Pattern Reference
Section titled “File Path Pattern Reference”"Edit(src/**)" // any file under src/"Edit(src/**/*.ts)" // TypeScript files under src/"Edit(/etc/**)" // absolute path — use double slash: //etc/**"Edit(~/.ssh/**)" // home dir paths"Edit(.env)" // exact file match"Edit(.env.*)" // .env.local, .env.production, etc."Read(node_modules/**)" // deny reading node_modules (performance)defaultMode Options Explained
Section titled “defaultMode Options Explained”{ "defaultMode": "default" }| Mode | What happens for unlisted tools |
|---|---|
default | Prompt the user (approve/deny each time) |
acceptEdits | Auto-approve file edits; prompt for shell commands |
plan | Block all writes and shell; read-only browsing |
auto | Claude judges safety; approves safe things automatically |
dontAsk | Block everything not in allow list — no prompting |
bypassPermissions | Approve everything — use only in sandboxed containers |
Recommended for:
- Daily dev work:
defaultoracceptEdits - Exploring/researching:
plan - CI/CD pipelines:
dontAskwith a tight allow list - Quick local scripts:
bypassPermissions(only if you trust the task fully)
Cycling Modes During a Session
Section titled “Cycling Modes During a Session”You don’t have to restart to change modes:
Shift+Tab # cycles: default → acceptEdits → plan → auto → (back to default)Example workflow:
- Start in
planmode — explore the codebase Shift+TabtoacceptEdits— make the changesShift+Tabtoplan— review what changed
🧠 Quick Recall
Section titled “🧠 Quick Recall”- Trick: “Start strict, loosen with evidence.”
👨🏫 Teach It
Section titled “👨🏫 Teach It”- Do: walk one scenario, then have learners write rules for their own repo.
- They’ll trip on: copying someone else’s allowlist without understanding it.
Learning path: ⬅ 14-permissions.md · Index · ➡ 16-sandboxing-and-security.md
Written by Fenil Patel