Config File Locations
CC Safety Net loads rulebooks from two scopes and merges them:- User scope —
~/.cc-safety-net/rules/rule.json(created withrule init --global). Use this for personal defaults that apply to every project. - Project scope —
.cc-safety-net/rules/rule.jsonin the project root. Use this for team or project-specific rules you can commit to source control.
project-rules. GitHub rulebook sources use owner/repo#ref/<rulebook-name> and point to .cc-safety-net/rules/<rulebook-name>/rulebook.json in that repository.
Merging behavior:
- Rulebooks from both scopes are combined.
- Duplicate active rulebook names are invalid.
- Project overrides win over user overrides for the same
<rulebook-name>/<rule-name>key.
Managing rulebook sources
A rulebook source is referenced by an entry inrule.json’s rules array. There are two kinds:
- Local source — a bare name like
project-rules. The rulebook lives at.cc-safety-net/rules/project-rules/rulebook.json(project) or~/.cc-safety-net/rules/project-rules/rulebook.json(user). Local sources must stay within their config directory. - GitHub source —
owner/repo#ref/<rulebook-name>, pointing at.cc-safety-net/rules/<rulebook-name>/rulebook.jsonin that repository and ref.
rule command to add, update, and remove sources rather than editing rule.json and the lockfile by hand:
--global (-g) to operate on the user scope instead of the project scope.
Lock and cache
Every configured source is pinned in arule.lock file by a SHA-256 digest, and its rulebook is cached under .cc-safety-net/cache/rulebooks/. At runtime CC Safety Net verifies the cached rulebook against the digest. A missing or stale lock/cache, a digest mismatch, or a local source that drifts from its pinned digest causes commands to fail closed until you run rule sync to repair it.
Quick Start
Create a starter project rule config and rulebook:.cc-safety-net/rules/rule.json:
.cc-safety-net/rules/project-rules/rulebook.json:
git add -A, git add --all, and git add . will be blocked with your custom message.
Config Schema
The top-levelrule.json selects which rulebooks are active and applies overrides.
Schema version. Must be
1.List of rulebook source strings. Defaults to an empty array.
Rule overrides keyed by
<rulebook-name>/<rule-name>. Values are either "off" to disable a rule or { "reason": "..." } to replace the rule reason.Rulebook Schema
Each rulebook lives in its ownrulebook.json file.
Rulebook schema version. Must be
1.Rulebook name. Must match the local directory name or GitHub source name.
Rulebook version string.
Human-readable description of the rulebook.
Rulebook author.
Commands this rulebook is allowed to define rules for.
Custom blocking rules (see Rule Schema below).
Rulebook fixtures (see Fixture Schema below). Every rule must have at least one blocked fixture.
Rule Schema
Unique within the rulebook. Must start with a letter, followed by letters, numbers, hyphens, or underscores; max 64 chars.
Base command to match. Must be listed in
allowed_commands.Subcommand to match (e.g.,
add, install). If omitted, matches any.Arguments that trigger the block (at least one required).
Message shown when blocked. Max 256 chars.
Fixture Schema
Shell command fixture.
Either
blocked or allowed.Rule expected to block the command. Required for blocked fixtures.
Matching Behavior
Understanding how CC Safety Net matches commands against your rules helps you write precise, effective rules:- Command normalization: Commands are reduced to their basename before matching.
/usr/local/bin/npmmatches a rule with"command": "npm". - Subcommand detection: The subcommand is the first non-option argument following the command. In
git --no-pager add -A, the subcommand isadd. - Argument matching: Arguments in
block_argsare matched literally. No regex or glob support. - Short option expansion: Bundled short flags are unbundled before matching.
-Apis treated as-Aand-p. - Long option matching: Long options use exact string matching.
--all-filesdoes not match--all. - ANY semantics: A command is blocked if any single argument in
block_argsis present. - Additive only: Custom rules can only add new restrictions. They cannot bypass built-in protections.
Known limitation:
-Cfoo is treated as -C -f -o -o, not -C foo. Blocking -f may false-positive on attached option values.Examples
Block global npm installs
Block global npm installs
Prevent the agent from installing packages globally:
Block dangerous docker commands
Block dangerous docker commands
Block
docker system prune:Multiple rules in one rulebook
Multiple rules in one rulebook
Block Output Format
When a custom rule blocks a command, the output is prefixed with the rulebook name and the rule name, so you can tell which rulebook produced the block:<rulebook-name>/<rule-name>. This is also the key you use in rule.json overrides to disable a rule ("off") or replace its reason.
Validating Your Rulebooks
After creating or editing rulebooks, validate them with:rule syncrebuilds the lock/cache for configured rulebook sources.rule verifyvalidates rulebook structure and rule definitions.rule testruns every fixture in every active rulebook.
Migration from Legacy Config
Legacy inline config files (.safety-net.json and ~/.cc-safety-net/config.json) are no longer loaded at runtime.
| Legacy file state | New behavior |
|---|---|
| Empty legacy file | Silently ignored — built-in rules only |
| Legacy file with rules | Fail closed until migrated with rule migrate |
| Invalid legacy file | Fail closed until fixed and migrated, or removed |
rule migrate creates a rulebook-based layout automatically:
Error Handling
Rulebook-backed custom rules fail closed when configured rulebooks cannot be loaded safely:| Scenario | Behavior |
|---|---|
| Config file not found | Silent — use built-in rules only |
| Invalid rule config | Fail closed until fixed |
| Empty legacy config | Silent — use built-in rules only |
| Legacy config with rules and no migrated rule config | Fail closed until rule migrate creates the new rule config |
| Invalid legacy config | Fail closed until fixed or removed |
| Missing or stale lock/cache | Fail closed until rule sync repairs it |
| Invalid local rulebook | Fail closed until the rulebook is fixed and synced |
| Invalid GitHub rulebook | Fail closed until the source is fixed or removed |