DefaultVerifier
← Back to Specifications

SettlementWitness Receipt Profiles v0.2

This page documents the signed-core field sets that Default Settlement's SettlementWitness service actually issues today: settlement-witness-verified-v0.2 (generic) and settlement-witness-verified-v0.2-counterparty-bound (its opt-in extension). It exists because /integrate previously pointed verifiers here at /spec/sar-v0.1/ for "the full field set" — but that page documents a different, earlier protocol (the portable SAR v0.1 six-field core) that SettlementWitness receipts do not use. This page is the one that is actually implementable against.

Not a new SAR protocol version

These are Default Settlement / SettlementWitness product receipt profiles, not a formal sar.v0.2 protocol release. The portable SAR protocol remains frozen at v0.1 unless a separate, explicit protocol artifact says otherwise. A SettlementWitness receipt sets receipt_profile to name itself explicitly — it never claims to be a sar.v0.1 receipt, and the two vocabularies below (reason codes especially) are not interchangeable.

Profile identifiers

receipt_version on both profiles is currently "0.2-candidate".

Generic signed-core field set — settlement-witness-verified-v0.2

Exactly these 9 fields form the signed core (field set is what matters; JCS sorts keys, so the order below is illustrative, not significant to the hash):

FieldTypeNotes
receipt_profilestringAlways "settlement-witness-verified-v0.2" on this profile.
receipt_versionstring"0.2-candidate".
sig_algstringAlways "Ed25519".
verdictstringOne of PASS, FAIL, INDETERMINATE. See Verdicts.
reason_codestringSee Reason codes.
propertiesobjectProperty-level evaluation state, as computed by the evaluator.
verification_basisobjectThe complete evaluation basis (see below) — everything the verdict is grounded in.
verifier_kidstringKey id of the signing key; resolve via /.well-known/sar-keys.json.
tsstringEvaluation timestamp (verification_basis.evaluated_at).

Counterparty-bound signed-core field set — settlement-witness-verified-v0.2-counterparty-bound

The same 9 fields above, plus exactly one more, required and non-empty:

FieldTypeNotes
counterpartystringCaller-supplied identifier, required and non-empty on this profile. Cryptographically bound into the signed core: changing it after issuance invalidates both receipt_id and the signature. It is not an attestation that Default Settlement verified ownership, control, or identity of the identifier, and it is not a wallet-state check — it proves only that the identifier could not have been swapped after issuance.

The verification_basis object

Exactly these 9 fields, all required, all non-empty strings (part of the signed core on both profiles above, nested under verification_basis):

FieldNotes
evaluator_idIdentity of the evaluator that produced the verdict.
evaluator_versionVersion of that evaluator.
evaluation_profileWhich supported evaluation profile was used.
policy_refReference to the policy the evaluation was run under.
condition_digestsha256:<64 hex> — digest of the declared acceptance condition.
evidence_profileThe evidence profile offered. Not restricted to a fixed allowlist here — an unsupported evidence profile is a signable INDETERMINATE verdict (see reason codes), not a request-shape error.
evidence_digestsha256:<64 hex> — digest of the evidence actually evaluated.
request_idIdentifier of the originating request.
evaluated_atTimestamp of evaluation; duplicated at the top level as ts.

Verdicts

Three verdicts are signable: PASS, FAIL, INDETERMINATE. A fourth internal state, EVALUATOR_TIMEOUT, exists in the evaluator's own vocabulary but is deliberately not signable — the signer refuses to issue a receipt for it (no receipt, no signature, rather than a receipt asserting a state its own basis can't justify).

Reason codes (current production)

Different vocabulary from portable SAR v0.1

SPEC_MATCH / SPEC_MISMATCH and the rest of the reason-code list on the portable SAR v0.1 reason-code registry are genuine, valid codes for that separate, older, frozen protocol. They are not stale and were not rewritten. They are simply a different vocabulary that SettlementWitness receipts (this page) do not use. Do not mix the two registries when implementing a verifier — check which receipt_profile (or its absence, for legacy portable receipts) you are looking at first.

receipt_id and canonicalization

  1. Take the signed-core object exactly as defined above for the receipt's receipt_profile (9 fields generic, 10 counterparty-bound) — nothing more, nothing less.
  2. Canonicalize it with JCS, RFC 8785 (recursive key-sorting, no whitespace).
  3. SHA-256 the canonical UTF-8 bytes. receipt_id is "sha256:" + hex(digest).

Signature verification

The unsigned _unsigned block

Receipts also carry receipt_id, sig, and a _unsigned object (currently just {"checks": [...]}, per-check detail) outside the signed core. _unsigned is inspectable but not part of what the signature attests — never verify it, and never treat its absence or alteration as invalidating the receipt.

Relationship to /attest's outer JWS envelope

/settlement-witness/attest's HTTP response wraps the receipt above in a separate outer response envelope that additionally carries a compact JWS (jws field, EdDSA, over the envelope payload minus _unsigned). This is a second, independent signature over different bytes than sig above. Verifying one does not verify the other. Full detail: /integrate.

Relationship to receipt lookup

GET /settlement-witness/receipt/{receipt_id} returns the flat receipt object described on this page (not the /attest response envelope, no jws field). Verify it exactly as described above. No auth required to look up a receipt by its id. Full detail: /integrate.