Skip to main content
Beyond built-in protections, CC Safety Net lets you define custom blocking rules to enforce team conventions or project-specific safety policies. Rules live in a rulebook-based layout and are merged from user and project scopes, so you can maintain personal defaults alongside per-project overrides without duplication.
Breaking change — Legacy inline config files (.safety-net.json and ~/.cc-safety-net/config.json) are no longer loaded at runtime. If they contain rules, commands now fail closed (stay blocked) until you migrate. Run npx -y cc-safety-net rule migrate to convert legacy rules into the new rulebook layout. See Migration below.
The fastest way to author custom rules is to use the /cc-safety-net skill inside your agent to create rules interactively with natural language. Examples:
/cc-safety-net read my package.json and suggest blocking rules
/cc-safety-net set up rules to block all terraform destroy commands
/cc-safety-net verify my rules and fix any errors
If your agent does not support skills, prompt it with:
run npx -y cc-safety-net rule doc and help me set up custom rules

Config File Locations

CC Safety Net loads rulebooks from two scopes and merges them:
  1. User scope~/.cc-safety-net/rules/rule.json (created with rule init --global). Use this for personal defaults that apply to every project.
  2. Project scope.cc-safety-net/rules/rule.json in the project root. Use this for team or project-specific rules you can commit to source control.
Local rulebook sources are referenced by bare names like 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.
If no config is found in either location, only built-in rules apply.

Quick Start

Create a starter project rule config and rulebook:
npx -y cc-safety-net rule init
This creates .cc-safety-net/rules/rule.json:
{
  "version": 1,
  "rules": ["project-rules"],
  "overrides": {}
}
Rule definitions live in .cc-safety-net/rules/project-rules/rulebook.json:
{
  "rulebook_version": 1,
  "name": "project-rules",
  "version": "1.0.0",
  "description": "Project-specific CC Safety Net rules.",
  "author": "project",
  "allowed_commands": ["git"],
  "rules": [
    {
      "name": "block-git-add-all",
      "command": "git",
      "subcommand": "add",
      "block_args": ["-A", "--all", "."],
      "reason": "Use 'git add <specific-files>' instead of blanket add."
    }
  ],
  "tests": [
    {
      "command": "git add -A",
      "expect": "blocked",
      "rule": "block-git-add-all"
    },
    {
      "command": "git add README.md",
      "expect": "allowed"
    }
  ]
}
After editing rulebooks, run:
npx -y cc-safety-net rule sync
npx -y cc-safety-net rule verify
npx -y cc-safety-net rule test
Now git add -A, git add --all, and git add . will be blocked with your custom message.

Config Schema

The top-level rule.json selects which rulebooks are active and applies overrides.
version
integer
required
Schema version. Must be 1.
rules
array
List of rulebook source strings. Defaults to an empty array.
overrides
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 own rulebook.json file.
rulebook_version
integer
required
Rulebook schema version. Must be 1.
name
string
required
Rulebook name. Must match the local directory name or GitHub source name.
version
string
required
Rulebook version string.
description
string
Human-readable description of the rulebook.
author
string
Rulebook author.
allowed_commands
array
required
Commands this rulebook is allowed to define rules for.
rules
array
required
Custom blocking rules (see Rule Schema below).
tests
array
required
Rulebook fixtures (see Fixture Schema below). Every rule must have at least one blocked fixture.

Rule Schema

name
string
required
Unique within the rulebook. Letters, numbers, hyphens, and underscores; max 64 chars.
command
string
required
Base command to match. Must be listed in allowed_commands.
subcommand
string
Subcommand to match (e.g., add, install). If omitted, matches any.
block_args
array
required
Arguments that trigger the block (at least one required).
reason
string
required
Message shown when blocked. Max 256 chars.

Fixture Schema

command
string
required
Shell command fixture.
expect
string
required
Either blocked or allowed.
rule
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/npm matches 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 is add.
  • Argument matching: Arguments in block_args are matched literally. No regex or glob support.
  • Short option expansion: Bundled short flags are unbundled before matching. -Ap is treated as -A and -p.
  • Long option matching: Long options use exact string matching. --all-files does not match --all.
  • ANY semantics: A command is blocked if any single argument in block_args is 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

