Sealrdocs
Reference

CLI reference

Every sealr and sealr-verify subcommand, flag, environment variable and exit code.

Two binaries ship in every release archive:

  • sealr — the recorder. Initializes, enrolls, intercepts, records, enforces, exports.
  • sealr-verify — the offline verifier. Reads a .seal bundle and reports on it. No network, no trust in the Console.

Synopsis

sealr [--home <PATH>] [--json] <COMMAND> [OPTIONS]

Global flags

FlagDefaultWhat it does
--home <PATH>$SEALR_HOME, else the platform data dirRecorder home directory. Overrides everything else.
--jsonoffEmit JSON instead of text, where the command has a JSON form.
-h, --help—Help for sealr or for a subcommand.
-V, --version—Print the version string and exit.

--json is honoured by init, enroll, status, doctor, policy show, policy test, export and verify. run, proxy and hook ignore it — their stdout belongs to the wrapped process or to the agent's protocol.

Where the recorder home lives

Resolution order: --home, then $SEALR_HOME, then the platform default.

PlatformDefault home
Windows%LOCALAPPDATA%\sealr
Linux$XDG_DATA_HOME/sealr, or $HOME/.local/share/sealr when XDG_DATA_HOME is unset
macOS$HOME/.local/share/sealr — there is no Application Support special case

If none of those variables resolve, sealr exits 30 with cannot determine a home directory.

The layout inside it is documented in configuration.

sealr init

Writes a local config. This is the offline path: no account, no Console.

sealr init
sealr init --endpoint https://console.example.com:50051
FlagDefaultWhat it does
--endpoint <URL>"" (empty)Console endpoint. Empty means offline mode: sync_enabled stays false and nothing is sent anywhere.
--stream-id <ID>generated ULIDThe stream id to record under. Supply one only if you are resuming a known stream.

In offline mode init also generates a self-signed local trust chain (root, recorder certificate) so bundles you export verify offline without the Console. With an --endpoint set it generates only an identity key, and enroll replaces it with a Console-issued certificate.

Exit code: 0, or 30 if the home directory cannot be created or written.

sealr enroll

Binds this device to a tenant using a single-use enrollment token from the Console.

sealr enroll --endpoint console.example.com:50051 --token ENROLL_xxx --label env:prod --label team:payments
FlagDefaultWhat it does
--token <TOKEN>requiredThe single-use enrollment token.
--endpoint <ENDPOINT>requiredConsole gRPC endpoint. An endpoint without http:// or https:// is prefixed with http://.
--label <LABEL>noneEnvironment label, repeatable. Stored in the config and attached to records; policy rules can match on them.

Enrollment generates a fresh keypair on the device, proves possession by signing the token, and stores the returned recorder credential in identity/recorder.token — never in config.json.

Exit code: 0 on success, 30 on any failure (unreachable Console, spent token, rejected proof).

sealr proxy mcp

A stdio MCP proxy. It sits between your agent and an MCP server, records every tool call, and — in enforce mode — refuses blocked calls without forwarding them.

sealr proxy mcp --name my-db --resource db:prod-main -- npx -y @modelcontextprotocol/server-postgres postgres://...
FlagDefaultWhat it does
--enforceoff (observe)Apply policy decisions. Without it every call is recorded and forwarded. Also switches the spool to fsync-per-write.
--name <NAME>mcpLogical name for the wrapped server, used in records.
--resource <RESOURCE>noneResource hint for policy binding, e.g. db:prod-main. Without it the target resource falls back to a tool-derived string and resource-bound rules will not match.
-- <COMMAND...>requiredThe MCP server command to wrap. Everything after --.

A blocked call gets a protocol-correct JSON-RPC error synthesized back to the agent; the request is never written to the server's stdin.

Exit code: the wrapped server's exit code (0 if it reports none). A block does not change the proxy's exit code — the proxy keeps running.

sealr hook pre-tool-use

Claude Code PreToolUse hook. Reads the hook JSON on stdin, writes a decision JSON on stdout.

