Receipt issuance is plan-dependent. Decisions on every plan are still recorded in the queryable audit logs.
What a receipt contains
Once the receipt is committed to a batch it also carries its Merkle coordinates: the batch it belongs to, its leaf index, and its leaf hash. The dashboard’s record verification page surfaces all of these fields for any record.
When receipts are issued
Signing is asynchronous — scheduled immediately after the envelope persists, entirely off the decision hot path. Your agents never wait on cryptography; a decision’s latency is identical with receipts on or off. Asynchronous does not mean best-effort. Unbatched receipts are the durable batching queue: if a batching sweep fails, the next sweep picks up the same receipts. A record’s verification result reportsattestationStatus: "timestamp_pending" between signing and batch commitment — typically a minute or two — and "signed" once the full chain verifies.
The exact cryptography
Canonicalization
The signed payload is the envelope’sevent object, serialized deterministically:
- Object keys are sorted alphabetically, recursively (arrays keep their order; primitives pass through unchanged).
- The sorted structure is serialized with compact
JSON.stringify— no whitespace. - The signature and hash operate on the UTF-8 bytes of that string.
Hash and signature
payload_hash= SHA-256 over the canonical bytes, hex-encoded.signature= Ed25519 over the canonical payload bytes themselves — not over the hash.
Key management
In production the signing key is mandatory — the signer refuses to fall back to an ephemeral key, so a receipt that exists is always verifiable against the published key. Receipts produced by the hardened strategies record which one signed them (a receipt with no recorded strategy was signed by the default strategy), and an optional hardened mode signs every receipt inside the HSM itself so the private key never enters an application process (for those receipts, the signed message is the 32-bytepayload_hash digest rather than the canonical bytes — batch roots are signed the same way). The receipt’s stored public_key is authoritative for verification, so key rotation never invalidates history.
Verify with one call (recommended)
Ask the platform to re-derive and re-check the entire attestation chain for a record:pass: false with a reason — never a silent pass — with one deliberate exception: the ingest anchor on legacy records that predate anchor recording. verified is true only when all applicable checks pass. The Merkle inclusion proof is recomputed on demand from the batch’s retained leaf set — it must reproduce the signed root exactly — and the root check performs a live AWS KMS Verify round-trip, confirming the HSM key itself vouches for the signature.
These endpoints sit on the management surface: harness keys used by your agents are deliberately confined to the operational SDK routes and receive 403 here. The dashboard’s record verification page runs the identical checks — click any record pill under Harness → Runtime Enforcement. For scripted access, see Platform Automation.
Verify offline (TypeScript)
Receipts also verify with no VisIQ dependency at all — Node’s built-in crypto is enough. Copy theevent and receipt fields from the record’s verification page in the dashboard:
This verifies the default signing strategy. A receipt whose recorded signing strategy is the HSM-per-receipt mode signs the 32-byte
payload_hash digest instead — substitute Buffer.from(receipt.payload_hash, "hex") as the message in step 3. The one-call endpoint above handles both automatically.Merkle inclusion and the checkpoint chain
Receipts are batched into an RFC 6962-style Merkle tree with domain-separated hashing:- Leaf hash =
SHA-256( 0x00 ‖ payload_hash_bytes ) - Node hash =
SHA-256( 0x01 ‖ left ‖ right ) - An unpaired trailing node is promoted unchanged to the next level — never hashed with a copy of itself.
chain_hash — so one trusted tip signature vouches for the entire history. Deleting a batch leaves a gap, reordering breaks the accumulator, and editing any root breaks the recomputation. Two management-API endpoints expose this to any authenticated caller (the feed itself is tenant-neutral — it contains only roots, signatures, and counts):
GET /record/checkpoints— the transparency-log checkpoint feed: signed roots, signatures, leaf counts, and timestamp times, newest first. Also browsable in the dashboard under Settings → Checkpoint feed (organization scope).GET /record/chain/consistency— walks a chain segment and proves, purely by recomputation, that no batch was deleted, reordered, or tampered with.
RFC 3161 trusted timestamps
Each batch root is countersigned by DigiCert’s timestamp authority. The DER-encoded timestamp token binds the root hash to a time asserted by DigiCert — an independent third party — and verification checks the token against DigiCert’s certificate chain. Combined with the chain above, this proves a decision existed no later than the attested time, on evidence that does not depend on VisIQ’s clocks or word. For long-horizon audits, batches can also carry frozen revocation evidence for the timestamp certificate chain, so tokens remain independently verifiable years later, after certificates expire or responders disappear.Next steps
Audit Trail
What gets recorded, the envelope schema, and how to consume the trail.
Platform Automation
Authenticate scripts and CI against the management API.