The Permissions System (Allow / Deny / Ask) and Why It's Not Just Security Theatre

2026-06-10


The Permissions System (Allow / Deny / Ask) and Why It's Not Just Security Theatre

You're running Claude Code on a real project. It asks permission every time it wants to run a shell command. That gets old fast — so you hit "always allow" and move on. That one decision quietly shapes everything Claude will do unsupervised.

The jargon

Tool call — when Claude decides to use a capability (run a shell command, read a file, write a file) rather than just generate text.
.claude/settings.json — a project-level config file where permissions are stored. Lives inside your project folder.
~/claude_desktop_config.json — user-level config on your machine. Applies across all projects unless overridden.

The lesson

The allow/deny/ask system is not primarily about keeping Claude from doing something malicious. It's about controlling autonomy. When you allow a tool, Claude will use it mid-task without stopping to check. When you set it to ask, Claude pauses. That pause is your decision point.

The real value is surgical. You can allow read operations freely, require approval for any write to a production path, and deny network calls entirely on a sensitive project. That's three different risk levels handled automatically — without you having to monitor every step.

How it works

Permissions live in .claude/settings.json at your project root. The structure looks like this:

{
  "permissions": {
    "allow": [
      "Read(*)",
      "Bash(git *)"
    ],
    "deny": [
      "Bash(rm -rf *)"
    ]
  }
}

allow rules let Claude proceed without asking. deny rules block the action outright — Claude can't override them even if you ask it to in chat. ask is the default for anything not covered by either list.

The pattern inside the brackets is a glob or prefix match against the actual command or file path. Bash(git *) allows any git command. Read(src/*) allows reads inside your src folder only.

You can inspect and edit these directly in PowerShell:

code .claude/settings.json

Or let Claude Code write them for you: when it asks permission during a session, answering "always allow" or "always deny" writes the rule into the file automatically.

When to reach for it / when not to

Reach for explicit rules when you're about to run a long agentic task — anything where Claude will take many sequential steps without you in the loop. Locking down what it can do before you start is far easier than untangling what it did after.

Skip the fine-grained configuration for short, exploratory sessions where you're watching every step anyway. Constant interruptions add friction with no real gain when you're already supervising closely.

Don't rely on deny rules as your only protection against destructive commands. They help, but the real safeguard is not pointing Claude at production systems in the first place.

Try it

Open a project you use with Claude Code. Check whether .claude/settings.json exists — if it doesn't, Claude is running on defaults. Add one deliberate deny rule for something you'd never want it to do unattended (deleting files, pushing to a remote, anything irreversible). Then add one allow rule for something you approve every single session. See how the next session feels different.


Don't miss what's next. Subscribe to My Claude Daily Learning: