Settlement evidence family

Mandate Cross Reference Receipt: performing the join that two payment specifications require and assign to nobody

The Mandate Cross Reference Receipt is a signed statement that two committed artifacts, each attested by a distinct registered attestor key, each carrying a keyed digest of one declared field value under a window salt committed before either artifact was presented, met or failed the declared equality relation, and that the outcome was recomputed from the two commitments rather than supplied. Payment schemes, merchants, acquirers and agent operators need it, because two published specifications split their evidence on purpose and then name no party able to join it.

What makes it different from a log is that neither half is opened. A reconciliation report is written by somebody who holds both halves, which is exactly the position the split was designed to prevent. This receipt compares two keyed digests, each committed by a different attestor under a salt fixed in advance, and recomputes the equality outcome itself. A mismatch is a valid receipt, not an error. The party that joins the halves holds neither artifact, and it can prove that it never held them.

The problem

An agent buys something. The credential that says the agent was allowed to buy it gets split in two, on purpose. One half goes to the payment network, the other half goes to the merchant. Each half carries only the disclosures relevant to the party that receives it, which is good privacy design and is the reason the split exists.

The Verifiable Intent specification, published at https://verifiableintent.dev/spec/, splits the agent credential into an L3a for the payment network and an L3b for the merchant. Each carries an sd_hash the specification describes as "computed over the L2 base JWT concatenated with only the disclosures relevant to that L3". It then states, in its own words, that "Cross-reference verification requires a Verifier with both L3 credentials". The check itself is one comparison, L3a.transaction_id == L3b.checkout_hash. The specification requires it and designates nobody who holds both sides.

The AP2 specification, published at https://ap2-protocol.org/ap2/specification/, has the same shape in its dispute path. It says that its four dispute objects together "provide a non-repudiable picture of the transaction" and that they "can be brought together". It does not say by whom.

Read those two statements plainly. The comparison that ties the payment side of a transaction to the checkout side is required, and no designated party is positioned to run it. Nothing here is a criticism of either specification. The gap is a structural consequence of getting the privacy right.

Now name the parties in the dispute that follows. A cardholder says the purchase was not the one she authorised. The merchant holds its half of the credential and says its half is consistent. The payment network holds its half and says the same. Each of those statements can be true at once, because each party is describing a different document.

Whoever wants the comparison run has one option today, which is to ask one of the two parties to hand its half to the other. That party then holds both halves, which is the state the split was written to avoid, and it also has an interest in the answer. Nobody accepts a reconciliation from a counterparty when money is on the line.

The autonomy of the buyer sharpens this. A human buyer generates a handful of transactions and remembers them. An agent generates thousands, at machine speed, under an authority that has been narrowed at each hop. When a dispute lands weeks later, the only artifacts are two halves in two places.

The second failure is timing. If a party can pick the digest key after it has seen an artifact, it can pick a key that makes any two values look related. The comparison is only evidence if the key was fixed before either half was presented. That is not something either attestor can assert about itself.

What the receipt binds

The schema is at https://thehiveryiq.com/.well-known/schemas/mandate-crossreference-v1.json. The signed body has receipt_type, schema, the boundary, and one object, mandate_crossreference.

FieldMeaning
crossreference_idIdentifier matching mx_ plus 16 to 64 lowercase hex characters.
recorded_atThe UTC instant this record was written.
cross_reference_field_nameThe one field being compared, as a lowercase name up to 64 characters.
window_salt_committed_atThe instant the window salt was committed.
delegation_layer_commitmentKeyed digest of the delegation layer both artifacts descend from.
merchant_side.attestor_roleThe fixed label merchant_side_attestor.
merchant_side.attestor_key_idThe decentralised identifier of the merchant side attestor key.
merchant_side.attestor_algorithmThe fixed value ed25519.
merchant_side.attestor_sig_b64uThat attestor's signature over its own side.
merchant_side.artifact_classOne of merchant_credential, checkout_mandate, cart_mandate or merchant_dispute_object.
merchant_side.artifact_commitmentKeyed digest of the whole merchant side artifact record.
merchant_side.cross_reference_value_commitmentKeyed digest of the merchant side value of the declared field.
merchant_side.presented_atThe instant that half was presented.
payment_side.artifact_classOne of payment_network_credential, payment_mandate, network_token_record or payment_dispute_object.
payment_side.*The fixed label payment_side_attestor and the same seven remaining fields as the merchant side.
artifact_relationEither distinct_artifact_classes or same_artifact_class.
equality_outcomeEither cross_reference_match or cross_reference_mismatch.
precedence_classEither salt_committed_before_both_artifacts or salt_committed_after_an_artifact.
observer_roleThe fixed label non_party_joiner.
evidence_access_classOne of confidential_independent, issuer_only or public_commitment_only.
salt_commitmentCommitment to the 32 byte window salt.

