Building needs Rust 1.88 or newer. Two of the three heads shell out to binaries you have to install yourself:
| Head | Needs | Where to get it |
|---|---|---|
risk |
tirith |
sheeki03/tirith |
policy |
cupcake and opa |
eqtylab/cupcake, openpolicyagent.org |
judgement |
nothing beyond cerberus | n/a |
If a head is enabled but its binary is missing, health marks the guard
degraded at the next SessionStart and gate then denies every guarded
tool until it's fixed. That's deliberate: under bypassPermissions these
heads are the only thing between the agent and your machine, so a guard that
has quietly stopped working is worse than no guard. The read-only tools are
outside the matcher and keep working, so the agent can still read enough to
explain what broke. Install the binaries, or turn the head off in
config.toml.
git clone https://github.com/ahokinson/cerberus
cd cerberus
cargo install --path crates/cerberus
cerberus initcerberus init bootstraps everything the guard needs and is safe to
re-run:
- writes the shipped
rules/*.rhaiscripts into${XDG_CONFIG_HOME:-$HOME/.config}/cerberus/rules/ - seeds a default
config.tomlif one doesn't already exist, and never touches an existing one - creates a cupcake project (
cupcake init --harness claude) at${XDG_DATA_HOME:-$HOME/.local/share}/cerberus/cupcake/ifcupcakeis on$PATHand it doesn't already exist - bootstraps cerberus's own cupcake store at
${XDG_CONFIG_HOME:-$HOME/.config}/cerberus/cupcake/if it doesn't already exist, and writes the shippedpolicies/cupcake/*.regointo itspolicies/claude/cerberus/directory — always refreshed. cerberus never reads or writes your own~/.config/cupcake. Both locations are passed tocupcake evalexplicitly (--policy-dir,--global-config), so nothing depends on the working directory or an inheritedXDG_CONFIG_HOME - creates
${XDG_CONFIG_HOME:-$HOME/.config}/cerberus/tirith/for your own tirith rule fragments, and never writes into it - composes the tirith overlay — the shipped
policies/tirith/policy.yamlbase plus your fragments plus every configured source's — into a cerberus-owned tirith policy root, always refreshed - removes the directories earlier versions created at the top level of
$XDG_DATA_HOME(cupcake-stub/,cerberus-tirith-overlay/,cupcake-global-init-home/), reporting each by name - wires the hooks into
~/.claude/settings.json, idempotently. cerberus owns no hook slot. The only hooks it will ever add, move, or remove are ones runningcerberus guardorcerberus health; it puts each at the front of its event so the guard runs first, and leaves every other hook exactly where it found it — including one that happens to share a matcher - wires the same hooks into
~/.codex/hooks.jsonifcodexis on$PATH, with the identical remove-then-prepend idempotency guarantee, and sets[features] codex_hooks = truein~/.codex/config.toml(Codex's hooks are silent no-ops without it), preserving every other key already there - wires
cerberus guardinto~/.cursor/hooks.json'sbeforeShellExecutionandbeforeMCPExecutionevents if a~/.cursordirectory exists. Cursor's own hooks are additive across scope layers, so this only ever adds cerberus's entry, never touching anyone else's - writes the shipped
templates/hermes/plugin into~/.hermes/plugins/cerberus/ifhermesis on$PATH, always refreshed - writes the shipped
templates/opencode/cerberus-guard.tsplugin into~/.config/opencode/plugin/ifopencodeis on$PATH, always refreshed
It finishes with a per-head summary of what's ready: tirith on $PATH and
whether the overlay was written and how many rules composed into it, the
cupcake project/store and opa and how many of cerberus's own policies are
installed, and the rule script count. Anything it couldn't do is reported and the exit code is non-zero,
but it doesn't error out. That matches how the heads themselves behave.