cc-safety-net
A coding agent CLI hook - block destructive commands and secret file access
Package details
Install cc-safety-net from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:cc-safety-net- Package
cc-safety-net- Version
2.0.1- Published
- Aug 10, 2026
- Downloads
- 7,753/mo · 1,935/wk
- Author
- kenryu
- License
- MIT
- Types
- extension
- Size
- 1.6 MB
- Dependencies
- 1 dependency · 1 peer
Pi manifest JSON
{
"extensions": [
"./dist/pi/index.js"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
CC Safety Net — short for Coding CLI Safety Net — is a PreToolUse hook that blocks destructive commands and access to secrets like SSH keys and .env files, before AI coding agents run them. It parses command semantics, so flag reordering, shell wrappers, and interpreter one-liners can't bypass it.
[!NOTE] Full documentation → — installation, configuration, reference, guides, and the security model live on the docs site. This README is the short version.
Why this exists
We learned the hard way that instructions aren't enough to keep AI agents in check. After an agent silently wiped hours of progress with a single rm -rf ~/ or git checkout --, it became clear that soft rules in a CLAUDE.md or AGENTS.md file cannot replace hard technical constraints. CC Safety Net is that constraint: it observes relevant tool calls and blocks destructive commands and secret access before they reach the shell. See What Is CC Safety Net for the full background.
What's new in v2.0.0
[!TIP] Already running v1? One command upgrades every installed integration to v2:
npx -y cc-safety-net@latest update. If you defined custom rules under v1, also read Upgrading from an older version.
- Rebuilt evaluation engine — canonical command IR, deeply immutable policy snapshots, and an ordered guard pipeline with intrinsic decision tracing behind
explain. - Secret protection — built-in rules block content access to SSH keys,
.envfiles, cloud credentials, and coding-CLI credential stores, across shell commands and file tools alike. - Always-on catastrophic protections — recursive deletion of root or home, Git metadata mutation (
.gitcontrol plane, hooks, worktrees, submodules), and mutation of the user policy file are blocked in every mode, regardless of overrides. - Safety presets —
standard/strict/paranoidlevels with per-rule overrides, trusted delete allow-paths, and env vars that can only raise protection. - Policy GUI —
cc-safety-net guiserves a local, token-authenticated editor with live preset preview. - Universal installer — interactive
install/uninstallacross all twelve supported agent CLIs, with anupdatecommand for installed integrations. - Command-decision audit trail — allowed and blocked command decisions recorded by default to local per-project JSONL with secret redaction, retained for 30 days by default, browsable via
cc-safety-net logs. - Documented threat model — the SECURITY.md mode contract, explicit resource limits, and a residual-risk registry of adjudicated bypass families.
Quick start
Prerequisite: Node.js 18 or higher.
Run the interactive selector to install CC Safety Net into one or more installed coding CLIs:
npx -y cc-safety-net@latest install
To update every installed integration:
npx -y cc-safety-net@latest update
The @latest qualifier matters: a bare cc-safety-net spec can re-run an older
cached copy from the npx cache instead of the current release.
To remove integrations interactively:
npx -y cc-safety-net uninstall
If you use the CLI often, install it globally to get ccsn, a shorter alias for the same commands:
npm install -g cc-safety-net
ccsn doctor
CC Safety Net works across twelve coding agent CLIs — Claude Code, Antigravity CLI, Codex, Gemini CLI, GitHub Copilot CLI, Kimi Code, OpenCode, Pi, Cursor, Amp Code, Hermes Agent, and OpenClaw — on Windows, macOS, and Linux (the Hermes Agent and OpenClaw integrations are macOS and Linux only; see Hermes Agent and OpenClaw). Per-agent instructions, the target flags for scripted non-interactive installs, post-install steps, and per-agent caveats are on Installation; how each integration hooks its agent is on Integration Architecture.
Hermes Agent and OpenClaw
These two integrations are newer than the docs site, so their setup steps and coverage boundaries live here.
# Hermes Agent — installs a managed Python plugin, then restart Hermes
npx -y cc-safety-net@latest install --hermes-agent
npx -y cc-safety-net@latest uninstall --hermes-agent
# OpenClaw — installs a bundled plugin through OpenClaw's own CLI, then restart the Gateway
npx -y cc-safety-net@latest install --openclaw
npx -y cc-safety-net@latest uninstall --openclaw
Run npx cc-safety-net doctor after either install: it is the only surface that reports a plugin
that is present but not enabled, not ours, or out of date. It reads disk and config only and never
asks a running host whether the plugin loaded, so a stopped OpenClaw Gateway is not reported as a
failure — the OpenClaw install verifies the load itself.
Hermes Agent. Install writes a managed plugin to $HERMES_HOME/plugins/cc-safety-net/
(~/.hermes when HERMES_HOME is unset) and then
runs hermes plugins enable cc-safety-net --no-allow-tool-override. It is an ordinary Hermes user
plugin: it runs inside the Hermes process with the trust Hermes grants user plugins, and it is
inert until Hermes is restarted. On every pre_tool_call it shells out to
npx -y cc-safety-net hook --hermes-agent with a 30-second timeout, and blocks the call itself
whenever that analysis cannot complete — missing npx, spawn failure, timeout, unreadable output,
or a Hermes session directory it cannot read.
Protected tools are terminal, read_file, write_file, and patch; calls to any other Hermes
tool are not forwarded and get no decision. Uninstall runs hermes plugins disable cc-safety-net
first — Hermes only resolves a plugin that is still on disk — and then removes the managed files,
so the config entry goes with them; a failing or missing hermes is reported as a warning and the
files are removed anyway.
OpenClaw. Install runs openclaw plugins install <bundled plugin dir> --force followed by
openclaw plugins enable cc-safety-net, then verifies the plugin actually loaded. Restart the
OpenClaw Gateway afterwards, and if plugins.allow is set in openclaw.json it must also list
cc-safety-net — until both are true the plugin never runs and nothing is blocked. The plugin
registers before_tool_call for the canonical exec tool only and returns a plain allow or
block; it never rewrites tool parameters. It resolves the agent workspace as the policy and
execution directory, and denies a call whose workdir resolves outside that workspace or whose
agent or workspace context cannot be established. Live end-to-end tests (bun run test:e2e:live,
run before a release) drive the real openclaw binary against a throwaway state directory: our
CLI installs the plugin, the host reports it loaded with the hook registered, and a real Gateway
agent turn puts a real exec call through it — a harmless command runs, git reset --hard is
blocked before it can touch the workspace.
What is not covered.
- Hermes bang-shell. A
!commandyou type yourself does not raisepre_tool_call; it goes straight to Hermes' own command guard. CC Safety Net sees model-generated tool calls only, not every subprocess Hermes starts. - Hermes shell hooks fail open. Hermes'
hooks:dispatcher allows the tool call when a hook cannot start, times out, or returns unparseable output. That is why the shipped integration is the Python plugin, which blocks on its own failures, and whyhooks:is a diagnostic path only. One case stays outside our reach either way: if Hermes never loads the plugin, nothing blocks and onlydoctorwill say so. - OpenClaw execution hosts.
execcalls with nohost,host: "auto", orhost: "gateway"are analysed as local Gateway calls. Explicithost: "sandbox"andhost: "node"are blocked as unsupported, because no test proves a correct path mapping for them. When a sandbox runtime is active, anautocall runs in the sandbox filesystem while paths are still analysed against the Gateway workspace — command rules are unaffected, path rules can be evaluated against the wrong filesystem. The same applies when a call carries nohostand your configuredtools.exec.hostdefault isnodeorsandbox: the call is still analysed as a local Gateway call. - OpenClaw Codex-native relay. Untested, and therefore unclaimed. The live end-to-end tests drive OpenClaw's own agent runtime, so they say nothing about a Codex-native shell, patch, or MCP call.
- OpenClaw file tools.
apply_patchand OpenClaw's read/write/edit tools are not protected; onlyexecis. - Windows. Both integrations assume the POSIX layout. Relocated state is handled the way the
hosts resolve it —
HERMES_HOMEfor Hermes, thenOPENCLAW_STATE_DIRand the directory ofOPENCLAW_CONFIG_PATHfor OpenClaw, falling back to~/.hermesand~/.openclaw. The Windows defaults are not: install and detection target the POSIX path there, so Hermes writes to the wrong directory and OpenClaw's own CLI installs correctly whiledoctormisreports the state.
What it does
| Capability | What it catches |
|---|---|
| Semantic command analysis | rm -rf on destructive targets, git reset --hard, git checkout --, git push --force, git stash clear, git clean -f, unsafe find -delete, dd/mkfs/shred — by intent, not string pattern. git checkout -b feature (safe) is allowed while git checkout -- file (destructive) is blocked. |
| Shell wrapper detection | Destructive commands hidden in bash -c, sh -c, and similar wrappers, recursively analyzed up to 10 levels deep. |
| Interpreter one-liners | Destructive code in python -c, node -e, ruby -e, perl -e one-liners (e.g. os.system("rm -rf /")). |
| Fail-closed by default | Malformed hook input and unparseable commands (in strict mode) block rather than allow. Invalid config never blocks: an unverifiable rule source is dropped and an unreadable policy.json falls back to protective defaults, both with a warning on every reporting surface. |
| Secret protection | Content access to SSH keys, .env files, ~/.aws, kube/docker/gcloud configs, and coding-CLI credential stores — enforced on shell commands and file tools (read/edit/write/search) alike. |
| Custom rules via rulebooks | Add your own blocking rules at user or project scope, pinned by SHA-256 digest when fetched from GitHub. |
| Audit logging | Allowed and blocked command decisions written to local per-project JSONL with secrets auto-redacted, retained for 30 days by default. Browse them with npx cc-safety-net logs, or triage them in the Activity view of npx cc-safety-net gui. |
Full rule catalogs: Blocked Commands · Allowed Commands · Secret Protection.
Why not just use a sandbox?
A workspace-writable sandbox still permits git reset --hard, git push --force, and rm -rf . inside the project directory, because the OS only sees writes to an allowed path. Sandboxing contains blast radius; CC Safety Net catches the destructive operations sandboxing permits — use both for defense-in-depth. See vs Sandboxing.
Safety presets
Set a session safety preset with CC_SAFETY_NET_LEVEL=standard|strict|paranoid:
| Preset | Effect |
|---|---|
| Standard | Blocks recognizable destructive Git and filesystem commands. Allows metadata-only checks of built-in sensitive paths while continuing to block content access. Recommended for normal coding. |
| Strict | Standard, plus blocks dynamic or unparseable commands the analyzer cannot verify safely and metadata-only discovery of built-in sensitive paths. Occasional false positives on advanced shell. |
| Paranoid | Strict, plus blocks rm -rf inside your project and interpreter one-liners. Expect friction; for untrusted agents or high-stakes repos. |
Presets supply inherited defaults; policy.json stores only your explicit deviations — per-rule overrides, allow paths, deny paths, worktree mode, and audit retention. Environment variables can only raise protection, never lower it. The full contract is on Modes, Policy, and Environment, or edit everything visually with the local GUI.
Diagnostics and tracing
# Summarize what is being enforced right now
npx cc-safety-net status
# Verify your installation and run a self-test
npx cc-safety-net doctor
# Trace how a command is analyzed step-by-step
npx cc-safety-net explain "git reset --hard"
# Browse recorded denials from the audit trail (add --all to include allowed commands)
npx cc-safety-net logs
# Review what was blocked and edit your policy in a local web GUI
npx cc-safety-net gui
doctor, explain, and logs support --json for machine-readable output. The audit trail records command decisions only — never command output or prompts — and stays on your machine. Invalid configuration never blocks your agent: unverifiable rule sources are dropped and every degraded state is reported on the next block message, doctor, the status line, and the GUI banner.
Details: CLI Commands · Explain Trace · Audit Log · Dashboard · Configuration Recovery.
Limitations
CC Safety Net denies a tool call before it runs; it does not enforce filesystem permissions, inspect network egress, or contain a process. Two v2 bounds worth knowing up front: the policy and sensitive-path command extractors remain primarily POSIX-oriented, so native PowerShell path expressions such as Get-Content $HOME\.ssh\id_rsa can evade static path extraction; and policy-file protection is a best-effort exact-path guard, not command emulation. Use operating-system permissions, a sandbox, or equivalent runtime enforcement when complete protection is required.
The full residual-risk registry lives in SECURITY.md; the practical consequences are on Known Limitations.
Upgrading from an older version
Upgrade every installed integration to the current release with one command:
npx -y cc-safety-net@latest update
[!WARNING] If you previously defined custom rules in a legacy inline config (
.safety-net.jsonor~/.cc-safety-net/config.json), those files are no longer loaded at runtime and their rules are not enforcing anything. Nothing is blocked, so you will not notice this from normal use — the commands those rules used to block now run. Runnpx -y cc-safety-net rule migrateto convert them to the rulebook layout, thennpx -y cc-safety-net doctorto confirm the runtime isready. See the migration guide.
Full documentation
All details live on the docs site at ccsafetynet.com/docs:
| Area | Pages |
|---|---|
| Get started | Introduction · Installation · Quickstart · How It Works · Dashboard |
| Configuration | Modes · Policy · Environment · Custom Rules · Status Line · Configuration Recovery |
| Reference | Blocked Commands · Allowed Commands · Secret Protection · Audit Log · CLI Commands · Explain Trace · Glossary |
| Guides | Architecture · Analysis Engine · Design Principles · Security Model · vs Sandboxing · Integration Architecture · Known Limitations · Troubleshooting |
| Project | Contributing · Security Policy |
Development
See CONTRIBUTING.md for details on how to contribute to this project.
Generated distribution ownership is intentionally narrow. Only dist/index.js,
dist/index.d.ts, dist/bin/cc-safety-net.js, and dist/pi/index.js are tracked. Run
bun run verify:build, bun run verify:package, and bun run verify:repository-plugin when
changing packaging, integrations, or release automation.
License
MIT
