Skip to main content
A claim is the verifier’s signed statement that a policy held for the user’s account. It is the only thing a consumer reads. A consumer never re-derives facts from receipts.

Fields

Outputs come only from what the circuit proved: Bool(true) for a predicate that held, a bucket index, or a policy constant. A raw field value never becomes an output, so no payment detail leaves the phone. A Pesa payment claim carries one output, satisfied.

The context digest

For a Pesa order the context is order_id, expected_amount, currency, payee_id_hash, payer_id_hash and reference. The orchestrator rebuilds the same context from on-chain data and compares digests.

The nullifier

The circuit computes it, so the prover cannot choose it. It never includes a nonce, a blinder or an order ID. The same real-world payment always gives the same nullifier, even across a recapture, so it can be spent once. For a payment the scope is the rail and the field is the provider’s transaction ID.

Canonical encoding

Every hash input starts with a versioned ASCII domain, then each field in order. Variable-width fields have a 4-byte big-endian length prefix; fixed-width fields are raw. A decoder must refuse anything that does not re-encode to itself, so a claim has exactly one byte form. The receipt digests are ordered K then T and must never be sorted. The transport form is the signed bytes followed by the 64-byte signature, base64-encoded as claim.encoded in the SDK.

Verifying a claim

A consumer checks, in order:
  1. Signature. Ed25519 over the canonical bytes (no prehash) by a claim key the trust bundle lists for verifier_epoch, on a verifier that is not revoked, in a valid epoch.
  2. Time. issued_at at most 60 seconds ahead of the consumer’s clock, and expires_at not passed.
  3. Holder binding. Refuse a bound claim unless the consumer itself checked a holder proof.
  4. Policy. profile_id and policy_id are the ones asked for, and the outputs say what is needed.
  5. Nullifier. If a fact may be proved once, refuse a nullifier already seen.
The zkTLS SDK does checks 1 to 3 on the phone. Backend libraries in TypeScript, Go and Rust do them on a server: Verify claims on your backend. On Stellar, the claims contract does them.