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.
integer
required
Schema version. Must be
1.array
List of rulebook source strings. Defaults to an empty array.
object
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.
integer
required
Rulebook schema version. Must be
1.string
required
Rulebook name. Must match the local directory name or GitHub source name.
string
required
Rulebook version string.
string
Human-readable description of the rulebook.
string
Rulebook author.
array
required
Commands this rulebook is allowed to define rules for.
array
required
Custom blocking rules (see Rule Schema below).
array
required
Rulebook fixtures (see Fixture Schema below). Every rule must have at least one blocked fixture.
Rule Schema
string
required
Unique within the rulebook. Must start with a letter, followed by letters, numbers, hyphens, or underscores; max 64 chars.
string
required
Base command to match. Must be listed in
allowed_commands.string
Subcommand to match (e.g.,
add, install). If omitted, matches any.array
required
Arguments that trigger the block (at least one required).
string
required
Message shown when blocked. Max 256 chars.
Fixture Schema
string
required
Shell command fixture.
string
required
Either
blocked or allowed.string
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.
Fail closed means commands stay blocked until legacy rules are migrated to the new layout.
rule migrate creates a rulebook-based layout automatically: