Independent Verification

SAR receipts are designed for offline, independent verification. No callbacks. No ongoing trust assumptions beyond the verifier’s public key — initial key resolution depends on the discovery endpoint. This page walks through the complete verification process using the canonical test vector.

The receipt to verify

This is the canonical SAR PASS test vector from the conformance fixtures:

PASS test vector
{
  "receipt_version": "0.2",
  "receipt_id": "sha256:e1624bdab7c999d3c300ddcf2bc8766270e29b5ca007be7a085cd0e21baa4676",
  "task_id_hash": "sha256:fixture-task-001",
  "verdict": "PASS",
  "confidence": 1,
  "reason_code": "SPEC_MATCH",
  "ts": "2026-02-14T12: 00: 00Z",
  "verifier_kid": "sar-ed25519-test-01",
  "sig_alg": "Ed25519",
  "sig": "base64url:hXfWolp9pl9yaHoAsP2kbdGWeZYYdqq0xe3t5wefYClhv1vBxeDg5sa1MHlRkfOQNtaAz1ucPmEheyw4ZWpPDg"
}
1

Extract and canonicalize the core fields

Extract the 6 core fields and serialize them using JCS (RFC 8785). JCS produces a deterministic byte sequence with lexicographically sorted keys — no whitespace, no trailing commas, consistent encoding.

Input: 6 core fields
{
  "task_id_hash": "sha256:fixture-task-001",
  "verdict": "PASS",
  "confidence": 1,
  "reason_code": "SPEC_MATCH",
  "ts": "2026-02-14T12: 00: 00Z",
  "verifier_kid": "sar-ed25519-test-01"
}
Output: JCS canonical form
{"confidence": 1,"reason_code": "SPEC_MATCH","task_id_hash": "sha256:fixture-task-001","ts": "2026-02-14T12: 00: 00Z","verdict": "PASS","verifier_kid": "sar-ed25519-test-01"}

Note how the keys are sorted alphabetically: confidence, reason_code, task_id_hash, ts, verdict, verifier_kid. This is the JCS canonical ordering.

2

Derive the receipt_id

Compute the SHA-256 hash of the canonical JSON bytes. The result, prefixed with "sha256:", is the receipt_id.

Input: canonical JSON bytes

166 bytes of UTF-8 encoded JCS output

Output: receipt_id

sha256:e1624bdab7c999d3c300ddcf2bc8766270e29b5ca007be7a085cd0e21baa4676

Comparison: Does the computed receipt_id match the claimed receipt_id in the receipt?

MATCHThe receipt_id is authentic — the core fields have not been modified.
3

Resolve the public key

Look up the verifier's public key using the verifier_kid field. Keys are published at the verifier's well-known endpoint.

Key ID to resolve

sar-ed25519-test-01

Discovery endpoint

GET https://verifier.example.com/.well-known/sar-keys.json

// Response:
{
  "keys": [
    {
      "kid": "sar-ed25519-test-01",
      "kty": "OKP",
      "crv": "Ed25519",
      "x": "<base64url-encoded-32-byte-public-key>"
    }
  ]
}

Find the key entry where kid matches verifier_kid. Decode the x field from base64url to get the 32-byte Ed25519 public key.

4

Verify the Ed25519 signature

Verify the Ed25519 signature over the 32-byte SHA-256 digest using the resolved public key.

Inputs

Message: 32-byte SHA-256 digest of canonical JSON
Signature: base64url:hXfWolp9pl9yaHoAsP2kbdGWeZYYdqq0xe3t5wefYClhv1vBxeDg5sa1MHlRkfOQNtaAz1ucPmEheyw4ZWpPDg
Public key: 32-byte Ed25519 key from SAR keys document
VALID

The signature is valid. The receipt was signed by the holder of the private key corresponding to sar-ed25519-test-01.

Verification result

Receipt verified

1. Core fields are intact (receipt_id matches recomputed hash)

2. Signature is valid (Ed25519 verification passed)

3. Verdict: PASS with confidence 1.0

What verification proves

Verification proves

  • The core fields have not been modified since signing
  • The receipt was signed by the holder of the claimed key
  • The receipt_id is deterministically derived from the core fields

Verification confirms receipt integrity and signer identity. Whether the underlying verdict is correct depends on the verifier’s evaluation policy — that judgment lives above the receipt layer.

Verdict semantics

PASS

All settlement conditions were met. The delivered result satisfies the task specification.

Example: A code generation task where the output compiles, passes tests, and matches the acceptance criteria.

FAIL

One or more settlement conditions were not met.

Example: A translation task where the output is in the wrong language or exceeds the specified word count.

INDETERMINATE

Settlement conditions could not be fully evaluated.

Example: A verification that timed out before completing, or where required evidence was unavailable.

Verify with the SDK

The SAR SDK implements the complete verification flow in TypeScript:

npm install sar-sdk
import { verifyReceipt, resolveKidFromWellKnown } from 'sar-sdk';

const receipt = { /* ... SAR receipt ... */ };

// Create a key resolver that fetches from well-known endpoint
const resolveKey = (kid) =>
  resolveKidFromWellKnown('https://verifier.example.com', kid);

// Verify — throws on failure, returns true on success
const valid = await verifyReceipt(receipt, resolveKey);