Skip to main content
This is the last page of the technical sequence: it adds no new behavior, and instead gives the rationale and the tradeoffs behind what Architecture and Analysis engine specify. Read those first if you want to know what happens; read this to know why. CC Safety Net was built after an AI coding agent deleted an entire home directory. That case still stands, but it is no longer the frontier: Claude Code now ships a deterministic circuit breaker for critical paths like that one, while the destructive git commands inside the workspace have no such breaker. Every design choice follows one goal: stop destructive commands before an agent executes them without creating a false sense of security.

Semantic analysis over wildcard patterns

Coding agents support deny rules with wildcard matching, such as a wildcard rule for git reset --hard. Wildcard patterns compare the raw command string against a pattern, so any variation in spacing, flag order, or command wrapping can cause a block to silently fail. Reordering flags (rm -r -f /), wrapping in a shell (sh -c "rm -rf /"), or hiding behind an interpreter all bypass string matching. CC Safety Net instead parses each command and hands it to analyzers that understand the real option grammar of git, rm, Remove-Item, find, xargs, and parallel, so the decision reasons about what the command does rather than what it looks like. The tradeoff is complexity: the parser must handle shell syntax correctly, and each supported command needs its own analyzer. The benefit is bypass resistance for the commands that matter most. The pipeline itself is specified in Architecture, and each analyzer’s exact behavior in Analysis engine.

One fixed order, with the always-on protections first

Every tool call runs through the same ordered stages, on every integration. Two of those stages, protection of the policy files in both scopes and protection of Git metadata, deliberately run before the policy snapshot is loaded. That ordering is the point. A protection evaluated after configuration is only as strong as the configuration, and configuration is what a compromised agent would try to edit first. By denying before the policy file is read, these two guards carry no config state. A preset, an override, or the protected file cannot switch them off. Their denials cannot report a safety level or fallback reason because neither is known yet. This trades diagnostic detail for an unconditional guarantee. Sensitive-path protection sits on the other side of the line, after the snapshot. It is policy-controllable, because which paths count as sensitive is genuinely a local decision, and because deny paths are only useful if you can add your own. The concrete stage table lives in Architecture.

Fail closed when analysis cannot complete

When CC Safety Net cannot complete an analysis, it blocks rather than allows:
  • A thrown error anywhere in the guard becomes a deny attributed to the stage that threw, at every entry point.
  • Exceeding a tool-input bound or a parser budget denies, in every safety level.
  • Strict mode extends fail-closed further, to commands the parser cannot fully understand and to destructive targets it cannot verify.
The reasoning is simple: a safety net that fails open is worse than no safety net, because it creates a false sense of security. Blocking on an unexpected error is annoying but recoverable; allowing a destructive command through is not. Invalid configuration is deliberately not part of this. The runtime drops a rejected configuration source instead of converting it into a denial. Otherwise, a typo in a rulebook could block all work and push users to uninstall the tool instead of fixing the file. Configuration recovery defines that contract and the repair path. See Security model for how these properties are enforced at each trust boundary.

Denials that keep the agent on task

A denial is not an error state. It reaches the agent as a normal tool result in the active session. This behavior determines how CC Safety Net writes each block message. A bare “permission denied” can make an agent retry similar commands or stop the full task. Repeated variants can find an unprotected form and use agent turns. Stopping the full task turns one safety action into a work stoppage. Each message gives the agent a productive next action. The reason states what the command would have done and names a safer alternative when one exists. Every rule carries an intent that selects the closing instruction: report the block and continue with the task, switch to the named alternative, retry with a narrower explicit target, hand the operation to the user, or restructure the command instead of trying variants. Even a fail-closed denial for an internal error carries the intent “restructure, don’t retry.” An unexpected failure of the tool itself still directs the agent to a useful response. The instruction is deliberately advisory. Nothing forces the agent to obey it; enforcement comes from the guard, which blocks a disobedient retry just the same. The message exists to make the compliant path the easiest one, so that in the common case the session absorbs a block and keeps moving. The message anatomy and the full intent table are in How it works.

A minimal dependency surface

CC Safety Net has no runtime package dependencies. Its own bounded POSIX and PowerShell parsers handle segment splitting, quoting, redirection, command substitution, and dynamic-word provenance instead of a third-party grammar. This choice has four reasons:
  • A smaller dependency tree means a smaller supply-chain attack surface, and the parser is the component an attacker most wants to confuse.
  • The analysis needs facts a general tokenizer does not carry, such as which words came from an expansion, which target is anchored to the working directory, and which quoting form was used. An owned parser makes those facts first-class.
  • Parser budgets can be fixed constants rather than configuration, which makes resource exhaustion a bounded, testable failure mode instead of an open-ended one.
  • For the agents that run CC Safety Net as a hook subprocess, it starts fresh on every shell tool call, so startup time matters and fewer dependencies mean faster cold starts.