The two sides sit in separate objects with their own attestor key, their own signature and their own instant. That separation makes the equality check a comparison of two things rather than a restatement of one thing twice.

What is deliberately not in the signed body

No artifact, no field value, no delegation layer record, no window salt. The receipt holds digests, one field name, two attestor key identifiers, two signatures, three instants and enumerated classes.

Four constructions in src/typed/mandate-crossreference.js do the work. The salt commitment is a plain SHA-256 over the UTF-8 label mandate.crossreference/salt/v1, a zero byte, and the raw 32 salt bytes.

The artifact commitment is HMAC-SHA256 keyed by the raw salt bytes, over the domain label mandate.crossreference/artifact/v1 followed by a zero byte and then the canonical JSON of an object holding just the artifact record. The record must hold exactly artifact_id, artifact_class, content_sha256, cross_reference_field_name and cross_reference_field_value, with content_sha256 required to be 64 lowercase hex characters. The record has no field that could carry the artifact itself.

The cross reference value commitment is HMAC-SHA256 under the label mandate.crossreference/cross-reference-value/v1, over the canonical JSON of an object holding the field name and the field value together. Binding the name into the keyed digest is what stops one side committing the value of some other field and still matching.

The delegation layer commitment is HMAC-SHA256 under the label mandate.crossreference/delegation-layer/v1, over the canonical JSON of the delegation layer record. That record must hold exactly delegation_layer_id, a layer_class drawn from agent_credential_layer, mandate_layer and token_control_layer, and layer_content_sha256. The join only means something inside one delegation layer, so both sides commit to the same layer and the service recomputes it.

Canonical JSON is the shared rule: keys sorted recursively, arrays kept in order, no whitespace, and every code unit above U+007F escaped as a lowercase \uXXXX sequence, then digested as lowercase hex SHA-256 over the UTF-8 bytes.

Each attestor signs its own side over a fixed ASCII string built from crossreference-attest, the cross reference identifier, its own role label, the declared field name, its artifact class, its artifact commitment, its value commitment, its presentation instant, the delegation layer commitment and the salt commitment. The role label stops a signature being moved to the other side, and the salt commitment stops it being replayed into a later window.

The receipt envelope uses the shared Ed25519 construction over the ASCII string hive-receipt <receipt_id> <payload_sha256> <ts>, with hive-receipt as the domain separation label.

The gates

