Docs Reference
CLI
The command-line tools the packages install, and what they print.
runback-verify checks an audit record without trusting Runback: it recomputes the hash chain and the signature on your machine and makes no network call. It exits 0, 1 or 2, so CI can tell a pass from a record nobody vouched for.
A genuine record signed with Ed25519. Every check holds, but the signer is a test key rather than Runback's published one, so the CLI answers UNVERIFIED.
The CLI's own output, word for word.
$ npx @runback/verify signed-genuine.json
runback.audit/v2 — UNVERIFIED
run_id rt-1
generated_at 2026-01-01T00:00:00.000Z
events 4
✓ schema — record declares a $schema this verifier knows
✓ summary — run_id and outcome match the signed events
✓ chain — event hashes form an unbroken SHA-256 chain
✓ digest — manifest.content_digest matches terminal chain hash
✓ cassette — oracle-stream digest matches manifest.replay.cassette_digest
~ signature — Ed25519 — signature is sound, but the signer is not Runback's published key (self-hosted deployments sign with their own)
Integrity holds, but the origin of this record is unproven.
The chain algorithm is public (see /spec), so a self-consistent record
can be authored by anyone. Only a signature verified against a key you
obtained independently shows it came from the agent it names.
spec https://runback.dev/specExit 2: the CLI says UNVERIFIED
Intact.
Intact, but not signed by a key you can check: unsigned, or signed by another key.
What the answers mean
The CLI answers in its own words. In the app and in these docs, a record is Intact, Signed or Changed:
| Exit code | The CLI says | The record is | Meaning |
|---|---|---|---|
| 0Exit 0 — Signed | VALID | Signed | Intact, and signed by Runback's published key. |
| 2Exit 2 — Intact | UNVERIFIED | Intact | Intact, but not signed by a key you can check: unsigned, or signed by another key. |
| 1Exit 1 — Changed | INVALID | Changed | A check failed: the record is not what was signed, or could not be read. |
Commands
| Command | Package | Version | Run it |
|---|---|---|---|
runback-verify | @runback/verify npm | 3.0.0 | npx @runback/verify |
runback-verify | runback-verify PyPI | 2.1.0 | pip install runback-verify |
The PyPI package is a port of the npm one, held to the same conformance tests; where they differ, npm is the reference. Each numbers its own versions.
runback-verify --help
The CLI's own help text, word for word. Its exit codes map to record states as in the table above.
runback-verify <record.json> [--key <signing-key>]
Verify the integrity of a Runback audit record: runback.audit/v2, or the
older runback.cassette/v1.
<record.json> Path to the audit record JSON file (or - / --stdin for stdin)
--key <key> HMAC signing key, for legacy HMAC-signed records only.
Ed25519-signed records need no key — they carry their
own public key and verify fully offline.
--json Print the raw result object instead of the report.
AI narrative rows (GET /api/runs/:run_id/narrative) and external
security-finding rows (GET /api/runs/:run_id/security-findings) are
auto-detected from their shape and verified as chain + payload + signature:
--current-digest <digest> Narratives only. The run's current cassette
digest (from its own audit export) — confirms
the narrative hasn't been left describing a
since-altered version of the run.
Exit codes:
0 VALID — integrity holds and the signer is Runback's published key
2 UNVERIFIED — record is self-consistent, but its origin is unproven
(unsigned, or signed by a key that cannot be pinned)
1 INVALID — a check failed, or an error occurred
A record can be perfectly self-consistent and still be something a stranger
wrote: the chain algorithm is published. Exit 2 exists so CI can tell the
difference instead of treating "nobody vouched for this" as a pass.
Spec: https://runback.dev/spec
Online: https://runback.dev/verify