How It Works

How to Verify a Receipt

Run the public receipt-only check, understand legacy public_exposure_review indeterminate semantics, and keep full-package verification separate.

This Page Answers

What does the public verifier check, why is a legacy public_exposure_review receipt currently indeterminate, and what would full verification require?

Public tool first: use Verify a receipt when you have supported receipt JSON. The public surface is receipt-only. It does not accept a proof bundle or caller-supplied keys, registries, policies, evidence, or prior verifier output.

1. Public procedure

  1. Open /verify.
  2. Upload a supported .json receipt or paste its JSON.
  3. Select Verify receipt.
  4. Read the verdict, adapter, verification scope, artifact-revalidation state, every named check, and every limitation.

Try an example currently loads a PV compatibility fixture whose receipt-scoped checks pass but whose artifact bytes are not revalidated. Its result is indeterminate, displayed as Indeterminate — verification incomplete.

2. Public result contract

The public API separates accepted verification results from malformed or unsupported input.

ResultMeaning
validAll checks required by that adapter and mode were independently completed and passed
invalidA proof-bearing, profile, binding, or policy check failed
indeterminateThe receipt may be coherent, but one or more required evidence, artifact, authorization, workflow, signature, or trust checks were not independently completed
FAILURE_INPUT_MALFORMEDThe submitted request or receipt JSON could not be safely parsed or validated as input
FAILURE_INPUT_UNSUPPORTEDThe input is outside the receipt-only surface, including proof bundles and caller-supplied trust inputs

An internal verifier result of limited-pass maps to public indeterminate, never to valid.

3. Legacy public_exposure_review adapter

Receipts with workflow class public_exposure_review route to witnessops.verify.public_exposure_review_receipt.v1.

The adapter recognizes the canonical product contract:

  • product OFFSEC-EXTERNAL-EXPOSURE
  • OffSec source runbook external-exposure-assessment version 2
  • witnessops.receipt.v0
  • witnessops.verification_context.v1
  • public_exposure_review
  • external_exposure_assessment method version 2.0.0

Checks performed on receipt JSON

The adapter checks:

  • exact envelope and workflow identity
  • bounded result and failure-state structure
  • the exact six required claims
  • offsec_<24 lowercase hex> evidence-reference syntax
  • subject and declared scope syntax
  • exact frozen method procedure and acceptance criteria
  • all eight required product limitations
  • UTC timestamp syntax and chronology
  • manifest SHA-256 syntax
  • Ed25519 signature-block syntax

These are deterministic receipt-shape and profile checks. They do not independently establish that the receipt's declarations are true.

The canonical acceptance-policy check ID is verification_method_definition. The v1 API response also retains verification_method as an additive compatibility alias; the verification page suppresses that duplicate display entry.

Checks not performed on the public surface

The adapter explicitly reports these as not checked:

  • originating request record
  • authority packet and scope authorization
  • frozen target and approved-check schedules
  • complete workflow execution records
  • whether the frozen method was actually executed
  • manifest bytes and manifest digest recomputation
  • artifact bytes and artifact hash recomputation
  • whether evidence references resolve and support each claim
  • cryptographic signature verification
  • production key authorization and revocation state

A well-formed receipt using the legacy profile therefore currently returns indeterminate. A profile conflict returns invalid.

4. Real evidence references

The legacy profile's claim references are not display labels. The bounded OffSec output adapter precomputes each staged source artifact's future canonical manifest ID in the form offsec_<24 lowercase hex>. The proof engine later uses the extensionless staged filename as the artifact ID in the manifest.

The web adapter validates only that form. It does not fetch or accept the manifest and cannot prove that an ID resolves, that bytes match, or that the evidence supports a claim. Those conclusions require the package-verification path.

5. Production key-registry acceptance

The server-owned public_exposure_review trust policy is currently non-authorizing:

  • workflow acceptance policy public_exposure_review.production.v1 is draft and has no trusted revision, policy hash, or registry-manifest hash pinned
  • key-trust policy public_exposure_review.production_signing.v1 is draft and has no trusted registry revision or manifest hash pinned
  • the registry is draft
  • no production signing keys are allowlisted
  • the production custody-approval reference is unset

Production acceptance must fail closed against a pinned, server-owned trust snapshot. Activation requires:

  1. an active workflow acceptance policy that pins the exact key-policy bytes, trusted registry revision, and registry-manifest SHA-256
  2. an active key-trust policy and registry whose revision and manifest pins match the verifier snapshot
  3. an explicitly allowlisted active key using Ed25519, hexadecimal encoding, production trust scope, and usage public_exposure_review_receipt_signing
  4. receipt issued_at on or after the key's valid_from and before its valid_until
  5. a recorded production custody-approval reference
  6. null revocation plus propagation checks proving that the verifier snapshot matches the approved current pins

Caller-supplied policy, registry, or public key material is never a production trust input. Missing, stale, unavailable, or unconfirmed required trust remains indeterminate; a known cryptographic mismatch, revoked key, disallowed signer, trust-scope or usage mismatch, validity-window failure, or policy mismatch is invalid once evaluated. A rotated key without an applicable historical policy is unsupported under the draft key policy.

6. Full-package verification is separate

The current internal package path can evaluate a receipt with its manifest, referenced artifacts, public key, request and workflow records, comparison output, and an explicit trust policy. That path can recompute bytes and evaluate checks the public receipt-only surface cannot perform.

Keep two outputs distinct:

  • the proof engine's verification_result.json records producer-side build and comparison checks
  • an independently run verifier result records what a separate verifier established from supplied package and trust inputs

The canonical full verifier is currently internal and has no supported public distribution. The public docs therefore do not direct buyers to install an unshipped CLI or imply that /verify performs package verification.

7. Interpretation boundaries

Even full verification does not prove:

  • that every source observation is correct
  • that the system is secure or vulnerability-free
  • that the review covers anything outside the approved scope
  • that the source host or toolchain was uncompromised
  • that human severity or business-risk judgment is correct

Verification establishes named relationships over supplied artifacts and trust inputs. It does not recreate the underlying world.

8. Next-page handoff

Read Receipt Specification for the exact legacy-profile contract and Evidence Bundles for the current package boundary.

How to Verify a Receipt | WitnessOps