sealr hook pre-tool-use --enforce
FlagDefaultWhat it does
--enforceoff (observe)Return deny when policy blocks. Without it the operation is recorded and always allowed.

Output is one JSON object:

{"decision":"allow"}
{"decision":"deny","reason":"Blocked by Sealr: SQL_UNBOUNDED_WRITE"}

The reason names the guard's first reason code. Only a BLOCK decision produces deny — REQUIRE_APPROVAL on this surface allows and records, because the hook has nowhere to hold the operation while a human decides.

Exit code: 0, including when the decision is deny (the denial is in the JSON, not the exit status). 30 if stdin is not valid JSON or the recorder is not initialized. The spool is fsync'd per write on this surface regardless of --enforce.

sealr hook post-tool-use

sealr hook post-tool-use

No flags. Reads stdin and exits 0, tolerating any payload.

This command is a working stub. It accepts and discards the payload — it does not currently record an outcome record. Wire it into your agent config now if you want the hook plumbing in place, but do not expect post-execution outcomes in your evidence stream yet.

sealr run

Wraps and records an arbitrary command.

sealr run -- psql -h prod-db -c "SELECT count(*) FROM users"
sealr run --enforce --resource db:prod-main -- psql -c "DELETE FROM users"
FlagDefaultWhat it does
--enforceoff (observe)Apply policy decisions before execution. Also switches the spool to fsync-per-write.
--resource <RESOURCE>noneResource hint for policy binding. Without it the target resource is cmd:<program>.
-- <COMMAND...>requiredThe command to run. Everything after --.

Guard evaluation happens only for commands the wrapper recognizes:

ProgramRecognized whenWhat the guard sees
psql-c, --command or --command= is presentThe SQL string
terraform, tofualwaysThe full argv (plan JSON is not available at argv time)
gita push argument is presentThe full argv

Anything else is recorded as a generic execution — argv redacted for the projected operation, exit code, duration and output digests — and is never guard-evaluated, so --enforce cannot block it.

Exit codes: the wrapped command's exit code; 20 if the operation was blocked in enforce mode (or blocked by an approval decision); 30 on a recorder error before execution.

sealr status

sealr status --json

No flags of its own. Prints the home path, stream id, connected/offline mode, cached policy version and whether it is stale, and the spool size in bytes. --json adds recorder_id, tenant_id, sync_enabled and console_endpoint.

Exit code: 0, or 30 if config.json exists but cannot be parsed.

sealr doctor

Self-check over config, keys, and the spool.

sealr doctor

Checks performed, in order: config present · identity key present · stream initialized · key loadable · spool dir writable. It also reports the active keystore backend and flags the file backend as a downgrade (ADR-010).

Exit code: 0 when every check passes, 30 when any check fails.

doctor does not contact the Console. The "connectivity" part of a self-check is not implemented; a network problem shows up as a warning on the next run or proxy, not here.

sealr policy show

sealr policy show --json

Prints the cached bundle's version, its staleness, and each policy's name with its rule and binding counts. Reads policy/bundle.json in the recorder home; an unreadable or absent bundle prints version 0 rather than failing. Exit code 0.

sealr policy test

Dry-runs a policy file against one sample operation. Nothing is recorded and no Console is contacted.

sealr policy test ./policy.yaml \
  --integration sql \
  --resource db:prod-main \
  --input "DELETE FROM users"
ArgumentDefaultWhat it does
<FILE>requiredPolicy YAML file to evaluate.
--input <TEXT>requiredThe operation payload: SQL text, plan JSON, or a git argv line.
--integration <NAME>sqlOne of sql, terraform, git. Any other value routes to the generic guard, which emits no reason codes.
--resource <RESOURCE>db:prod-mainThe target resource the rule binds against.

Output names the guard decision, the risk class, the reason codes, the final policy decision, the matched rule id and the mode.

Exit code: 0 whatever the verdict — a BLOCK result is printed, not signalled. Exit 30 if the file cannot be read or the YAML does not parse. Assert on the --json output if you want this in CI.

sealr export

Assembles a .seal bundle from the local spool.

