# AP2 Attestation Binding Profile

**Version 0.1.0-draft.1 — 2026-08-20**
**Binds:** Agent Payments Protocol (AP2) mandate chain — intent, cart, payment
**Publisher:** Rubric Protocol (rubric-protocol.com)
**Status:** Draft. Pinned to the AP2 mandate model as published at time of
writing; will re-pin on AP2 releases. This profile references mandates **by
hash only** and never by AP2-internal field name, so it survives AP2 field
renames and does not restate AP2 structure it does not own.

## 1. Purpose

AP2 establishes what a user authorized: an intent mandate, a cart mandate and
a payment mandate, each signed, forming a delegation chain from human to agent
to merchant. The AP2 specification is candid about what that chain does not
establish, naming three open problems:

- **Temporal gap.** A mandate may be minted long before it is executed.
  Nothing in the chain evidences *when* execution occurred relative to
  authorization.
- **Indirect trust.** Trust in the counterparty is conferred by curated
  allow-list membership rather than by evidence a verifier can check.
- **Agent identity.** The chain shows an agent was authorized. It does not
  show *which* agent key actually acted at execution time.

Each is an evidence problem, not a payment problem, and each is answerable
with an artifact the parties already produce. This profile binds an AP2
mandate chain to a Verifiable Settlement Receipt (VSR v0.4) and to a
post-quantum-signed, ledger-anchored Rubric attestation, so that the three
questions above are answered by verification rather than by assertion.

The binding is optional and backward compatible. An AP2 processor that does
not understand this profile MUST ignore the extension member and MUST NOT
reject the mandate for its presence.

## 2. Extension member

A bound mandate chain carries one additional member on the payment mandate:

~~~json
"rubric_binding": {
  "profile": "ap2-binding/0.1",
  "mandate_chain": {
    "intent":  { "hash": "sha3-256:<hex>", "issued_at": "<RFC 3339>" },
    "cart":    { "hash": "sha3-256:<hex>", "issued_at": "<RFC 3339>" },
    "payment": { "hash": "sha3-256:<hex>", "issued_at": "<RFC 3339>" }
  },
  "chain_hash": "sha3-256:<hex>",
  "vsr": { "receiptId": "<uuid>", "payloadHash": "sha3-256:<hex>" },
  "agent": {
    "publicKey": "<base64>",
    "algorithm": "ML-DSA-65",
    "verified": true
  },
  "attestation_id": "<rubric attestation id>",
  "anchor": {
    "network": "hedera-mainnet",
    "hcs_topic": "0.0.10416909",
    "hcs_seq": <int>,
    "consensus_timestamp": "<RFC 3339>"
  },
  "verify_url": "https://rubric-protocol.com/v1/verify/<attestation_id>"
}
~~~

All members REQUIRED except `anchor.hcs_seq` and `anchor.consensus_timestamp`,
which MAY be absent for up to five minutes after write (asynchronous anchoring
window). A binding that still lacks an anchor after that window verifies as
`unresolvable`, never as `bound-verified`.

`agent.verified` MUST be `true` or the member MUST be omitted entirely. A
recorded-but-unverified agent signature in anchored evidence would imply
authorship it does not prove.

## 3. Mandate hashes and canonicalization

Circularity rule: the payment mandate hash covers the mandate **excluding**
the `rubric_binding` member itself.

    mandate_hash(m) = "sha3-256:" || hex( SHA3-256( 0x10 || JCS(m) ) )

where JCS is RFC 8785 canonical JSON as profiled in VSR v0.4 §4, and `0x10` is
the domain tag for an AP2 mandate. Producers MUST NOT place floating-point
values in a hashed mandate; monetary values are strings in atomic units, per
VSR v0.4 §3.

The chain hash binds the three mandates **in order**, so a cart cannot be
substituted under an intent that did not authorize it:

    chain_hash = "sha3-256:" || hex( SHA3-256(
        0x11 || intent_hash || cart_hash || payment_hash ) )

Hashes are concatenated as their lowercase hex bytes without separators, in
the fixed order intent, cart, payment. A chain missing a mandate is not
bindable under this profile; all three are REQUIRED.

## 4. Leaf construction

One leaf per settled mandate chain, leafType `ap2-mandate-chain-v1`:

~~~json
{
  "leafType": "ap2-mandate-chain-v1",
  "ap2_chain_hash": "sha3-256:<hex>",
  "vsr_payload_hash": "sha3-256:<hex>",
  "agent_public_key": "<base64>",
  "settled_at": "<RFC 3339>"
}
~~~