Verification runs in this order and stops at the first failure.

  1. SCHEMA. Validates the envelope against the cross reference v1 schema, including the boundary constant, the two role constants and the two disjoint artifact class lists. A failure means the artifact is malformed.
  2. ISSUER_KEY_MATCH. Resolves envelope.key_id in the trusted keyring and checks the key was authorised for this receipt type at the envelope timestamp. A failure means the signer is not trusted here.
  3. PAYLOAD_DIGEST_VALID. Recomputes the canonical digest of signed_body against payload_sha256. A failure means the body was altered after signing.
  4. SIGNATURE_VALID. Requires Ed25519 and verifies the envelope signature with the resolved key. A failure means that key did not produce it.
  5. RECORD_INSTANT_ORDERED. Requires all four instants to be valid UTC and both presentations to be at or before recorded_at. A failure means the record describes something that had not happened yet.
  6. SALT_BINDING. Requires the disclosed salt to be 64 lowercase hex characters and to recompute to salt_commitment. A failure means none of the keyed digests can be checked.
  7. WINDOW_SALT_PRECEDENCE. Recomputes the precedence class with strict inequality against both presentation instants, then fails outright when the salt came after an artifact. A failure means the salt was fixed with an artifact already in hand.
  8. CROSS_REFERENCE_FIELD_DECLARED. Requires both artifact records to hold exactly the five allowed fields, to name the one declared field, and to carry the class in their own signed side. A failure means the sides are not comparing the same field.
  9. DELEGATION_LAYER_BINDING. Requires the delegation layer record to hold exactly its three allowed fields, and recomputes its commitment. A failure means the halves are not being joined inside one delegation layer.
  10. MERCHANT_SIDE_ARTIFACT_INTEGRITY. Recomputes the keyed digest of the merchant artifact record against merchant_side.artifact_commitment. A failure means the record supplied is not the record committed.
  11. PAYMENT_SIDE_ARTIFACT_INTEGRITY. The same check against the payment artifact record.
  12. MERCHANT_SIDE_VALUE_COMMITMENT_RECOMPUTE. Recomputes the keyed digest of the merchant side field value under the declared field name. A failure means the compared value was supplied rather than derived.
  13. PAYMENT_SIDE_VALUE_COMMITMENT_RECOMPUTE. The same check on the payment side.
  14. ATTESTOR_ROLE_DISTINCTNESS. Requires both fixed role labels and each artifact class in its own side's list, and refuses two sides naming one attestor key or an issuing key named as an attestor. A failure means one signer holds both halves.
  15. ATTESTOR_SIGNATURE_VALID. Requires a supplied attestor registry with each key registered for its own role, and verifies each side's Ed25519 signature over its reconstructed signing string. A failure means the side is not what that attestor signed.
  16. ARTIFACT_RELATION_RECOMPUTE. Recomputes the relation from the two artifact classes and fails outright on same_artifact_class. A failure means this is one record compared against itself.
  17. EQUALITY_OUTCOME_RECOMPUTE. Recomputes the outcome from the two value commitments this service derived. A mismatch passes. A failure means the outcome was asserted rather than computed.
  18. OBSERVER_ROLE_DECLARED. Requires the observer role to be the fixed label non_party_joiner. A failure means the joining role was edited.
  19. NO_ARTIFACT_CONTENT_LEAK. Walks every string in the signed body against its approved opaque form: identifier patterns, 64 character hex digests, enumerated classes. A failure means something readable reached an opaque field.
  20. MANDATECROSSREFERENCE_BOUNDARY_PRESENT. Requires the boundary text to equal the fixed string. A failure means the non attestation was edited or removed.

Mint refuses every field this list recomputes, from both artifact commitments through to the salt commitment. It also refuses to sign a salt committed after an artifact was presented, two identical attestor keys, a request with no attestor registry, and an attestor signature it cannot verify.

The boundary

This receipt attests only that two committed artifacts, each attested by a distinct registered attestor key, each declaring one cross reference field name and one keyed digest of that field value under a window salt committed before either artifact was presented, met or failed the declared equality relation, and that the equality outcome was recomputed by this service from the two commitments rather than supplied by the caller. It does not attest that either artifact is genuine, complete, or unaltered before commitment. It does not attest that either attestor is entitled to hold the artifact it committed. It does not attest that the transaction occurred, was authorised, settled, or was proper. It does not attest that the cross reference field is the right field for any purpose. A mismatch outcome does not mean that either side is wrong, and this receipt allocates no fault. It discloses neither artifact, neither field value, nor the salt.

That string is a schema constant inside the signed bytes and it is checked again by its own gate.

The clause that costs the most is the last one about fault. A mismatch is the strongest thing this instrument produces, and the boundary says in the signed bytes that a mismatch does not mean either side is wrong. That sounds like giving away the value. It does the opposite. A mismatch has innocent causes, including a truncation on one side, a normalisation difference, or a replayed cart. If the receipt claimed a mismatch proved misconduct, the first party blamed unfairly would attack the receipt rather than explain the cause. Because the receipt allocates nothing, a mismatch becomes a fact both sides can accept and then argue about.

The clause about genuineness points the same discipline at the input. If a merchant committed a doctored artifact, the digest of the doctored artifact is what the receipt compares, and it says so.

The clause about the field is the most practical. This receipt records the declared field name and compares that field faithfully. Whether it is the right field for a given purpose is a question about the protocol, and the receipt refuses to answer it.

Adversarial cases

These are the real cases in test/mandate-crossreference.test.mjs, which holds 56 tests, all passing.

A window salt committed after an artifact was presented fails WINDOW_SALT_PRECEDENCE, and a salt committed exactly at the merchant presentation instant fails the same gate, because the requirement is strict inequality. Both are refused at mint.

A match claimed over two differing values fails EQUALITY_OUTCOME_RECOMPUTE, and a mismatch claimed over two equal values fails the same gate. The outcome is recomputed from the two commitments the service derived, so neither direction of forgery survives.

One attestor key on both sides fails ATTESTOR_ROLE_DISTINCTNESS. This is the shape the split was designed to prevent, and mint refuses it too. The issuing key named as an attestor fails the same gate, so the joining party cannot attest a half it joins. A payment side carrying a merchant artifact class fails that gate too, because each side is checked against its own class list.