sealr export -o evidence.seal
FlagDefaultWhat it does
-o, --out <PATH>requiredOutput bundle path.
--level <LEVEL>fullExport level recorded in the manifest. metadata selects metadata level; any other value is treated as full, including a typo.

--level metadata sets the level in the manifest. It does not strip anything on the way out — the recorder always writes its full local records, and the projection to metadata level happens at Console ingest. Records never contain payload literals in the first place; they carry salted commitments.

The bundle carries the records, the durable checkpoint log, the certificate chain and the trusted root when present. sealr export prints the manifest hash.

Exit code: 0, or 30 when there are no records to export or the output cannot be written.

sealr verify

Verifies a bundle in-process, using the same engine as sealr-verify.

sealr verify evidence.seal
sealr verify evidence.seal --root ./root.pem --json
ArgumentDefaultWhat it does
<BUNDLE>requiredPath to the .seal bundle.
--root <PEM>bundle-pinned / embedded rootTrusted root certificate PEM override.

Exit codes: 0 pass · 10 pass with findings · 11 fail · 30 if the bundle cannot be opened.

For auditing, prefer sealr-verify: it is the standalone binary, and it supports the --console-key and --expect-high-water options that sealr verify does not.

sealr version

sealr version

Prints the version and a reminder that there is no self-update — releases are verified and installed with cosign. Distinct from --version, which prints only the version string. Exit code 0.

sealr-verify

sealr-verify <BUNDLE> [OPTIONS]
FlagDefaultWhat it does
<BUNDLE>requiredPath to the .seal bundle.
--root <PEM>bundle-pinned / embedded rootTrusted root certificate PEM. Overrides both the embedded root and the one pinned in the bundle.
--console-key <PEM>key from the bundleConsole verifying key (SPKI PEM) used to check countersignatures.
--expect-high-water <STREAM_ID=SEQ>nonePin a stream's authoritative extent. Repeatable.
--jsonoffEmit the machine-readable JSON report instead of text.
-h, --help / -V, --version—Help and version.

--expect-high-water is the answer to tail truncation. A bundle alone cannot prove that trailing records were not removed before it was exported. Fetch the authoritative sequence out of band from GET /v1/streams/{id}/integrity (max_seq) and pass it here to make missing trailing records a hard failure:

sealr-verify evidence.seal --expect-high-water 01HZY...=4821

Exit codes: 0 pass · 10 pass with findings · 11 fail · 2 malformed --expect-high-water value · 30 usage or config error (unreadable bundle, unreadable --root or --console-key).

Exit codes

CodeMeaningWhich commands
0Success, or verification passedall
10Verification passed with findingsverify, sealr-verify
11Verification failedverify, sealr-verify
20Operation blocked by policy in enforce moderun
30Recorder error; or doctor had a failing check; or sealr-verify usage errorall
2Malformed --expect-high-water argumentsealr-verify
otherThe wrapped process's own exit coderun, proxy mcp

Under sealr run and sealr proxy mcp the exit code you see is usually the wrapped process's own. A wrapped command that itself exits 20 or 30 is indistinguishable from a Sealr block or a recorder error by exit code alone. Use --json records or stderr (sealr: command BLOCKED by policy (...)) when you need certainty in a script.

Environment variables

VariableRead byWhat it does
SEALR_HOMEall commandsRecorder home directory. --home overrides it.
SEALR_PRINCIPALrun, proxy mcpHuman principal (e.g. an OIDC subject) attached to records as an attributed subject.
SEALR_LOGsealr, sealr-ledgerTracing filter, default warn. Logs go to stderr so stdout stays clean for JSON-RPC and hook output. Try SEALR_LOG=info.
SEALR_KEYSTORE_PASSPHRASEkeystoreWhen set and non-empty, the identity key at rest is protected by Argon2id over this passphrase instead of a host-derived key. See configuration.

Subject attribution falls back through these when SEALR_PRINCIPAL is unset: GITHUB_ACTOR, GITLAB_USER_LOGIN, CI_COMMIT_AUTHOR (recorded as CI-attributed), then USER or USERNAME (recorded as inferred). With none of them, the record is unattributed and says so.

Next

On this page