Obtain the bundle
Export covering the period under assessment. State the shape of this honestly: there is no single command that emits one sealed bundle for a date range. Three things exist, and only the first two are signed.
Evidence / For assessors
The claim is that evidence is a by-product of governing rather than a report assembled afterwards. This page is that claim made checkable: what the bundle contains, how to verify it without trusting us, and what it does and does not establish.
CONTENTS
Signed evidence bundles are available per deployment and are not default-on. Configuration and verification are explicit steps, and a deployment that has not configured them does not produce one.
entry_hash = SHA-256(prev_hash + entry_json), where entry_json is the entry in a fixed field order with the two chain fields removed from their own input, and the first entry chains from an empty string. Every row also carries a sequence number; against Postgres the prior hash and the next sequence are read inside the appending transaction under a cross-replica advisory lock, so concurrent writers cannot fork the chain. Two hash layouts exist: the original does not cover account_id or sequence_number, the current one does, and a verification reports how many legacy rows it walked — a chain that passes over legacy rows says nothing about which tenant those rows belonged to. Note what this is: the platform audit chain. A separate per-tenant decision ledger with the same prev_hash / entry_hash shape exists as database schema only, with no write path behind it.from_ts and to_ts are the first and last record timestamps in it. A loop evidence bundle reuses the same manifest envelope but sets merkle_root to a flat SHA-256 content hash over the whole canonical bundle body. Same field, no tree.sig omitted — covering decision_id, ts, org_id, gateway_id, bundle_hash, input_hash, decision, effect, stage, kid and seq. The batch manifest is signed separately over its own canonical bytes: batch_id, from_ts, to_ts, record_count, merkle_root, kid. So the manifest signature covers the root, and the root is what binds the records to it. A loop bundle signs that same envelope, where the root is the bundle’s content hash, so there the signature does cover the whole emitted body except the signing envelope itself. Distribution: every signature names a kid, and the public halves are served as a keyring — public material only, each key carrying ACTIVE, PREVIOUS or RETIRED status and a not-before / not-after window — from the control plane’s gateway-keys endpoint; a gateway that generates its own key registers the public half back. There is no JWKS endpoint and no out-of-band publication, which is precisely why third-party offline verification is not yet available.bundle_hash: the SHA-256 over the canonical body of the policy bundle in force at that gateway when the decision was made — the org, kill switches, resolved bindings, active exceptions, OPA policies, the SLM contract and the behavioural thresholds, with every slice sorted before hashing so the value is order-independent. The bundle header alongside it carries a monotonic sequence and an epoch and is itself Ed25519-signed, and the same hash is what the drift check compares. Because bundle_hash sits inside the record’s canonical signing bytes, it cannot be changed after the fact without breaking that record’s signature. What it does not do is name the individual rule: it identifies the policy state by content, and resolving it back to readable policy needs the control plane that issued it.batch_id, from_ts, to_ts, record_count, merkle_root, kid, sig. A loop evidence bundle is one document declaring schema_version: loops.evidence.v1, with a content-addressed bundle_id, generated_at, the account, the loop, an explicit machine-readable honesty verdict, then runs, events, stop_conditions and shadow_findings, a counts block and the signing envelope. counts.truncated is set when any collection hit its cap, so an omission is disclosed rather than silent. The compliance report export is a different artifact: one file in PDF, JSON, CSV or HTML, and it is unsigned — a report, not the sealed record.VERIFICATION
The property that matters is that verification does not require us to be online, honest, or still in business.
Export covering the period under assessment. State the shape of this honestly: there is no single command that emits one sealed bundle for a date range. Three things exist, and only the first two are signed.
Confirm the bundle was produced by the deployment it claims to come from, and has not been altered since. The endpoint re-derives the Merkle root from the records you supply, compares it to the manifest, then checks the manifest’s own Ed25519 signature against that gateway’s keyring. A manifest can be checked without its records, and then merkle_verified deliberately stays false. With no keyring wired it answers 503 rather than a comforting valid: true.
Can a standard tool do it? The signature itself is plain Ed25519 over canonical JSON whose field order is fixed and documented, so any Ed25519 implementation can check it once you hold the public keyring. But no standalone verifier ships, and the keyring is served by the control plane — so nothing completes the check today without a GovernorAI component present.
Confirm no entry has been removed or edited. This is the check that makes the record worth reading: an append-only log that anyone can silently edit is a report, not evidence. The walk starts from an empty genesis hash and stops at the first row it cannot verify, naming that row by id in first_broken. There are two failure shapes: a prev_hash that does not match the running hash, which is what a removed or reordered entry looks like, and an entry_hash that matches neither hash layout, which is what an edited entry looks like.
Two limits stated before an assessor finds them. An empty chain verifies, which is not evidence of integrity. And a truncated tail is invisible to this check by construction — it validates the entries it is handed and has no independent notion of where the chain should end, so detecting truncation needs a head anchored outside the database, which is not shipped.
Trace a specific governed action to the control it evidences, and to the policy version in force at the time.
Read the last block carefully, because it is where the chain genuinely breaks. The mapping artifact maps a control to a capability — kill switch, audit log, policy engine, RBAC, approval workflow — and declares whether that coverage is automatic, partial or manual attestation. Nothing maps a control to an individual decision: no field on an evidence record, an audit entry or a manifest names a control, and no code joins the two. So an assessor is shown the control’s declared coverage and the events produced by the capability it names, and a person makes the join. That is a mapping of controls to mechanisms, not of controls to decisions, and calling it the second thing would be the overstatement this page exists to avoid.
External offline verification — a third party verifying without any GovernorAI component present — is roadmap, not shipped. Say so on this page rather than letting an assessor discover it during fieldwork.
LIMITS
not_assessed, which is a stated gap rather than a pass.The value of this page to an assessor is entirely in the commands and their real output, and three of them are worth reading twice. There is no single export that seals one bundle for a date range. merkle_root names a real binary tree on a gateway batch and a flat content hash on a loop bundle, and the page says which is which rather than letting the stronger word cover both. And the control mapping reaches a capability, not a decision — the last step of the join is made by a person.