Prevent the agent from installing packages globally:
{
  "rulebook_version": 1,
  "name": "project-rules",
  "version": "1.0.0",
  "allowed_commands": ["npm"],
  "rules": [
    {
      "name": "block-npm-global",
      "command": "npm",
      "subcommand": "install",
      "block_args": ["-g", "--global"],
      "reason": "Global npm installs can cause version conflicts. Use npx or local install."
    }
  ],
  "tests": [
    {
      "command": "npm install -g typescript",
      "expect": "blocked",
      "rule": "block-npm-global"
    },
    {
      "command": "npm install typescript",
      "expect": "allowed"
    }
  ]
}
Block docker system prune:
{
  "rulebook_version": 1,
  "name": "project-rules",
  "version": "1.0.0",
  "allowed_commands": ["docker"],
  "rules": [
    {
      "name": "block-docker-system-prune",
      "command": "docker",
      "subcommand": "system",
      "block_args": ["prune"],
      "reason": "docker system prune removes all unused data. Use targeted cleanup instead."
    }
  ],
  "tests": [
    {
      "command": "docker system prune",
      "expect": "blocked",
      "rule": "block-docker-system-prune"
    },
    {
      "command": "docker ps",
      "expect": "allowed"
    }
  ]
}
{
  "rulebook_version": 1,
  "name": "project-rules",
  "version": "1.0.0",
  "allowed_commands": ["git", "npm"],
  "rules": [
    {
      "name": "block-git-add-all",
      "command": "git",
      "subcommand": "add",
      "block_args": ["-A", "--all", ".", "-u", "--update"],
      "reason": "Use 'git add <specific-files>' instead of blanket add."
    },
    {
      "name": "block-npm-global",
      "command": "npm",
      "subcommand": "install",
      "block_args": ["-g", "--global"],
      "reason": "Use npx or local install instead of global."
    }
  ],
  "tests": [
    {
      "command": "git add -A",
      "expect": "blocked",
      "rule": "block-git-add-all"
    },
    {
      "command": "npm install -g typescript",
      "expect": "blocked",
      "rule": "block-npm-global"
    }
  ]
}

Block Output Format

When a custom rule blocks a command, the output includes the rule name:
BLOCKED by CC Safety Net

Reason: [block-git-add-all] Use 'git add <specific-files>' instead of blanket add.

Command: git add -A

Validating Your Rulebooks

After creating or editing rulebooks, validate them with:
npx -y cc-safety-net rule sync
npx -y cc-safety-net rule verify
npx -y cc-safety-net rule test
  • rule sync rebuilds the lock/cache for configured rulebook sources.
  • rule verify validates rulebook structure and rule definitions.
  • rule test runs 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 stateNew behavior
Empty legacy fileSilently ignored — built-in rules only
Legacy file with rulesFail closed until migrated with rule migrate
Invalid legacy fileFail closed until fixed and migrated, or removed
Fail closed means commands stay blocked until legacy rules are migrated to the new layout.
# Convert legacy inline rules into the rulebook layout
npx -y cc-safety-net rule migrate

# Optionally delete verified legacy files after migration
npx -y cc-safety-net rule migrate --cleanup

# Validate the migrated rules
npx -y cc-safety-net rule verify
npx -y cc-safety-net rule test
Before — single inline config with rules embedded:
.safety-net.json                # project rules (inline)
~/.cc-safety-net/config.json    # user rules (inline)
Afterrule migrate creates a rulebook-based layout automatically:
.cc-safety-net/rules/rule.json                    # project rulebook sources + overrides
.cc-safety-net/rules/project-rules/rulebook.json  # migrated project rules
~/.cc-safety-net/rules/rule.json                  # user rulebook sources + overrides
~/.cc-safety-net/rules/user-rules/rulebook.json   # migrated user rules

Error Handling

Rulebook-backed custom rules fail closed when configured rulebooks cannot be loaded safely:
ScenarioBehavior
Config file not foundSilent — use built-in rules only
Invalid rule configFail closed until fixed
Empty legacy configSilent — use built-in rules only
Legacy config with rules and no migrated rule configFail closed until rule migrate creates the new rule config
Invalid legacy configFail closed until fixed or removed
Missing or stale lock/cacheFail closed until rule sync repairs it
Invalid local rulebookFail closed until the rulebook is fixed and synced
Invalid GitHub rulebookFail closed until the source is fixed or removed
If you add or modify custom rules manually, always validate them with npx -y cc-safety-net rule verify and npx -y cc-safety-net rule test.