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.
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. Letters, numbers, hyphens, and 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 includes the rule name: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 |
