cerveau docs site github
How it works

Safety guard

Go code at the dispatch point, not an instruction in a prompt. It reads the actual arguments of every tool call before anything reaches a shell, refuses what has no safe form, pauses what leaves the machine, and rewrites what can be made safe.

A safety floor, not a sandbox The guard catches the common destructive shapes. An obfuscated command can slip past it. The file tools are genuinely jailed to the workspace, symlinks included; real containment for bash — a Landlock jail — is on the roadmap. This is stated plainly because overclaiming here would be the worst kind of lie.

Where it sits

Every tool call passes through Guard.Check before execution, and through Guard.Remediate after that. Neither is reachable by the model: they are compiled into the core, and a call that fails Check never becomes a subprocess.

What the guard does with a tool call Every call is checked against its arguments. A catastrophic match is refused outright, a sensitive one waits for the user, and anything with a safe form is rewritten before it executes. CATASTROPHIC SENSITIVE HAS A SAFE FORM tool call from the model Check reads the arguments refused nobody can approve waits for you you can approve rewritten and disclosed executes NINE CATASTROPHIC PATTERNS · FOUR SENSITIVE · FOUR PATH RULES
The tier decides who may override it. Catastrophic means nobody, including you.

Two tiers, and the difference matters

Denials are typed. This is the design position, not an implementation detail:

catastrophic
No safe form exists. Refused outright, and nobody can approve it — not the model, not you.
sensitive
The effect leaves the machine, or touches something private. Paused, and you can approve it.

A denial reaches the model as [tier] blocked: reason — hint, so it learns what happened and what to do instead rather than retrying blindly.

Catastrophic rules

Nine patterns, matched against the command string. From internal/guard/guard.go.

MatchesReasonHint
:(){fork bombnever allowed
dd … of=/dev/dd writing to a devicenever allowed
dd … of=$VARdd writing to a shell-variable target (unverifiable — may be a device)never allowed — use an explicit path
mkfsfilesystem formatnever allowed
shutdown reboot poweroff haltsystem power operationnever allowed
git push --forceforce push rewrites remote historynever allowed — use a normal push
DROP DATABASE|TABLEDROP on a database objectnever allowed
> /dev/sd|nvme|hdraw write to a disk devicenever allowed
chmod 777 /chmod 777 on rootnever allowed
Unprovable is treated as unsafe The dd … of=$VAR rule is the one worth studying. The target is a variable, so nobody can prove where it points — it may be a file, it may be your disk. The guard refuses what it cannot verify rather than hoping. That principle is why it holds up.

Sensitive rules

MatchesReasonHint
git pushpush publishes to a remoteexternal side effect — needs user confirmation
npm|pip|cargo|gem publishpackage publishexternal side effect — needs user confirmation
curl|wget … | shpiping remote script into a shelldownload first, review, then run
ssh scp rsyncremote connectionremote operations need user confirmation

The curl | sh pattern also matches laundered forms — curl … | env sh, | sudo sh, | xargs sh — because matching only bare sh is a rule that looks right and does nothing. The ssh rule does not require an @: ssh 192.168.1.5 id runs a command on another host with no user in the target.

Path rules

The guard reads paths as well as commands. These apply to read, edit, write and grep, and to any path appearing in a bash command.

MatchesReasonHint
.envenvironment file may contain secretsuse config example files instead
.ssh/ id_rsa id_ed25519SSH materialnever touch SSH keys
*.pem *.key *.p12 *.pfxkey/certificate filenever touch key material
credential secret tokenpossible secrets fileverify the path is not a secrets store

The rm boundary

rm is not a pattern match, because rm -rf build/ inside your project is ordinary work and rm -rf / is not. rmViolation judges each target against the real workspace boundary:

Remediation

Not everything dangerous deserves a refusal. Where a safe form exists, the guard rewrites the call and discloses the rewrite. From internal/guard/hardrules.go:

mv A B
becomes cp -a A B && <verify> && rm -rf A — a copy that is checked before the original is deleted, so a move that dies halfway cannot lose files.
edit / write
an important file gets a timestamped backup — file.bak.20260824-141802 — before it is touched. If the backup fails, the edit is blocked.

The mv rewrite is deliberately conservative: it matches a single source and destination with no flags, globs, pipes or redirects. Anything fancier passes through untouched, on the reasoning that a half-understood rewrite is worse than none.

The workspace jail

File tools resolve every path against the workspace root, and lexical containment is not enough — a symlink inside the workspace can point outside it. internal/tools/fs.go resolves symlinks on the deepest existing ancestor and re-checks containment against the resolved path, which closes that escape:

path "…" resolves outside the workspace via a symlink

Recalled memory cannot give orders

Memory is a place text arrives from the outside. Anything recalled re-enters the conversation wrapped in a marked envelope, as text the agent read rather than instructions you gave:

<system-reminder>
## Recalled memory (auto, system-owned)
- [evt_004411] prefers tabs
</system-reminder>

Any closing tag inside a stored document is escaped before it goes in, so a note indexed months ago cannot end the envelope early and have the rest of itself obeyed. Prompt injection through your own memory is a real path; this closes it. See Skills for the envelope's other job.

Threat model

Defended
The common destructive command shapes. Secrets and key material reaching the file tools. Escape from the workspace by symlink. Replay of a captured request from a paired device. Injection through recalled memory.
Not defended
A deliberately obfuscated shell command. Anything bash does after the guard has passed it — there is no kernel-level containment yet. A malicious RFX pack you installed yourself.
Out of scope
An attacker with local access to your machine. The console is the root of trust for pairing, by design.