See Architecture for the parser budgets and the packaging check that keeps the dependency count at zero.

The rulebook system

Earlier versions stored custom rules as inline JSON in a single project file. CC Safety Net replaced this with a rulebook system for four reasons:
  • Sharing. rule add and rule update fetch a rulebook from a GitHub repository at a named ref and vendor it into the consumer’s own rules/<name>/rulebook.json, so teams share blocking policy without copying JSON by hand. The vendored copy is a file in the consumer’s repository. A person can read it and review it in a pull request, and a diff shows what an update changed.
  • Integrity. rule add and rule update resolve the ref to a commit, fetch the file over HTTPS with redirects refused and the response bounded by bytes and time, validate it against the schema, and require its name to match the source. For rulebook_version: 2 they also run the rulebook’s own fixtures against its rules. A source that fails any of these is rejected before anything is written. Nothing re-checks the vendored file against the upstream copy afterwards; from then on it is reviewed like any other file in the repository.
  • Scoping. Rulebooks support separate user and project scopes with distinct configuration directories.
  • Validation. The runtime validates rulebook content against the schema before it can affect a blocking decision.
Custom rules are strictly additive: they can only add restrictions, never relax built-in protections. This keeps the trust boundary simple. See Custom rules for the authoring workflow.

Graduated levels instead of one setting

Protection has three presets: standard, strict, and paranoid. A single on/off switch cannot account for whether a command came from a supervised or untrusted context. Standard optimizes for a human-supervised session: it blocks recognizable destructive commands and sensitive-content access while tolerating unparseable-but-harmless text, so day-to-day work is not interrupted. It is explicitly best-effort against adversarial or dynamic input and is not the right setting when commands may originate from prompt injection or another untrusted context. Strict and paranoid add certainty at the cost of more blocks. Strict blocks what cannot be verified. Paranoid also blocks categories that are usually safe but can be catastrophic. Standard does not add more parser heuristics to close these gaps because guessing cannot make a statically unresolved target safe. A new gap requires a strict or paranoid fail-closed fixture. The documentation therefore records standard mode’s residual-risk families. Individual capabilities remain separately settable, and any per-rule override can force a strict-tier rule on under standard. What no override can do is weaken a catastrophic rule or the always-on protections. See Modes for the levels and Analysis engine for exactly where each boundary falls.

Defense-in-depth, not a replacement

CC Safety Net does not claim to be a complete security solution. It is positioned as one layer in a defense-in-depth stack:
  • Permission deny rules offer quick, user-configurable blocks. CC Safety Net runs before the permission system, so it inspects every command regardless of how deny rules are configured.
  • OS-level sandboxing restricts filesystem and network access but does not understand whether an operation is destructive within those boundaries. A git reset --hard inside a sandboxed directory is technically safe from the sandbox’s perspective but still a footgun.
Use these layers together: deny rules for fast iteration, sandboxing for unknown threats and containment, and CC Safety Net for bypass-resistant protection against known-destructive patterns. See the protection-layers comparison.

Worktree relaxation

Linked git worktrees create a usability problem: a developer working in a worktree often wants to run git checkout -- . or git reset --hard to discard local changes in that worktree, but the default rules block these as local-discard operations. Rather than relax the rule generally, worktree mode lifts the block only for local discards, only inside a directory positively verified as a linked worktree, and only when nothing has redirected the git context. Verification is the load-bearing part of the design: a directory that merely looks like a worktree does not qualify, and if the check cannot complete the command stays blocked. The relaxation is narrow. Every remote-affecting operation, including force pushes, branch deletes, and stash drops, stays blocked. Local discards that could reach outside the disposable worktree also stay blocked. Analysis engine lists the exact conditions and non-relaxable cases. Modes explains how to turn the mode on.

Where to go next

The technical guides run from the user-facing lifecycle down to the reasoning behind the design. This page is step 5, the last one.
  • Back: Analysis engine, which shows the exact classification behavior these tradeoffs produce, and Architecture, which shows the guard order they justify.
  • Start over: How it works, which presents the same system at user depth.
Related: Security model for the trust boundaries these decisions protect, Known limitations for what they cannot do, and Configuration recovery for the ready and degraded contract referenced above.
Last modified on September 15, 2026