# Claude Code settings.json & Permissions: The Practical Reference > Which of the three settings.json files wins, how allow/deny permission rules actually match, and the safe-by-default setup — a practical Claude Code reference. **Source:** [https://northlabapps.com/blog/claude-code-settings-json/](https://northlabapps.com/blog/claude-code-settings-json/) **Published:** 2026-07-04 **Topics:** claude-code, settings, permissions **Author:** NorthLab Apps (https://northlabapps.com) — makers of NorthLab Folders, a privacy-first folders/search/export extension for ChatGPT and Claude --- Claude Code's permission prompts exist so an agent with a shell can't surprise you. But out of the box you'll be approving `npm test` for the hundredth time, and the fix people reach for — blanket bypass — throws away the guardrails entirely. The actual fix is fifteen minutes with settings.json. This is the reference I wanted when I started: which file wins, how rules match, and a setup that's quiet *and* safe. ## The three files and who wins Claude Code reads settings from three layers, most specific winning: 1. **`~/.claude/settings.json`** — your user file. Applies to every project on your machine. Personal defaults live here. 2. **`.claude/settings.json`** — the project file. Committed to the repo, shared with the team: project policy. 3. **`.claude/settings.local.json`** — your personal overrides for one project. Git-ignored; this is where your machine-specific allowances go without becoming team policy. The layering is the feature. Team policy says what's forbidden; your local file says what *you're* comfortable auto-approving; neither pollutes the other. (If the `.claude` directories themselves are new to you — what else lives in them, what to commit — see [the .claude folder, explained](https://northlabapps.com/blog/what-is-the-claude-folder/).) ## Permission rules: the part worth understanding properly The `permissions` block holds `allow`, `deny`, and `ask` lists. Each entry names a tool, optionally with a matcher for its input: ```json { "permissions": { "allow": [ "Bash(npm run test:*)", "Bash(git diff:*)", "Bash(git log:*)", "Read(~/notes/**)" ], "deny": [ "Bash(git push --force:*)", "Read(.env*)", "WebFetch" ] } } ``` The rules that matter in practice: - **Deny always beats allow.** A denied pattern is blocked even if a broader allow matches it. - **Matchers are prefix-style patterns, not regex.** `Bash(npm run test:*)` covers `npm run test` and everything after it. Precision matters: `Bash(git:*)` allows *all* git — including `git push --force` — which is almost never what you meant. Allow narrow, specific prefixes. - **Deny is also your secrets shield.** `Read(.env*)` keeps the agent from ever opening your env files — enforcement, not etiquette. - **You don't have to hand-edit JSON.** The `/permissions` command edits these interactively, and the "always allow" option on any prompt writes the rule for you. The JSON view is for reviewing and committing policy. ## Permission modes (and the one to be careful with) Claude Code also has session-level modes: the default (ask for anything not allowlisted), **accept-edits** (file edits pre-approved, shell still gated), **plan mode** (read-only — Claude proposes, you approve before anything changes), and **bypass** (no prompts at all). Plan mode is underrated for unfamiliar codebases; bypass belongs in sandboxes and CI, not on your laptop with your dotfiles. The quiet-but-safe goal is: default mode plus a well-tended allowlist. ## What else lives in settings.json Beyond permissions, the file carries the rest of the tool's configuration — the highlights you'll actually touch: - **`env`** — environment variables for every session (API endpoints, feature flags). - **`model`** — your default model. - **`hooks`** — shell commands that fire on lifecycle events, like running your formatter after every file edit. Hooks are enforcement's big brother: where permissions say *no*, hooks say *and then this happens automatically*. They deserve their own post. - **`statusLine`** — the configurable terminal status line (also set up via `/statusline`). ## The starter setup I'd give a teammate 1. In the **project** file: deny the disasters (`git push --force`, reading `.env*`, your deploy command), so policy travels with the repo. 2. In your **local** file: allow your loop — test runner, linter, `git status/diff/log` — each as a narrow prefix. 3. Leave everything else on ask, and use "always allow" prompts to grow the allowlist from real usage rather than speculation. 4. Revisit monthly with `/permissions` — allowlists, like [CLAUDE.md files](https://northlabapps.com/blog/claude-md-complete-guide/), accumulate cruft. The division of labor across Claude Code's config, in one line each: **settings.json enforces, [CLAUDE.md](https://northlabapps.com/blog/claude-md-complete-guide/) instructs, [memory](https://northlabapps.com/blog/claude-code-memory/) accumulates, and [custom commands](https://northlabapps.com/blog/claude-code-commands-cheat-sheet/) package procedures.** Get each concern in its right file and the tool feels like it was configured by someone who likes you. (And if you're looking for where all these files live on disk, the [Mac guide to Claude's folders](https://northlabapps.com/blog/claude-folders-mac/) has the map.) Usual disclosure before the sign-off: we build [NorthLab Folders](https://northlabapps.com/folders) — local folders and search for the claude.ai/ChatGPT/Gemini side of your workflow, where the design conversations happen before the settings get written. ## Frequently asked questions ### Where is Claude Code's settings.json file? Three places, by scope: ~/.claude/settings.json applies to you on every project; .claude/settings.json in a repo applies to that project and should be committed; .claude/settings.local.json is your personal per-project layer and belongs in .gitignore. More specific files override broader ones. ### How do Claude Code permissions work? Through allow, deny, and ask rules matched against tool calls — for example Bash(npm run test:*) allows test commands without prompting, while a deny rule blocks a tool call outright. Deny beats allow. You can edit rules interactively with the /permissions command, and they persist to settings.json. ### How do I stop Claude Code from asking for permission constantly? Allowlist the safe, repetitive commands your workflow actually uses — your test runner, linter, git status/diff — rather than reaching for a blanket bypass. Each allow rule like Bash(make test:*) removes one recurring prompt while keeping genuinely destructive actions behind confirmation. ### What's the difference between settings.json and CLAUDE.md? CLAUDE.md is advisory — instructions the model reads and follows as intent. settings.json is mechanical — permission rules the harness enforces before anything runs. Rules that must be unbreakable (never touch prod, never force-push) belong in settings.json; context and conventions belong in CLAUDE.md.