policy.json is CC Safety Net’s user-scoped settings file. It selects your safety preset, turns individual built-in protections on or off, adds extra protected paths, and sets how long audit records are kept.
It is separate from rule.json and rulebooks, which define your own custom blocking rules — see Custom rules for that schema.
Where the file lives
With
CC_SAFETY_NET_HOME set, the file sits directly under that directory, as a sibling of rules/. See Environment for the override itself.
There is exactly one policy file. There is no project-scoped policy.json — the runtime reads that single path and nothing else, so a project cannot lower or raise your policy.
When the dashboard writes the file it creates the directory with 0700 and the file with 0600.
The policy file is protected from your agent
The canonical userpolicy.json is a protected path in every runtime state, ready or degraded. Policy-file protection runs before the configuration snapshot is loaded, so a broken config cannot weaken it. These operations hard-stop:
- Writes, edits, and patches to the file through any tool
- Shell commands naming the file as an operand
- Write redirections into the file
- Recursive
rmof its directory or any ancestor find … -deleteandfind … -exec rmreaching itmvwith the file, its directory, or an ancestor as the source
[, cat, file, grep, head, jq, less, ls, more, rg, sed, stat, tail, test, and wc — sed only when it is not editing in place with -i or --in-place. Read-only tools such as Grep and Glob are exempt entirely.
Editing the file
You have two supported options:- The dashboard.
cc-safety-net guigives you a form over the same document, writes it with the correct permissions, and can repair a file that no longer validates. It is the lower-risk option, and the only one that repairs a broken file for you. One caveat while the file has an error: the form renders the full defaults, not the valid values still in your file, and saving is refused until you repair. Repair itself keeps every recognized valid setting and discards only the invalid fields — but if you would rather not trust the round trip, fix the file by hand and check the result withstatus. - Direct JSON editing. Open the file in an editor and edit it by hand. The runtime picks up the change on the next tool call — there is nothing to restart.
degraded verdict means part of your file was rejected. npx cc-safety-net doctor names exactly which fields. The runtime never rewrites policy.json on its own, so an invalid file stays exactly as you left it until you fix it or use the dashboard repair action.
A complete policy file
Every field, with its default value:version is required. Every other field can be omitted, and an omitted field takes the default shown above. If the file does not exist at all, CC Safety Net runs on these defaults and stays ready.
The root object is strict: an unrecognized top-level key is an error, as is an unrecognized key inside safety, workflow, destructive_command_protection, secret_protection, or audit.
Schema reference
integer
required
Schema version. Must be
1. This is the only required field; the diagnostic for a missing or wrong value is version must be 1.string
default:"standard"
The safety preset. One of
"standard", "strict", or "paranoid". Each preset supplies inherited capability defaults: strict enables fail_closed; paranoid enables fail_closed, paranoid_rm, and paranoid_interpreters. See Modes for what each capability changes.boolean
Set the fail-closed capability explicitly, up or down, regardless of the preset. Omit the key to inherit from the preset.
boolean
Set the paranoid
rm capability explicitly, up or down. Omit the key to inherit from the preset.boolean
Set the paranoid interpreter capability explicitly, up or down. Omit the key to inherit from the preset.
boolean
default:"false"
Relax local-discard git rules inside a confirmed linked worktree. Detection is fail-closed: if the working directory cannot be positively identified as a linked worktree, the stricter default rules stay in effect. See Modes for the exact list of what is relaxed and what is never relaxed.
boolean
default:"true"
Master switch for the registered destructive-command rules. Setting it to
false short-circuits every registered rule — except the catastrophic ones, which are always enforced.object
default:"{}"
Per-rule state, keyed by registered destructive-command rule id, with the value
"on" or "off". Applied on top of the capability-derived state, so "on" can enable a rule your preset leaves off and "off" can disable one it turns on.string[]
default:"[]"
Paths exempted from destructive-command rules. Entries must be absolute or start with
~/.boolean
default:"true"
Master switch for secret protection. Setting it to
false skips the entire secret stage, including your deny_paths.object
default:"{}"
Per-rule state, keyed by registered secret-protection rule id, with the value
"on" or "off". Most secret rules are on whenever secret protection is enabled, so "off" is the usual direction. The Coding CLI config tier is off by default, and an explicit "on" is how you opt into one of those rules.string[]
default:"[]"
Extra paths to protect as secrets, in addition to the built-in sensitive paths. See Deny paths for the validation rules.
integer
default:"30"
Days of audit history to keep before the sweep deletes it. Must be an integer between
1 and 365.Safety preset and capability overrides
safety.level picks a preset; safety.overrides then sets individual capabilities explicitly. This is the only place a capability can be turned down — environment flags can only raise.
paranoid preset but leaves interpreter one-liners alone. The reported effective level becomes custom whenever the resulting combination of capabilities matches no preset.
The environment can raise your policy’s level and force capabilities on, but never the reverse. See Environment for the full ordering between
policy.json and the environment, including the worktree_mode OR and the legacy SAFETY_NET_* aliases.Destructive-command protection
destructive_command_protection.overrides addresses built-in rules by id, for example:
unknown destructive command rule id "<id>", and any value other than "on" or "off" is rejected with destructive_command_protection.overrides.<id> must be "on" or "off".
Catastrophic rules are always enforced and are not user-configurable. They ignore enabled: false and ignore an "off" override. These are the rules covering removal of / or your home directory and deletion of Git metadata, together with their PowerShell and find equivalents. See Blocked commands for the behavior each rule enforces.
Allow paths
destructive_command_protection.allow_paths exempts specific locations from destructive-command rules. Validation is stricter than for deny paths:
Secret protection
Secret protection blocks reads and writes of credential-bearing files. This section is the configuration contract; the full catalog of built-in rules — every id, the paths each protects, and the exemptions — is the Secret protection reference.secret_protection.overrides addresses individual built-in rules by id, with the value "on" or "off". "off" disables a rule that is on by default; "on" opts into a rule from the default-off tier:
unknown secret protection rule id "<id>", and any other value with secret_protection.overrides.<id> must be "on" or "off".
Rules that are off by default
Most built-in secret rules are on whenever secret protection is enabled. One tier is not: the Coding CLI config rules, which cover the settings and MCP configuration files of supported coding agents. Those files can carry credentials inline, but agents also edit them as routine work, so the tier ships off and you opt in per rule with an explicit"on" override. A config rule you turn on protects the agent’s user-level config files, and it also protects project-level files matched by name at any repository root, such as any .mcp.json.
The ten default-off ids, their on-by-default Coding CLI credential counterparts, and the exact paths each rule protects are listed in the Secret protection reference.
Deny paths
secret_protection.deny_paths adds your own protected locations on top of the built-in sensitive paths. Deny paths are checked first, before the built-in rules, and a hit is a hard stop attributed to the rule id secret.deny-path.
Validation:
Relative entries are accepted precisely because they resolve against each session’s working directory, which is unknowable when the file is saved. The rejected class — home, anything above home, and
/ — has no legitimate reading and would block essentially every command in every workspace under home.
What a valid deny path protects: the path itself and every descendant. A target is normalized against the execution working directory and each configured path against the config working directory before comparison.
Two limits worth knowing:
- Deny paths only apply while
secret_protection.enabledistrue. Setting it tofalseturns them off along with everything else in the secret stage. secret.deny-pathis not a registered secret rule id, sosecret_protection.overridescannot disable it. Onlysecret_protection.enabled: falsedoes.
Audit retention
audit.retention_days controls how long audit records survive before the retention sweep deletes them. The default is 30 days, and the accepted range is 1 to 365.
Retention resolves independently of the rest of the policy. The sweep reads this one field directly from the file, so a policy that fails validation elsewhere still prunes. A value that is missing, non-integer, or unusable falls back to 30; a value below
1 is clamped to 1, and above 365 to 365.Out-of-range values therefore do two things at once: the schema rejects them, degrading the runtime, while the sweep clamps them. "retention_days": 1000 both shows up as a diagnostic and prunes at 365 days.When the file is invalid
An invalidpolicy.json never blocks ordinary work. It moves the runtime to degraded and falls back:
- Readable but invalid — the file is salvaged field by field. Every recognized valid section stays active and protective defaults replace only the rest.
- Empty, unparseable, or not a JSON object — built-in protective defaults are used for the whole file.
- Missing entirely — built-in defaults are used, with no diagnostic. The runtime stays
ready.
Configuration recovery is the complete contract for degraded state, including how it is reported and how to get back to
ready.
Related pages
Modes
What each safety capability changes, and what worktree mode relaxes.
Environment
Every variable, including the ones that raise your policy’s level.
Custom rules
The separate
rule.json and rulebook schemas for your own blocking rules.Configuration recovery
Ready versus degraded, the fallback matrix, and the repair sequence.