A missing attestor registry fails ATTESTOR_SIGNATURE_VALID. No attestor signature is taken on trust. An attestor registered for the other role fails the same gate, and so does a merchant signature moved to the payment side, because the role label is inside the signed string.

An edited merchant artifact record fails MERCHANT_SIDE_ARTIFACT_INTEGRITY, and the payment side has its own gate for the same edit. A forged merchant value commitment fails MERCHANT_SIDE_VALUE_COMMITMENT_RECOMPUTE, and again the payment side has its own.

An artifact record naming a different field fails CROSS_REFERENCE_FIELD_DECLARED, and so does a record whose class disagrees with the signed class. An edited delegation layer record fails DELEGATION_LAYER_BINDING, and so does a record from another layer class, so a join cannot be assembled across two delegation layers.

A substituted salt fails SALT_BINDING, a raw field value in a signed field fails NO_ARTIFACT_CONTENT_LEAK, and a boundary mismatch fails MANDATECROSSREFERENCE_BOUNDARY_PRESENT.

Four positive cases carry the instrument. Two halves that agree return cross_reference_match. Two values that disagree return cross_reference_mismatch and the receipt stays valid, which is the case the dispute path needs. A merchant dispute object joined to a payment dispute object verifies on its own artifact classes. A checkout mandate against a payment mandate verifies.

Structural tests confirm the design. The minted envelope carries neither artifact, neither field value, nor the salt. Value commitments are salt scoped and never repeat across salts. The same value under a different field name commits differently. The classifiers are pure functions of their inputs, and the two artifact class lists are disjoint. Mint refuses every computed field at both the request level and the nested side level.

Performance

From hive-verifier-api/benchmarks/results-latest.json, measured at 2026-08-10T08:00:47.634Z at commit a22b4d88f6e041d6f593381b6e310a8d527d5b4b over 200 iterations:

MeasureValue
Mint p506.453 ms
Mint p958.025 ms
Verify p505.753 ms
Verify p956.202 ms
Artifact size3044 bytes

Local harness measurement on one machine and one Node version. The run recorded Node v20.20.1 on linux/x64 with two Intel Xeon cores at 2.90 GHz. It is a reproducible measurement of this code, not a production latency guarantee. This is one of the more expensive instruments in the set, and the reason is in the gate list. Both mint and verify perform two extra Ed25519 verifications, one per attestor side, on top of the envelope signature. A join that trusted a supplied attestor signature would prove nothing about who committed which half. The cost does not grow with the size of the two artifacts, because the artifacts never arrive.

How to run it

This type has no mint route. It is produced as an export and only verified here. The verify route takes no credentials.

The verify route is live on the production host. It is open, needs no account, and costs nothing to call.

  • Verify route: /verify/mandate-crossreference
  • Demonstration route: /demo/mandate-crossreference, carrying the keys match, mismatch, dispute_pair and hindsight_salt
curl -sS -X POST https://thehiveryiq.com/v1/verify/mandate-crossreference -H 'content-type: application/json' -d '{"receipt": <a receipt of this type>}'

The hindsight_salt case fails at WINDOW_SALT_PRECEDENCE. It cannot be minted, so it is built by minting the honest case, editing the body and re-signing with the same demonstration key.

Where it sits in the canon

Family: settlement-evidence. Category: typed receipt contract.

It is the two party join that the surrounding instruments assume somebody else has done. The Ledger Parity Receipt settles whether two parties agree on one settlement figure, over one kind of record held twice. This one compares two structurally different artifacts that were routed to two different parties on purpose, on one declared field, inside one delegation layer. The Mandate Conformance Receipt judges whether a payment fit the authority in force, and it needs the halves of that authority tied together first. The Intent Affirmation Receipt fixes that the artifact a person affirmed is the artifact transmitted, one step earlier in the same chain.

It does not replace either party's own reconciliation, and it does not decide a dispute. It reports an outcome neither party could produce without holding the other's half.

Patent pending.

Status

production_deployed.

References for this instrument
Canon entry
Mandate Cross Reference Receipt in the Hive canon
JSON Schema
https://thehiveryiq.com/.well-known/schemas/mandate-crossreference-v1.json
Verify route
POST /verify/mandate-crossreference
Canonical type
mandate.crossreference
Private by design. Hive does not store your prompts. Every request is already receipted by a one-way SHA-256 fingerprint, not the words. Proof, not surveillance.