This chapter explains how Claude Code's permission system works, what it can do on its own and what needs your approval. You always stay in control. Reading time: 5 minutes.
Claude always asks before acting
Claude Code will never do anything risky without explicitly asking you. That's a fundamental safety rule.
But there's a practical issue: by default, it asks for confirmation on every command, even the most mundane ones. Run a typecheck? Confirmation. Run lint? Confirmation. Read a file? Confirmation. After 50 confirmations in 1 hour, you go nuts.
The solution: pre-authorize safe commands while keeping validation for risky actions.
The 3 risk levels
Safe, automatic
Claude does this freely, without asking you anything:
- Read files
- Navigate the code
- Analyze the project
- Search for patterns in the code
Moderate, asks for confirmation
Claude shows you what it's about to do and waits for your OK:
- Modify files
- Install packages
- Run commands (tests, build, migrations)
- Create new files
Risky, always asked
Claude explains in detail and waits for your explicit validation:
- Push code to GitHub (
git push) - Delete files or branches
- Irreversible actions (database reset, etc.)
- Unknown or unusual commands
Pre-authorize safe commands
To avoid clicking "Yes" 50 times per session, create a config file that automatically allows repetitive, risk-free commands.
Create the file
Create the file .claude/settings.local.json at the root of your project:
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(npx supabase *)",
"Bash(npx vitest *)",
"Bash(git status*)",
"Bash(git diff*)",
"Bash(git log*)",
"Bash(git add *)",
"Bash(git commit*)"
]
}
}
Wildcard syntax: use * as a wildcard. Bash(npm run *) allows npm run dev, npm run build, npm run test, etc. with a single rule. No need to
list every variant.
What it allows
| Command | Why it's safe |
|---|---|
npm run typecheck | Checks types, modifies nothing |
npm run lint | Checks style, modifies nothing |
npm run test | Runs tests, modifies nothing |
npm run build | Compiles the app, doesn't change source code |
npx supabase | Local Supabase commands |
git status/diff/log | Read-only on Git state |
git add/commit | Local save (no push) |
What still asks for confirmation
git push(sending code online)npm install(adding dependencies)- File deletion
- Unknown commands
Trust mode
If you're in a heavy dev session and you fully trust Claude, you can switch to "trust" mode from the interface. Claude won't ask for confirmation on anything anymore.
Watch out: trust mode is powerful but risky. Use it only when you know exactly what Claude is going to do (e.g. running a plan you've validated). Don't leave it on permanently.
The day-to-day experience
With a well-configured permissions file, here's what it looks like in practice:
- Claude writes code → you see files getting modified live in VS Code
- Claude runs typecheck → goes through automatically, no confirmation
- Claude runs lint → goes through automatically
- Claude wants to install a package → asks "Install
framer-motion?" → you approve - Claude wants to commit → goes through automatically with the right message
- Claude wants to push → asks "Push to branch
feat/streak?" → you approve
The result: you only click on decisions that actually matter.
The safety net: the deny list
More powerful than the allowlist: the deny list. It blocks dangerous commands even in full trust mode. It's your ultimate safeguard.
In your ~/.claude/settings.json (global, for all projects):
{
"permissions": {
"deny": [
"Bash(rm -rf*)",
"Bash(sudo*)",
"Bash(supabase db reset*)",
"Bash(git push --force*)",
"Bash(git push -f*)",
"Bash(git reset --hard*)",
"Bash(git clean -f*)"
]
}
}
These commands are blocked no matter what, even if you're in trust mode,
even if Claude thinks it's necessary. rm -rf can wipe out your entire
project. supabase db reset destroys all data and credentials. git push --force can overwrite your team's work. Better safe than sorry.
The difference between allow and deny:
- allow (
.claude/settings.local.jsonper project): commands allowed without confirmation - deny (
~/.claude/settings.jsonglobal): commands forbidden everywhere, even in trust mode
Takeaway: configure the allowlist once per project and the deny list once globally. You eliminate 90% of useless confirmations while blocking dangerous commands. It's the perfect balance between flow and safety.