Receipt Specification
Technical reference for WitnessOps Receipt v2, its proof references, and what the current verifier can assert about it.
What exactly is the receipt object, which proof references define it, and what can the current verifier assert about it?
This is the technical reference page for WitnessOps Receipt v2. It explains what a receipt is expected to carry, which proof references matter, how continuity is expressed, and how the current verifier reads the surrounding proof package.
If you need the concept first, start with Receipts. If you need the operator workflow, go to Verification.
1. Problem this page solves
Reviewers often receive a receipt artifact but still need a field-level contract for what is signed, what is referenced, and what verification can actually assert.
This page solves that diligence problem by defining the Receipt v2 shape and its proof references in technical terms.
2. What you should understand after reading
After this page, you should be able to answer:
- What exactly is a WitnessOps Receipt v2?
- What is signed, what is timestamped, and what is only referenced?
- Which fields establish continuity between receipts?
- How does the current verification path classify success, failure, or incomplete trust?
This page describes the public receipt contract. It does not replace the verifier implementation or bundle contract. It explains how they fit together.
Receipt v2 at a glance
Receipt v2 is the canonical statement WitnessOps issues for a governed event. It is designed to be:
- portable enough to move outside the runtime
- structured enough for offline verification
- narrow enough to stay atomic
Receipt v2 is not the full evidence bundle. It is the atomic proof statement inside the wider trust package.
At minimum, a Receipt v2 needs to express five things:
- identity of the receipt itself
- continuity position within a receipt stream
- claimed event details
- proof references to signed and timestamped material
- enough linkage to connect the receipt to the bundle and chain around it
Canonical shape
The public shape below is intentionally simplified, but it reflects the current model: a governed statement plus structured references to the cryptographic artifacts that bind it.
{
"schema": "witnessops.receipt.v2",
"receipt_id": "rct_01H...",
"ledger": {
"stream": "engagement/execution",
"seq": 1842,
"prev_hash": "sha256:...",
"entry_hash": "sha256:..."
},
"time": {
"claimed_utc": "2026-03-28T12:34:56Z"
},
"actor": {
"principal": "operator://...",
"origin": "bridge://cli"
},
"intent": {
"type": "scan.recon",
"request_id": "req_01H..."
},
"result": {
"status": "ok",
"summary": "Governed recon completed"
},
"proof": {
"dsse": {
"payload_type": "application/vnd.witnessops.execution-receipt.v2+json",
"envelope_digest": "sha256:...",
"keyid": "sha256:..."
},
"rfc3161": {
"present": true,
"hashed_message": "sha256:...",
"tsa": "tsa://...",
"token_ref": "bundle/TIMESTAMP.tsr"
},
"log": {
"included": true,
"checkpoint_ref": "bundle/checkpoint.json",
"inclusion_ref": "bundle/inclusion-proof.json"
}
}
}
The exact packaging may vary by bundle or delivery format. The trust model does not: the receipt is the claim, and the bundle carries the surrounding material required to verify that claim.
Field groups
Receipt identity
These fields identify the artifact being inspected.
| Field | Purpose |
|---|---|
schema | Declares the receipt contract version |
receipt_id | Stable identifier for this receipt |
intent.type | Names the governed event category |
result.status | Records the terminal outcome claimed by the issuer |
These fields tell the verifier what kind of statement they are looking at. They do not establish continuity or proof strength on their own.
Continuity fields
These fields place the receipt into an append-only stream.
| Field | Purpose |
|---|---|
ledger.stream | Names the stream or lane this receipt belongs to |
ledger.seq | Sequence number inside that stream |
ledger.prev_hash | Hash of the previous receipt in the stream |
ledger.entry_hash | Hash of this receipt's canonical form |
Continuity is broken if entry_hash recomputes differently, prev_hash does not match the prior receipt, or expected sequence order is missing.
Claimed event fields
These fields describe the governed event itself.
| Field | Purpose |
|---|---|
time.claimed_utc | Claimed event time from the issuer |
actor.principal | Who acted or approved |
actor.origin | Which invocation path produced the request |
intent | What was requested under policy |
result | What WitnessOps says happened |
These fields matter for audit reconstruction, but they remain issuer claims until the surrounding proof material verifies.
Proof reference fields
Receipt v2 carries references to cryptographic artifacts rather than pretending the receipt alone is the whole proof story.
| Field | Purpose |
|---|---|
proof.dsse.payload_type | Declares the signed payload type |
proof.dsse.envelope_digest | Digest of the DSSE envelope bytes |
proof.dsse.keyid | Identifier for the signing key used to issue the statement |
proof.rfc3161.hashed_message | Digest bound into the RFC 3161 timestamp token |
proof.rfc3161.tsa | Timestamp authority that issued the token |
proof.log.checkpoint_ref | Reference to the log checkpoint that covers the receipt |
proof.log.inclusion_ref | Reference to the inclusion proof or equivalent log material |
These references matter because the receipt does not become portable proof until a verifier can locate and validate the signed, timestamped, and committed material around it.
DSSE reference model
The signing layer is a DSSE envelope over the receipt statement. For diligence purposes, the important points are:
- the payload type is explicit
- the envelope bytes are the object being signed
- the envelope digest is the object later bound to trusted time
That order matters. The timestamp should bind to the signed envelope, not to an unsigned precursor and not to a human-readable rendering.
RFC 3161 reference model
WitnessOps uses RFC 3161 as the trusted-time reference for the signed object. In the public model:
- the timestamp token covers the DSSE envelope digest
- the timestamp token is an adjacent proof artifact, not a replacement for the receipt
- the receipt records where that timestamp proof lives and what digest it is expected to cover
When the timestamp token is absent, the receipt may still be valid as a signed statement, but it proves less about time.
Canonicalization and derived hashes
Receipt verification depends on deterministic hashing. Two rules matter:
- the canonical representation must be stable
- derived fields must not participate in computing themselves
Current typed proof paths in the implementation follow this rule explicitly. Artifact-hash computation and execution-hash computation are treated as separate canonicalization paths so the same receipt body is not allowed to self-justify derived digests.
For a technical buyer, the important consequence is simple: if canonicalization rules are ambiguous, trust drops immediately. WitnessOps treats canonicalization as part of the proof contract, not as a UI detail.
Current verification model
Keep two verification surfaces separate.
- The public
/verifysurface is receipt-first v1. It accepts supported receipt JSON and returns a receipt-scoped result. - Bundle-complete verification evaluates the receipt in the context of the canonical proof bundle around it.
At a high level, a bundle-complete verifier does this:
- normalize and parse the manifest
- validate required fields and protocol compatibility
- check artifact presence and hash integrity
- validate signature and digest relationships
- evaluate witness or trust-registry requirements when declared
- return
valid,invalid, orindeterminate
The public receipt-first surface does not prove bundle completeness or artifact-byte revalidation. A public valid result means supported receipt-level checks passed for the submitted receipt.
Those statuses mean:
| Status | Meaning |
|---|---|
valid | Required bundle structure and proof checks passed |
invalid | A proof-bearing check failed |
indeterminate | The bundle may be well-formed, but a required outside trust condition was unavailable |
That last case matters. WitnessOps treats incomplete trust state differently from a hard cryptographic failure.
Public failure vocabulary and proof breach codes
The public API first distinguishes input rejection from receipt verification results.
Public API failure classes:
FAILURE_INPUT_MALFORMEDFAILURE_INPUT_UNSUPPORTED
Proof-bearing receipt checks then expose breach codes such as:
SCHEMA_MISSING_FIELDSCHEMA_UNKNOWN_STAGEDIGEST_RECORD_NOT_IN_SETDIGEST_ARTIFACT_TAMPEREDATTESTATION_SIGNATURE_INVALIDANCHOR_RFC3161_IMPRINT_MISMATCHWITNESS_SUBJECT_MISMATCHWITNESS_UNTRUSTED
For diligence review, that separation matters. A malformed request is different from a receipt that parses but fails a proof-bearing check.
What Receipt v2 proves
When verified against a trusted WitnessOps verification key, Receipt v2 proves that the signed statement is intact and claims issuance under that key identity, and that the statement is bound into a continuity structure with explicit proof references. It does not by itself prove key custody was uncompromised or that only WitnessOps could ever produce a matching shape.
When the surrounding bundle also verifies, the verifier can additionally conclude that:
- the signed statement is intact
- the required artifacts match declared digests
- the receipt sits inside the declared trust package
- any declared witness policy or trust-registry requirement was satisfied, unless the verifier returns
indeterminate
What Receipt v2 does not prove
Receipt v2 does not, by itself, prove:
- that the underlying tool output was correct
- that the target choice was appropriate
- that the host running the operation was uncompromised
- that key custody outside the proof system was perfect
- that an isolated receipt tells the whole operational story without its chain and bundle context
Those boundaries belong in the threat model, not hidden in marketing copy.
8. Next-page handoff
Next, read Threat Model and Trust Boundaries to evaluate what this receipt contract can and cannot guarantee under adversarial conditions.
Then continue with:
- Execution Chains for continuity and Merkle packaging
- Proof Model for signature and timestamp layers
- Verification for operator workflow