Before you start
This lesson is for people who already use Claude Code every day. If you do not, take the beginner course first. You need a git repository to try the shared and local files.
Anthropic's docs tie several behaviors to a release. The local settings file lives at the repository root since v2.1.211, and bypassPermissions stopped working from project files in v2.1.257. Run claude --version if something here does not match your install. The facts below come from the settings guide and the permissions page, read on 4 November 2026 and checked again on 5 November.
Settings change faster than most of Claude Code, because a release ships most days. These four releases changed what a settings file can do, with their dates from GitHub and the changelog.
v2.1.211: approvals save at the repository root
"Always allow" rules go to the local file at the root, so a session in a git worktree keeps them.
Source: Claude Code release v2.1.211 on GitHub (read November 5, 2026)
v2.1.257: bypass mode leaves project files
defaultMode: "bypassPermissions"in a project or local file is ignored, asautoalready was.Source: Claude Code release v2.1.257 on GitHub (read November 5, 2026)
v2.1.314: a block on writes outside the project
permissions.blockWritesOutsideWorkingDirectoriesmakes Write, Edit and NotebookEdit refuse paths outside the working directories in every mode.Source: Claude Code changelog (read November 5, 2026)
v2.1.318: one dialog for /permissions
Rule details and delete confirmations open in the standard dialog, and pressing Ctrl+C twice there goes back instead of quitting.
Source: Claude Code changelog (read November 5, 2026)
Set up a team file and a personal override
A committed file for the team and an untracked file for you cover most projects. The block below is the permissions part of Anthropic's example team file, plus the schema line that gives your editor autocomplete and validation.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run *)"
],
"ask": [
"Bash(git push *)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}- Save the block as
.claude/settings.jsonat the top of the repository and commit it. Claude Code reads the shared file from the session's primary working directory, so startclaudeat the repository root. - Create
.claude/settings.local.jsonfor your own exceptions. Claude Code keeps a file it writes itself out of git. If you create the file by hand, add it to.gitignoreyourself. - In the local file, set a key the team file also sets. Local sits above shared project, so only your sessions change.
{"spinnerTipsEnabled": false}turns the tips under the spinner off for you alone. - Run
/status. TheSetting sourcesline names each file Claude Code loaded, such asUser settingsorProject local settings. It confirms which files were read, not which file supplied a key. - Run
/permissionsto list every rule and the file it comes from. Runclaude doctorto list the entries Claude Code rejected. - Edit a rule while a session runs. Claude Code watches the files and applies most edits, including
permissionsandhooks, without a restart.
Which value wins when files disagree
Claude Code uses the value from the highest level that sets a key. Managed settings sit on top, above even --settings, and nothing in your own files overrides a managed key.
Lists are the exception to winner takes all. When permissions.allow appears in several files, Claude Code combines the lists, so each file adds entries and removes none. Four model keys follow their own rules: fallbackModel, modelPicker, availableModels and modelSettings.
Environment variables are not a level. When a behavior has both a variable and a key, the pair decides. ANTHROPIC_MODEL exported in your shell beats the model key from any file, while ANTHROPIC_DEFAULT_MODEL applies only when no file sets model. An env block inside a settings file is an ordinary key that follows the table, and its values overwrite the same variable in your shell.
| Level | Source | Applies to |
|---|---|---|
| 1 | Managed: managed-settings.json, an MDM policy or server-managed settings | Everyone your organization deploys it to |
| 2 | Command line: --settings with inline JSON or a file path | One session. It writes nothing to disk |
| 3 | Project local: .claude/settings.local.json | You, in one project |
| 4 | Shared project: .claude/settings.json | Everyone who clones the repository |
| 5 | User: ~/.claude/settings.json | You, in every project on this machine |
Deny, then ask, then allow
Claude Code checks deny rules first, then ask, then allow. The first match decides, and a narrower rule does not win for being narrower. A deny on Bash(aws *) blocks aws s3 ls even when an allow rule names that exact command. A deny in any scope beats an allow in any other, in either direction.
The config below runs npm scripts and commits without asking and refuses git push. Rules reach past Bash, too. mcp__puppeteer__* matches every tool of one MCP server, and Agent(Explore) in deny turns one subagent off.
Permission rules are enforced by Claude Code, not by the model.
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)"
],
"deny": [
"Bash(git push *)"
]
}
}- Put the
*after the subcommand.Bash(git log *)allows onlygit logcommands, whileBash(git *)allows every git command. Claude Code warns at startup about an allow rule with a*before the subcommand, such asBash(git * main). - A trailing
*with a space also matches the bare command, soBash(ls *)matchesls. The space matters:Bash(ls*)matcheslsofas well. - A compound command needs every part allowed.
Bash(safe-cmd *)does not approvesafe-cmd && other-cmd, and a deny or ask rule fires when any part matches. - Claude Code strips a fixed set of wrappers before matching, such as
timeout,time,niceandnohup. It does not strip runners likenpx,docker execordevbox run, so a rule fordevbox run *allows whatever followsrun.
Path rules and where they anchor
Read and Edit rules use gitignore syntax, and the leading slash trips most people. A single slash is not an absolute path. A rule that starts with / anchors at the settings source, so a /secrets rule in your user settings blocks the secrets folder under ~/.claude and leaves the one in your project alone. The block below lists the four anchors with Anthropic's examples.
Depth depends on the rule type. A one-segment directory pattern such as src covers only the src folder under the current directory as an allow rule, and a folder of that name at any depth as a deny or ask rule. To allow a name at any depth, start the pattern with a double star, as in the last line of the block.
//path Read(//Users/alice/secrets/**) absolute path from the filesystem root
~/path Read(~/Documents/*.pdf) from your home directory
/path Edit(/src/**/*.ts) under the primary working directory, in project settings
path Read(*.env) relative to the current directory
allow Edit(src/**) only the src folder under the current directory
deny Read(secrets/**) a secrets folder at any depth
any Edit(**/src/**) a src folder at any depthA path rule written for Write, NotebookEdit or Glob is accepted and never matches. Use an Edit rule in place of Write and a Read rule in place of Glob.
What a deny rule does not stop
A Bash rule matches the command text Claude writes, after Claude Code splits compound commands and strips wrappers. The same program called another way is a different string, so the rule covers the usual form and nothing more. Anthropic's table shows three examples.
Files have the same gap. Read(./.env) stops cat .env and the file tools, and it does not stop grep -r run over the directory or a script that opens the file itself. When a limit must hold, the permissions page points to two tools. Sandboxing enforces filesystem and network limits at the OS level, and a PreToolUse hook can inspect the whole command, as in the hooks lesson. A hook that exits 2 blocks the call before any allow rule is read.
| Rule | Stops | Does not stop |
|---|---|---|
Bash(curl *) | curl https://example.com | /usr/bin/curl https://example.com and sh -c 'curl https://example.com' |
Bash(rm *) | rm -rf build/ | /bin/rm -rf build/ and bash -c 'rm -rf build/' |
Bash(git push *) | git push origin main | git -C . push origin main and git 'push' origin main |
Sharing a file with a team: trust and the keys that never travel
A committed file cannot grant capability by itself. permissions.allow and permissions.additionalDirectories in .claude/settings.json apply only after each teammate accepts the workspace trust dialog for that folder. Deny and ask rules apply at once, because they only restrict. We would fill the committed file with secret-file denies first and convenience allows second, since the denies protect from the first session.
Non-interactive runs skip the dialog. claude -p and SDK sessions never show it, so project allow rules go unused and Claude Code prints a this workspace has not been trusted warning to stderr. Hooks and the env block from the repository still run. Before you run claude -p in a repository you did not write, pass --setting-sources user, or start with --bare, which the next lesson covers.
Some keys never take effect from a repository file at all.
permissions.defaultModeset toautoorbypassPermissions. Set it in your user settings, or pass--permission-modefor one session.- The OpenTelemetry exporter variables in an
envblock, apart from a few values that turn telemetry off. Set them in managed or user settings. - Any key whose Scope in the settings reference reads
User,User or managed,User, local, or managed,ManagedorGlobal config.
How Claude Code's creator shares permission rules with his team
Boris Cherny created Claude Code and works on Claude at Anthropic. His thread of customizing tips is on X, and tip 5 starts from the defaults. Claude Code pre-approves a small set of safe commands, you add more through /permissions, and the rules go into the team's settings.json. His examples are a Bash rule for bun run and an Edit rule for a docs folder, both in this lesson's wildcard syntax. The Edit rule starts with a slash, so in the team file it anchors at the project. Pasted into your user file, it would point at ~/.claude/docs instead.
Five minutes later, tip 12 said the same about every setting. He counted 37 settings and 84 environment variables that day and suggested the env field over wrapper scripts. Treat those counts as a February snapshot. The settings reference lists every key today.
Each "Yes, and don't ask again" writes one more rule into .claude/settings.local.json, so your own approvals pile up there. After a week of work, open /permissions, move the rules every teammate would approve into the shared file, and commit it.
When you do customize, check your settings.json into git so your team can benefit, too.
A screenshot of X's own embed of the post, taken on 5 November 2026, with the time in UTC. X cuts the post short with Show more. Select it to open the post on X.
Common mistakes
- A trailing comma or a
//comment in a settings file. The files are strict JSON, and Claude Code shows a Settings Error at the next start. A-prun shows no dialog and skips the broken file, so runclaude doctorafter a run that ignored a setting. - Trying to carve an exception out of a deny. An allow rule cannot reopen what a deny blocks. Narrow the deny instead.
- Reading
/statusas a trace of each key. It lists loaded files and not the file behind each value./permissionsshows where each rule comes from. - Writing Windows paths as Windows writes them. Claude Code normalizes
C:\Users\aliceto/c/Users/alicebefore matching, so a rule for that drive starts with//c/. - Expecting "Yes, and don't ask again" to beat a project rule. That choice saves an allow rule to your local file, and an allow there does not outrank an ask rule from a project or managed file.
Try it yourself
- Add
Read(./.env)to the project file's deny list, ask Claude to read.env, then open/permissionsand find the file that rule comes from. - Put
Bash(git push *)in ask in the team file and the same rule in allow in your local file. Predict whether Claude Code prompts, then ask it to push and check. - Set
spinnerTipsEnabledtofalsein your user file, then start one session withclaude --settings '{"spinnerTipsEnabled": true}'. The tips return for that session only. - Run
/status, thenclaude doctor, and write down what each one tells you that the other does not.
Next: headless runs
Settings decide what a session may do. In the next lesson, headless runs, claude -p runs where nobody can answer a prompt, so the rules from this lesson become the whole safety model.
Hooks, from an earlier lesson, run your own commands at set points of a session. Standing instructions still belong in a CLAUDE.md file, which settings do not replace.
Check yourself
Your user settings deny Bash(npm run deploy *) and the committed project file allows Bash(npm run *). What happens to npm run deploy prod?
Show answer
B. It is blocked, because a deny in any scope beats an allow Claude Code checks deny rules first, then ask, then allow, across every scope, and the first match decides.
Which file holds your own overrides for one project and stays out of git?
Show answer
C. .claude/settings.local.json Project local settings apply only to you in one project and sit above the shared project file in the precedence stack.
You commit "defaultMode": "auto" in .claude/settings.json. What do your teammates get?
Show answer
A. Nothing, because auto does not take effect from project or local settings The values auto and bypassPermissions only take effect from user or managed settings, or from --permission-mode for one session.
Which protection still holds when Claude runs /usr/bin/curl instead of curl?
Show answer
C. A sandbox network allowlist A Bash rule matches the command text as written, while the sandbox restricts network access at the OS level whatever the command looks like.
Frequently asked questions
Where is the Claude Code settings.json file?
There are four sources: ~/.claude/settings.json for you in every project, .claude/settings.json committed with a project, .claude/settings.local.json for you in one project, and a managed file or policy your organization deploys. Installing Claude Code creates none of them. On Windows, ~/.claude means %USERPROFILE%\.claude.
Where does "Yes, and don't ask again" save my choice?
In .claude/settings.local.json, as an allow rule that only you get. It outranks the shared file, and Claude Code keeps the local file out of git when it creates it.
How do I stop Claude Code from reading my .env file?
Add Read(./.env) and Read(./.env.*) to permissions.deny. Claude Code then excludes matching files from discovery and search, and blocks Edit and Write on those paths. A command that never names the file, such as grep -r over the folder, still gets through, so turn on the sandbox when the file must stay unreadable.
Why is my permission rule ignored?
Start with /status. A higher level may set the same key, or a project allow rule may still wait for workspace trust. Other causes are auto or bypassPermissions in a project file, an invalid entry that Claude Code skipped, and a Write or Glob path rule, which never matches. claude doctor lists what it rejected.
Do I need to restart Claude Code after editing settings?
Usually not. Claude Code reloads user, project, local and managed files when they change, including permissions, hooks and credential helpers. A few keys load once at session start, and removing permissions.blockReadsOutsideWorkingDirectories mid-session keeps the block until you restart each session.
Can I set the permission mode for a whole project?
Yes for default, acceptEdits, plan and dontAsk, through permissions.defaultMode in .claude/settings.json. The values auto and bypassPermissions take effect only from user or managed settings, or from --permission-mode for one session.
Can an admin stop developers from changing these settings?
Yes. Managed settings sit above every other level, and allowManagedPermissionRulesOnly makes them the only source of permission rules. permissions.disableBypassPermissionsMode set to "disable" removes bypass mode, and a developer can see the policy in /status.
Sources
- Claude Code docs: Settings files and precedence https://code.claude.com/docs/en/settings
- Claude Code docs: Configure permissions https://code.claude.com/docs/en/permissions
- Claude Code docs: Example settings files https://code.claude.com/docs/en/settings-example
- Claude Code docs: Settings reference https://code.claude.com/docs/en/settings-reference
- Claude Code release v2.1.211 on GitHub https://github.com/anthropics/claude-code/releases/tag/v2.1.211
- Claude Code release v2.1.257 on GitHub https://github.com/anthropics/claude-code/releases/tag/v2.1.257
- Claude Code changelog https://code.claude.com/docs/en/changelog
- Boris Cherny on X: pre-approve common permissions https://x.com/bcherny/status/2021700332292911228
- Boris Cherny on X: customize all the things https://x.com/bcherny/status/2021701636075458648

