Receipt Specification
Current WitnessOps receipt contracts, the legacy public_exposure_review profile, and the bounded checks performed by the public verifier.
Which receipt contracts are current, what does the legacy public_exposure_review profile require, and what can the public verifier assert?
This page describes the receipt contracts that the current WitnessOps code accepts. It keeps the canonical, legacy-named public_exposure_review contract separate from older compatibility receipt families.
If you need the concept first, start with Receipts. If you need the public procedure, go to Verification.
1. Current contract boundary
The canonical cross-repository receipt envelope is witnessops.receipt.v0. The public_exposure_review compatibility profile applies the additive context witnessops.verification_context.v1 and workflow class public_exposure_review.
The public /verify surface also retains bounded compatibility adapters. Acceptance by a compatibility adapter does not make that receipt family the canonical cross-repository contract.
| Receipt family | Current public handling | Boundary |
|---|---|---|
witnessops.receipt.v0 + witnessops.verification_context.v1 + public_exposure_review | Product-specific receipt-only adapter | Canonical legacy profile; currently indeterminate when structurally conforming because full evidence and production trust are not checked |
PV 1.0.0 / 1.1.0, QV 2.0.0, WV 2.1.0 | Generic receipt compatibility adapter | Receipt-only checks; partial artifact coverage maps to indeterminate |
witnessops.local_server_audit.receipt.v1 and its exact legacy markers | Structural dual-read adapter | Structural and cross-field checks only; artifact bytes are not checked |
Objects such as a conceptual witnessops.receipt.v2 ledger envelope are not accepted by the current public API unless they also match one of the executable contracts above. They must not be presented as the universal current receipt format.
The public_exposure_review identity crosswalk is exact:
| Authority layer | Identifier |
|---|---|
| Existing product ID | OFFSEC-EXTERNAL-EXPOSURE |
| Receipt workflow class | public_exposure_review |
| OffSec source runbook | external-exposure-assessment version 2 |
| Receipt verification method | external_exposure_assessment version 2.0.0 |
The product ID and hyphenated source-runbook ID belong to the offer and producer context; they are not extra top-level receipt fields.
2. Legacy public_exposure_review envelope
A receipt using this profile has exactly these top-level fields:
| Field | Purpose |
|---|---|
receipt_version | Must be witnessops.receipt.v0 |
receipt_profile | Must be witnessops.verification_context.v1 |
workflow_class | Must be public_exposure_review |
proof_run_id | Stable run identifier in the pr_per_<24 lowercase hex> form |
verification_context | Subject, scope, method, timestamps, and preserved limitations |
result | Bounded review outcome and named failure states |
claims | Exact workflow claim set with manifest artifact references |
manifest_hash | Declared SHA-256 digest of the evidence manifest |
signature | Declared Ed25519 signature and public-key identifier |
The following example is a shape illustration, not a pasteable valid receipt. Real receipts must contain the exact frozen method text, complete claims and limitations, real manifest artifact IDs, a real manifest digest, and a real signature.
{
"receipt_version": "witnessops.receipt.v0",
"receipt_profile": "witnessops.verification_context.v1",
"workflow_class": "public_exposure_review",
"proof_run_id": "pr_per_<24-lowercase-hex>",
"verification_context": {
"subject": {
"type": "public_facing_system",
"reference": "urn:witnessops:public-exposure-review:<proof-run-id>"
},
"scope": {
"included": ["declared included scope"],
"criteria": ["declared review criteria"],
"excluded": ["declared exclusions"],
"observation_window": {
"started_at": "<UTC timestamp>",
"ended_at": "<UTC timestamp>"
}
},
"verification_method": {
"id": "external_exposure_assessment",
"version": "2.0.0",
"procedure": ["<exact frozen procedure>"],
"pass_criteria": ["<exact frozen pass criteria>"],
"fail_criteria": ["<exact frozen fail criteria>"]
},
"timestamps": {
"performed_at": "<UTC timestamp>",
"issued_at": "<UTC timestamp>",
"expires_at": null
},
"limitations": ["<all required limitation codes>"]
},
"result": {
"outcome": "inconclusive",
"failure_states": ["<named unresolved state>"]
},
"claims": [
{
"claim": "offer_contract_applied",
"status": "inconclusive",
"evidence_refs": ["offsec_<24-lowercase-hex>"]
}
],
"manifest_hash": "sha256:<64-lowercase-hex>",
"signature": {
"algorithm": "ed25519",
"public_key_id": "<registry-key-id>",
"encoding": "hex",
"signature": "<128-lowercase-hex>"
}
}
Receipt outcomes are exactly pass, partial, fail, or inconclusive.
blocked is an operational workflow state and is not a receipt outcome. The
bounded review outcome is separate from the verifier verdict: even a receipt
whose review outcome is pass remains publicly indeterminate while required
evidence, execution, signature, or trust checks are incomplete.
3. Workflow context and claims
The profile binds the receipt to one product workflow, not to a generic verification offer.
The subject must be a public_facing_system. The subject reference must be derived from the proof-run ID. Scope must preserve included items, review criteria, exclusions, and a UTC observation window.
The method is frozen as external_exposure_assessment version 2.0.0. The public adapter compares the complete procedure, pass criteria, and fail criteria with its server-owned contract snapshot. Request input cannot replace that contract.
Every receipt must contain each of these six claims exactly once:
offer_contract_appliedauthority_and_scope_recordedrecorded_checks_within_approved_schedulefindings_reference_evidenceunknowns_and_limitations_preservedsource_artifacts_hash_bound
Each claim must reference at least one real OffSec manifest artifact ID in the form offsec_<24 lowercase hex>. The bounded producer adapter precomputes each staged source artifact's exact future manifest ID; the proof engine later emits that ID from the extensionless staged filename. Synthetic labels or invented path names are not acceptable substitutes.
The public receipt-only adapter checks identifier syntax and claim membership. It does not receive the manifest entry or artifact bytes, so it cannot establish that a reference resolves or supports the claim.
4. Required limitations
The profile preserves these eight product non-claims:
not_a_penetration_testnot_a_certificationnot_an_attestationnot_a_compliance_determinationnot_proof_of_securitynot_proof_of_completenessnot_proof_of_third_party_acceptancenot_proof_of_absence_of_vulnerabilities
Additional limitations may be present. Removing or weakening any required limitation makes the profiled receipt invalid.
5. Time, manifest, and signature fields
All timestamps are UTC RFC 3339 values. The required chronology is:
started_at <= ended_at <= performed_at <= issued_at < expires_at
expires_at may be null. The public adapter checks syntax and chronology only; it does not supply an independent time source.
manifest_hash must declare a lowercase SHA-256 digest. The receipt-only adapter checks that syntax but does not receive or hash manifest bytes.
The signature block must declare Ed25519, a canonical key identifier, lowercase hexadecimal encoding, and a 64-byte signature. The public adapter checks that shape. It does not verify the signature cryptographically while no server-owned active production key is authorized.
6. Production key acceptance
The current public_exposure_review trust snapshot is deliberately non-authorizing:
- workflow acceptance policy
public_exposure_review.production.v1isdraft, with no trusted revision, policy hash, or registry-manifest hash pinned - key-trust policy
public_exposure_review.production_signing.v1isdraft, with no trusted registry revision or manifest hash pinned - the registry status is
draft - no production public-key IDs are allowlisted
- the production custody-approval reference is unset
Production acceptance requires server-owned policy and registry snapshots, not caller-supplied trust material. Before a receipt can be accepted as production-valid:
- the workflow acceptance policy must be active and pin the exact key-policy bytes, trusted registry revision, and registry-manifest SHA-256
- the key-trust policy and registry must be active and their revision and manifest pins must agree with the verifier snapshot
- the signer must be explicitly allowlisted with an active key record using Ed25519, hexadecimal encoding,
productiontrust scope, and usagepublic_exposure_review_receipt_signing verification_context.timestamps.issued_atmust be on or aftervalid_fromand beforevalid_until- the production custody-approval reference must be recorded
- revocation must be null and propagation rules must confirm that the verifier snapshot matches the approved current pins
Missing, stale, unavailable, or unconfirmed required trust input is indeterminate. A cryptographic mismatch, known revoked or disallowed key, trust-scope or usage mismatch, or signature outside the accepted validity window is invalid once those checks are actually performed. A rotated key without an applicable historical policy is unsupported under the draft key policy.
7. Public verification result
For a structurally conforming public_exposure_review receipt, /verify currently reports indeterminate, with scope: receipt-only and artifactRevalidation: not_performed.
The adapter checks:
- exact envelope, version, profile, workflow class, and proof-run ID shape
- bounded result and failure-state shape
- exact required claim set and manifest artifact-ID syntax
- subject and declared scope syntax
- exact frozen verification method
- preservation of the eight required limitations
- timestamp syntax and chronology
- manifest-digest syntax
- signature-block syntax
It does not independently check:
- the originating request record
- authority packet, frozen target schedule, or approved-check schedule
- complete workflow execution records
- whether the frozen verification method was actually executed
- the manifest bytes or their digest
- artifact bytes or per-artifact hashes
- whether referenced evidence supports each claim
- the Ed25519 signature against an active production key
- production key authorization and revocation state
Caller-supplied evidence, keys, registries, policies, or verifier results are rejected rather than promoted to server trust inputs.
8. What the receipt can and cannot establish
Receipt shape validation establishes that the submitted JSON conforms to the declared profile. It does not establish issuance, evidence truth, signer authorization, scope authorization, or complete execution.
When a full package is independently verified against authentic, active trust inputs, stronger conclusions may be possible about signature integrity, manifest and artifact consistency, workflow evidence, and claim support. That is a separate internal package-verification path; the canonical internal verifier is not a supported public distribution today.
A receipt never proves that the reviewed system is secure, that a finding is correct, that no vulnerability exists, or that the engagement is complete beyond its declared scope and evidence.
9. Next-page handoff
Read How to Verify a Receipt for the public procedure and exact verdict boundaries.
Then continue with:
- Receipts for the mechanism summary
- Evidence Bundles for the package boundary
- Threat Model and Trust Boundaries for adversarial limits