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 in response to a real incident — an AI coding agent deleting an entire home directory. Every design choice flows from one goal: stop destructive commands before an agent executes them, and never create 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 canonical policy file 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 exactly what a compromised agent would try to edit first. By denying before the policy file is read, these two guards carry no config state, cannot be relaxed by a preset or an override, and cannot be switched off by the very file they protect. The cost is that their denials cannot report a safety level or a fallback reason, because neither is known yet — a deliberate trade of 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 as the default

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. A rejected configuration source is dropped, not converted into a denial — turning a typo in a rulebook into a machine that blocks all work would be a denial-of-service on the user, and it would push people to uninstall the tool rather than fix the file. Configuration recovery owns 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 is delivered to the agent as an ordinary tool result, inside the loop the agent is already running, and that framing drives how every block message is written. An agent that receives a bare “permission denied” tends toward one of two failure modes: it retries near-variants of the blocked command until something slips through, or it declares the whole task failed because one step was refused. Both are worse than the block itself — the first is probing behavior that burns turns and might eventually find a real gap, the second turns a safety intervention into a work stoppage. Each message is therefore built to hand the agent a productive next move. The reason states in plain language what the command would have done and, where one exists, names the safer alternative. And every rule carries an intent that selects the closing instruction: report the block and continue with the rest of 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 brute-forcing variants. Even the fail-closed denial for an internal error carries an intent — restructure, don’t retry — so an unexpected failure of the tool itself still routes the agent toward 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 keeps its runtime dependency surface to a single lazily loaded package, and everything structural — segment splitting, quoting, redirection, command substitution, dynamic-word provenance — comes from its own bounded POSIX and PowerShell parsers rather than a third-party grammar. That is a deliberate build-versus-buy decision:
  • 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 — which words came from an expansion, which target is anchored to the working directory, which quoting form was used — so an owned parser is the only way to make 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 how each dependency is consumed.

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 — rulebooks can be fetched from GitHub repositories and pinned with SHA-256 digests in a lockfile, so teams share blocking policy without copy-pasting JSON.
  • Integrity — remote rulebook content is verified against the lockfile digest before use. Inline config had no integrity story.
  • Scoping — rulebooks support separate user (global) and project scopes with distinct config directories.
  • Validation — rulebook content is schema-validated before it can influence 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 is shipped as three presets — standard, strict, and paranoid — rather than a single on/off switch, because the honest answer to “should an unverifiable command be blocked?” depends on where the command came from. 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 buy certainty with friction. Strict blocks what cannot be verified; paranoid additionally blocks categories that are usually fine but occasionally catastrophic. The design deliberately does not try to close standard’s gaps by adding more standard-mode parser heuristics — a target that cannot be resolved statically cannot be made safe by guessing harder. The prescribed response to a newly found gap is a strict or paranoid fail-closed fixture, which is why the residual-risk families for standard mode are recorded rather than hidden. 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 all three 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 CC Safety Net vs. sandboxing.

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 deliberately narrow — every remote-affecting operation (force pushes, branch deletes, stash drops) stays blocked, as do local discards that could reach outside the disposable worktree. The exact conditions and the non-relaxable cases are in Analysis engine; how to turn the mode on is in Modes.

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 — the exact classification behavior these tradeoffs produce, and Architecture for the guard order they justify.
  • Start over: How it works — 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 August 5, 2026