ASK KNOX
beta
LESSON 389

Permission Modes & the Allowlist

Four permission modes control how aggressively Claude Code asks for confirmation — and an allowlist designed correctly eliminates 90% of prompts without sacrificing any safety.

6 min read·Skills, Hooks & Slash Commands

Introduction

Every time Claude Code prompts "May I run this command?", it's asking you to make a trust decision in the moment. Some of those decisions matter — a destructive operation deserves review. Most don't — running a test suite for the fifth time in a session is not a decision that needs human attention.

Prompt fatigue is real: when everything prompts, operators approve without reading. The allow/deny list and permission modes exist to direct attention correctly — surface the decisions that matter, eliminate the ones that don't.

The Four Permission Modes

Default mode prompts for every new tool type. It's appropriate for unfamiliar codebases or first sessions on a project — you want to see what Claude is doing before you trust it. In day-to-day development, it's too noisy.

acceptEdits pre-approves all Write, Edit, and Create operations. File edits no longer prompt. Bash operations still require confirmation. This is Knox's standard mode for development sessions — file changes are expected and low-risk in the context of a coding session; Bash execution deserves more scrutiny.

plan mode shows Claude's plan before executing any tools. This is the right mode for high-stakes tasks: production deploys, multi-repo operations, database migrations. Claude shows you what it will do, you approve or modify, then it executes.

bypass (--dangerously-skip-permissions) approves everything automatically. This is correct for CI pipelines where there's no human to respond to prompts — but only inside an isolated, sandboxed runner (an ephemeral container with scoped, throwaway credentials), so that a destructive mistake harms nothing of value. Branch protection guards the repository, not the runner; deny rules and sandboxing remain the real containment. It is never appropriate in interactive sessions.

Designing the Allowlist

The allowlist is the surgical tool: it allows specific tool patterns without requiring confirmation, even in default mode.

Design principle: allow what you trust without reviewing every time

The test runner pattern — Bash(npm run test:*) — is a good example. Knox runs tests dozens of times per session. He doesn't need to confirm each run; he trusts the command completely. Putting it in the allowlist removes that friction while leaving it available for the hook to analyze if needed.

Design principle: deny what you never want to happen

Deny entries are the hard floor. They block patterns regardless of mode, regardless of who's running the session, regardless of any prompt or instruction. Bash(git push --force:*) is Knox's clearest deny — no session, no context, no justification makes force-pushing to remote branches acceptable.

The asymmetry is intentional: allowlist entries reduce friction for trusted operations; denylist entries are unconditional safety floors. An operator who understands this distinction will have a clean, maintainable permission setup.

Allowlist Patterns That Earn Their Place

Not every command belongs in the allowlist. The test: "Do I ever want to review this before it runs?" If yes, leave it as a prompt. If no, add it.

Commands that consistently belong in allowlists for development:

  • Test runners: Bash(npm run test:*), Bash(pytest:*)
  • Type checkers: Bash(npx tsc --noEmit:*)
  • Linters: Bash(npm run lint:*)
  • File reading: Bash(cat:*), Bash(ls:*)
  • Git status queries: Bash(git diff:*), Bash(git log:*)

Commands that should remain as prompts or be denied:

  • Git push: reviewed as a milestone, not a routine operation
  • npm install: dependency changes deserve attention
  • Docker operations: infrastructure changes are significant
  • Database operations: any DB mutation deserves a human eye

Now write the block yourself. Allow the runners you fire dozens of times a session so they stop prompting, and set the unconditional deny floor that holds in every mode.

The Prompt Audit

Once a month, Knox runs through his permission setup with one question: "Which prompts appeared in the last 30 days that I approved without reading?" Those are candidates for the allowlist. "Which denies fired and were correct?" Those are candidates for documentation in lessons.md.

The audit keeps the permission setup calibrated to actual usage rather than theoretical risk. A deny entry that never fires deserves re-evaluation — it may be too narrow to catch the actual threat, or it may not be needed at all.

What's Next

The next lesson covers model routing and subagent configuration — how to route different task types to different models, configure subagent tool restrictions, and build a routing table that cuts token costs without sacrificing quality on the tasks that matter.