Keep a copy of the response you already paid for
The seller seals it to the public key your payment already revealed and you recover it later with the same wallet — 90 days by default, and it can never fail the payment it rides on.
The idea, in one sentence
A payment authorization is a signature, and a signature yields the signer's public key — not merely their address. So paying is publishing your encryption key. There is nothing to register and no extra round trip: by the time the seller has been paid, it already holds everything it needs to encrypt the response to the one wallet that is entitled to read it.
Four of the seven chain families need nothing but the address, because Solana, Stellar, Algorand and NEAR are ed25519 and the address is the key. EVM and XRPL recover it from the signature. Sui reads it out of the signature envelope. No family needs a step the buyer has to remember to take.
Where each piece runs
The facilitator is not in the response path. It sees /verify and /settle and never a body, so it could not encrypt one even if it wanted to. That constraint decides the whole architecture:
| piece | runs | holds |
|---|---|---|
x402-axum the seller hook |
in the seller, at the one point where the delivered body and the settlement identity both exist | the plaintext. It encrypts and uploads. |
src/dx402/ this facilitator |
notary and index: signs EIP-712 receipts, records pointers | in direct mode, neither plaintext nor key material |
x402-reqwest the buyer |
fetches, decrypts and checks contentHash |
the private key, which never leaves that side |
The rules that are load-bearing
DX402 can never fail a payment. Every failure degrades to a reason carried in the X-Durable-Evidence header — too_large, anchor_failed, no_payer_key, disabled — and the payment goes through regardless. An extension that can take down settlement is not an extension, it is a liability.
contentHashis over the PLAINTEXT. Over the ciphertext it would only prove the blob was not corrupted. Over the plaintext it proves the anchor decrypts to what was actually delivered — which is the entire claim.paymentIdis the AEAD associated data, and it iskeccak256(caip2Network || txHash). Derive it differently on either side and decryption fails with nothing to point at.- Anchoring is publishing. Retention defaults to 90 days on purpose. A permanent, irrevocable anchor is a decision the buyer has to make, not a default someone inherits.
- Small-order ed25519 keys are rejected, in constant time (RFC 7748 §6.1).
ed25519-dalekaccepts non-canonical and small-order encodings, and unchecked that collapses the shared secret to a constant. The check is tested against libsodium's seven-value blacklist — not against invented vectors, which is exactly how three fabricated hashes once passed CI for months by being compared only to themselves. - Domain resolution is not duplicated. DX402 calls the facilitator's own EIP-712 metadata lookup rather than keeping a second copy, because a second copy drifts and then silently recovers a different — perfectly valid — public key.
The backends, and which one is on
Read from /dx402/stats, not described here. A backend that is provisioned is not the same as a backend that is switched on, and the difference is the whole point of the column that says so.
| backend | retention | revocable | public | enabled |
|---|---|---|---|---|
| loading… | ||||
The endpoints
| route | what it does |
|---|---|
POST /dx402/anchor | record a pointer and sign its receipt |
GET /dx402/evidence/{paymentId} | what is anchored for that payment |
GET /dx402/receipt/{paymentId} | the signed EIP-712 attestation |
GET /dx402/blob/{paymentId} | the sealed bytes. The store is private: a pointer resolves through here, never through a public bucket, and it addresses the payment rather than a storage key so old pointers survive a re-layout |
GET /dx402/stats | the four numbers at the top of this page |
POST /dx402/recover | not implemented in v0.1 — answers 501 rather than pretending |
Where this stands
Running, and counted above. It is not a proposed x402 standard: the Foundation asks for a reviewed pull request, and a proposal with no production traffic behind it gets discarded. The order is traffic first, proposal second. The specification, the research and the prior-art survey are in the repository, and the handoffs for the four products already wired to it are next to them.