Quickstart
Record a real operation, watch a guardrail block one, and verify the evidence offline — in five minutes, with no account and no network.
By the end of this page you will have blocked a DELETE with no WHERE clause, exported the
evidence, and verified it with an offline verifier. No account, no Console, no network calls.
1. Install
Download the signed binary for your platform, or build from source — see
Installation. Both sealr (the recorder) and sealr-verify (the standalone
offline verifier) ship in every archive.
sealr version2. Initialize
sealr initLeaving --endpoint unset selects offline mode: nothing is sent anywhere, and no account exists
to send it to.
Initialized recorder at /home/you/.local/share/sealr
stream_id: 01K2E9V4Q7YB3T8N0C6HRXZ5MF
mode: offline (local)
keystore: file backend (see ADR-010 for the security delta)That directory is the recorder home ($SEALR_HOME, defaulting to ~/.local/share/sealr and
%LOCALAPPDATA%\sealr on Windows). init creates four things in it:
| Path | What it is |
|---|---|
config.json | Stream id, recorder id, and whether sync is on (it is not) |
identity/recorder.key | This recorder's Ed25519 signing key, in the encrypted-file keystore |
identity/recorder.pem, identity/root.pem | A local trust chain — local dev root, local tenant CA, recorder certificate |
spool/ | Append-only record segments and the checkpoint log |
The local trust chain is what makes offline evidence verifiable end to end: exports carry their own root, so a verifier can validate the certificate that signed them without contacting anyone. Its certificate common names say "Local Dev" so that nobody mistakes it for a Sealr-issued production identity.
3. Record something
sealr run wraps any command, records what was executed, then runs it:
sealr run -- git --versionThe record holds the argv, working directory, exit code, duration, and BLAKE3 digests of stdout
and stderr — the digests, not the output itself. For commands Sealr recognizes (psql -c,
terraform, git push) it also parses the operation and attaches the guard's structural
classification.
4. See a guardrail fire
Recording is the default; enforcement is opt-in per invocation with --enforce.
sealr run --enforce --resource db:prod-main -- psql -c "DELETE FROM users"sealr: command BLOCKED by policy (SQL_UNBOUNDED_WRITE).echo $? # 20The SQL guard parsed that statement with the real PostgreSQL grammar, found a DELETE with no
WHERE clause, and classified it unbounded_write / risk CRITICAL with reason code
SQL_UNBOUNDED_WRITE. Exit code 20 is the recorder's "blocked" code, distinct from anything
psql itself could return.
psql never ran, and it does not need to be installed for this to work. The block happens before
the child process is spawned — that is the point of a pre-execution guard. Drop --enforce and the
same statement is recorded with the same verdict but executes normally, which is how you roll
Sealr out before you trust it to say no.
Git works the same way. main, master, release/* and prod* are the default protected
branch patterns:
sealr run --enforce --resource repo:acme/api -- git push --force origin main
# sealr: command BLOCKED by policy (GIT_FORCE_PUSH_PROTECTED). [exit 20]Why this blocks with no policy installed
You have not written a policy, and there is no Console to hand you one. When no policy rule
matches, the verdict falls through to the guard's own default decision, and the SQL guard's
default for an unbounded write is BLOCK. Policies exist to override those defaults — to allow
what a guard would refuse, to require human approval instead of blocking, and to bind enforcement
to specific resources so --enforce is not a per-command decision. See
Policies when you get there.
--resource db:prod-main is the resource label recorded against the operation and the string
policy bindings match on. It changes nothing here, because there are no bindings yet.
5. Export and verify
sealr export -o evidence.sealExported evidence.seal (manifest hash 9f2c…)The bundle contains the stream's records, the signed checkpoints attesting them, and the identity chain and root needed to check the signatures. Now verify it — with the standalone verifier, which shares no state with the recorder:
sealr-verify evidence.seal --root ~/.local/share/sealr/identity/root.pemRESULT: PASS
Proven: N records across 1 stream(s); N attested by checkpoints; 0 anchored.
Recorded span: 2026-08-12T09:14:02Z .. 2026-08-12T09:16:41ZYour record count will be higher than the number of commands you ran: a wrapped command that executes produces both a request and an outcome record, and each run closes its segment with a lifecycle record.
sealr verify evidence.seal runs the same checks in-process if you would rather not switch
binaries. Exit codes: 0 pass, 10 pass with findings, 11 fail.
Pass --root for offline bundles. Released verifier binaries embed the production Sealr roots,
and your local dev chain is not under them — without --root, verification of a locally-generated
bundle fails with checkpoint_cert_unknown. A verifier built from source embeds no roots at all;
it falls back to the bundle's own roots.json and reports a root_bundle_pinned warning, which
is PASS WITH FINDINGS (exit 10). The warning is correct: a bundle that supplies its own root
proves internal consistency, not origin.
0 anchored is also correct and worth reading. Timestamp anchoring is an RFC 3161 service, so an
offline stream has none: you have proven the ordering and integrity of these records and who signed
them, but not that they existed before some external moment in time.
What just happened
Each operation became a record whose hash covers the previous record's hash, so the stream is a chain that cannot be reordered or have entries removed from the middle without detection; the statement text itself never entered the record, only a salted commitment to it plus the structural metadata the guard extracted. At close, the recorder built a Merkle tree over the new records and signed the root into a checkpoint with its identity key, which is what the verifier checks when it reports records as "attested".
Next steps
Cover an agent
Wrap an MCP server or wire the Claude Code hooks, so operations are recorded without anyone
remembering to type sealr run.
Write a policy
Bind enforce mode to named resources, require approval instead of blocking, and stop deciding
with --enforce per command.
Read what each guard detects
Every reason code, the risk it carries, and the default decision behind it.
Understand the limits
What a bundle proves, and the four things it deliberately does not.