Leaves are batch-attested through the Rubric tiered path, Merkle-aggregated
(SHA3-256), federation-signed (3-of-5 independent ML-DSA-65 keys) and
HCS-anchored. `leafType` is producer-chosen, so this profile is emittable
against the deployed API without protocol changes.

Note the leaf commits to the chain hash and the VSR payload hash, not to
mandate contents. Cart contents, prices and user identity stay off the ledger;
what is anchored is a commitment that this chain settled this way at this
time.

## 5. What the binding establishes

Each of AP2's three named weaknesses maps to a specific check.

**Temporal gap.** The anchor carries a Hedera consensus timestamp, assigned by
a network with no stake in the transaction. Combined with the mandate
`issued_at` values, the authorization-to-execution interval stops being an
assertion and becomes a measured quantity:

    execution_gap = anchor.consensus_timestamp − max(intent, cart, payment issued_at)

A verifier MUST report this interval. A policy MAY reject a binding whose gap
exceeds a threshold. The profile deliberately sets no threshold: what counts
as stale authorization is a business decision, and a spec that guessed would
be wrong for most of its users.

**Indirect trust.** Verification requires the receipt, the published
federation public keys and a public mirror node. It does not require
allow-list membership, an account with the merchant, or the merchant's
cooperation at verification time. Allow-lists remain useful for deciding whom
to transact with; they stop being load-bearing for proving what happened.

**Agent identity.** `agent.publicKey` is the key whose signature the settling
server verified over the request, and it enters the anchored leaf only when
that verification succeeded. The chain therefore names the key that acted, not
merely a key that was authorized to act. Where an intermediate agent re-signs
on behalf of another, each hop that produces its own settlement produces its
own binding, and the bindings share the intent mandate hash — which is what
makes a delegation chain reconstructible after the fact.

## 6. Verification procedure

1. Extract `rubric_binding`; remove it from the payment mandate.
2. Recompute `intent_hash`, `cart_hash`, `payment_hash` per section 3 and
   compare to `mandate_chain`. Any mismatch is `hash-mismatch`: a mandate was
   altered after signing.
3. Recompute `chain_hash` and compare. A mismatch here with individually
   matching mandates means the chain was **reordered or recombined** — the
   substituted-cart case.
4. Resolve `vsr.payloadHash` against the receipt held by the buyer; confirm
   the receipt's `payment.reference` settled on the named network.
5. Resolve `verify_url`, or verify offline against published federation keys
   and an HCS mirror. Require signature quorum, Merkle inclusion and anchor
   consistency.
6. Compute and report `execution_gap` per section 5.

Result tiers: `bound-verified` | `hash-mismatch` | `chain-mismatch` |
`anchor-mismatch` | `unresolvable`. A verifier MUST NOT collapse
`chain-mismatch` into `hash-mismatch`: they have different causes and
different remedies.

`anchor-mismatch` is returned when the resolved attestation reports a
sequence number that disagrees with `anchor.hcs_seq`, **or** when the binding
claims a sequence the attestation does not yet report. Treating the second
case as acceptable would let any claimed sequence pass for as long as the
attestation stays pending. The producer controls
the binding, so a claimed sequence number evidences nothing until it agrees
with the resolved attestation; a verifier that checks only that an anchor is
*present* accepts any integer a producer cares to write.

Reference verifier: https://rubric-protocol.com/docs/verify-ap2.py

## 7. Relationship to VSR v0.4 and x402

This profile does not modify VSR. A VSR receipt is produced by the settling
server for any x402 payment; the binding adds the mandate-chain commitment
that x402 alone has no place to carry. Where AP2 runs over x402 — as in the
published AP2 + x402 sample — the two artifacts are produced by the same
settlement and share the `payment.reference`.

Where AP2 runs over a non-crypto rail, `vsr.payloadHash` MAY be omitted and
the leaf binds the chain hash alone. Such a binding still answers the temporal
and identity questions; it does not evidence settlement.

## 8. Considerations

The extension namespace `rubric_binding` is claimed by this profile.
Producers MUST NOT emit it without a resolvable attestation.

This profile evidences that a mandate chain existed and settled as recorded.
It does not evidence that the user understood the mandate, that the agent
behaved reasonably within it, or that the merchant delivered value. Those are
different questions and this document does not answer them.
