The first time you run Claude Code, you will see a prompt like this:
Claude wants to run: npm install
[1] Allow
[2] Allow always for this command
[3] DenyThat prompt is the brake pedal. Claude can read files, write files, run commands, fetch URLs, and call external tools. Each of those actions can change something on your machine. Permissions are how you tell Claude what it can do without asking, what it must ask about, and what it cannot do at all.
A contractor working on your house gets a set of keys. Some rooms are already open. Some need you to come and unlock them yourself. Some are off limits. The permission system is that set of keys.
There is also a default that matters: any command that does not match a rule requires manual approval. Claude is not trusted until you say so.
Permissions live in settings.json. JSON is a way of writing structured data in plain text: curly braces for groups, square brackets for lists, string values in quotes. There are three lists inside the permissions group. The order matters.
{
"permissions": {
"deny": ["Bash(rm -rf *)", "Bash(git push --force *)"],
"ask": ["Bash(git push *)", "Bash(npm publish *)"],
"allow": ["Bash(npm test *)", "Bash(npm run *)", "Read(*)"]
}
}Rules are evaluated in order: deny first, then ask, then allow. The first match wins. A command that matches deny will never run, even if allow would also match it.
The format is ToolName(pattern). The pattern uses * as a wildcard. Bash(git push *) matches every git push command regardless of what follows. Read(*) matches reading any file. This wildcard shorthand is called globbing. Read, Edit, and Write can all be scoped to specific file paths the same way.
One more thing the tutorial usually skips: even an allowlisted command gets scanned for injection. If a command looks suspicious, Claude will prompt you regardless of the allow list.
Info
Tip: Start with a small allow list, an empty deny list, and let Claude ask about everything else. You will learn which commands are safe to auto-approve much faster than you can guess up front.
Claude Code reads permissions from three places, in this order:
~/.claude/settings.json # global, all projects
<repo>/.claude/settings.json # project, shared via git
<repo>/.claude/settings.local.json # project, gitignoredFor most scalar settings, project overrides global and local overrides project. But permission lists work differently: the lists from all three files are combined. A rule in your global file and a rule in your project file both apply at the same time. The deny/ask/allow logic then runs across that merged set.
Use each file for a different scope:
Read(*), common dev commands.Info
Warning: Anything you commit in .claude/settings.json runs for every teammate who pulls the repo. Do not commit a wildcard like Bash(*). The next person who runs Claude on a fresh checkout will hand the model a loaded shotgun.
You can review and change your permission rules without leaving the chat. Type /permissions and Claude opens a view of the current rules across all three files.
/permissionsFrom there you can add, remove, or move rules between scopes. Faster than editing settings.json by hand, and it shows which rule is active for the command Claude just asked about.
Claude Code's write access stays inside the folder where you started it. It cannot create or modify files in parent directories unless you explicitly allow it. If you open Claude in ~/projects/my-app, its edits stay inside my-app.
This matters because it is the first line of defense, before any rule you write yourself. Even with a permissive allow list, Claude cannot accidentally overwrite files in your home directory or another project on your machine. This is a write boundary, not a read one. Claude can still read files outside the folder, like a shared system library, so deny reads of sensitive paths such as your SSH keys if that matters to you.
Some sessions you trust more than others. If you are doing a long refactor and every prompt produces edits you already expected, the constant approvals slow you down.
Shift+Tab cycles through three modes:
normal → Claude asks for every non-allowed action
auto-accept edits → Claude auto-approves edits and basic file commands, still asks about other Bash
plan mode → Claude can read but not change anythingAuto-accept edits is the middle gear. Use it when you have already reviewed the plan and want Claude to apply the edits without stopping at each file. Most shell commands still need approval, though basic file commands like mkdir and mv run automatically too.
You can also enable it from the start with a flag:
claude --permission-mode acceptEditsInfo
Tip: Pair auto-accept edits with frequent git diff. Auto-accept does not mean unreviewed. It means reviewed at the end instead of at every step.
There is a flag that turns the whole system off:
claude --dangerously-skip-permissionsThis tells Claude to run any tool without asking. No prompts. No allow list. No deny list. The flag is named the way it is because Anthropic wants you to think before you type it.
Use it only in environments where Claude cannot cause damage that matters:
Never use it on your laptop with your real codebase, your SSH keys, and your password manager open in the next tab. The whole point of the permission system is that AI models occasionally do the wrong thing. Without the brakes, the wrong thing happens at full speed.
Admins can disable this flag entirely through managed settings (disableBypassPermissionsMode). If you work somewhere with strict policies, the flag may already be off.
Here is a configuration that works well for most developers on a personal project. Drop it in ~/.claude/settings.json:
{
"permissions": {
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force *)",
"Bash(sudo *)"
],
"ask": [
"Bash(git push *)",
"Bash(npm publish *)",
"Bash(docker *)"
],
"allow": [
"Read(*)",
"Bash(ls *)",
"Bash(git status)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(npm test *)",
"Bash(npm run *)"
]
}
}This pre-approves reading and inspecting, asks before pushing or publishing, and refuses the truly dangerous patterns. A quick note on those: rm -rf * recursively deletes everything from the current folder down with no warning, git push --force can overwrite work other people have already pushed, and sudo runs a command with administrator privileges (so a mistake under sudo can damage the whole system, not just your project).
After a week of use you will notice which commands you keep approving by hand. Move those into allow. Run /fewer-permission-prompts to let Claude suggest the additions for you.
The permission system is the difference between Claude Code as an assistant and Claude Code as a loose process running on your machine. Three lists, three scopes, three modes. Workspace isolation keeps Claude inside your project folder by default, before any rule you write. Start strict, loosen as you learn which commands are safe in your workflow. Auto-accept when you trust the session, plan mode when you do not. The bypass flag exists, but it is for sandboxes, not for everyday work.
Next up: 10. Choosing Models: /model, Effort Levels, and /fast.
Discussion
0 comments· Help other readers by sharing what worked (or didn't) for you.
Sign in to join the discussion.
No comments yet. Be the first to share your thoughts!