{"description":"Conformance vectors for the x402 Signed Offers & Receipts extension, JWS format (alg EdDSA over Ed25519). Generated by scripts/generate-conformance-vectors.mjs in the scvd-general-store repo; deterministic and regenerable byte-for-byte. The signing key is a TEST key (seed = 0x42 * 32, published on purpose) — it has never signed a real artifact and is not the scvd.store production key.","version":2,"changed_in_v2":["offer_valid carried validUntil 2026-01-01, which had passed. A set published as known-good contained an EXPIRED offer marked expect:accept, so a client that correctly refuses expired offers failed our suite and would have concluded its own implementation was broken. The live vector now expires in 2100 and the expired case is its own deliberate vector.","The kid is a LABEL, not a resolvable DID URL, and v1 never said so. did:web:scvd.store publishes the STORE's key and deliberately not this test key — a test key in a production key document is a hazard, not a convenience. So a verifier that resolves the kid finds nothing and fails the valid vectors, while offer_wrong_key's own note told you to resolve it. Use signing.public_key_hex. That is stated in how_to_use now.","Added algorithm-confusion (HS256 over the public key), alg:none, a missing required field other than version, a RECEIPT schema failure (every v1 invalid case was an offer, so a verifier with no receipt validation passed the whole suite), a kid-less header, a two-segment JWS and a truncated signature."],"how_to_use":{"key":"Verify against signing.public_key_hex. Do NOT resolve the kid — see kid_is_a_label.","kid_is_a_label":"The kid reads did:web:scvd.store#conformance-test-key and does not resolve, on purpose. scvd.store's DID document publishes the store's live signing key and must never publish a test key beside it: a key document is a trust anchor, and a test key sitting in one is a foothold rather than a convenience. Treat the kid here as a stable string identifying which key signed the vector.","time":"Vectors are fixed and deterministic. The only time-dependent expectations are offer_valid (live until 2100) and offer_expired_but_wellformed (expired since 2025), and both say so in their own entry. Nothing else in this set changes verdict with the clock.","conformance":"A conformant verifier ACCEPTS every entry in `valid` and REJECTS every entry in `invalid`. reject_at names the layer we expect to catch it; rejecting EARLIER is also conformant. Accepting is never conformant."},"spec":"x402-foundation/x402 specs/extensions/extension-offer-and-receipt.md","signing":{"algorithm":"EdDSA (Ed25519)","kid":"did:web:scvd.store#conformance-test-key","test_seed_hex":"4242424242424242424242424242424242424242424242424242424242424242","public_key_hex":"2152f8d19b791d24453242e15f2eab6cb7cffa7b6a5ed30097960e069881db12","note":"Sign input is ASCII(base64url(header) + '.' + base64url(payload)) per RFC 7515 compact serialization."},"valid":[{"name":"offer_valid","expect":"accept","payload":{"version":1,"resourceUrl":"https://example.com/api/buy/widget","scheme":"exact","network":"eip155:8453","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","payTo":"0x1111111111111111111111111111111111111111","amount":"10000","validUntil":4102444800},"jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiIxMDAwMCIsInZhbGlkVW50aWwiOjQxMDI0NDQ4MDB9.I3V9b8co0MqWfOrg9NyUfw3FEx_5JZf1l4sb23XKyyNlNXIf1GTl979S1SbGfoluAtK2evPqSanpA9QBI7InAA"},{"name":"receipt_valid","expect":"accept","payload":{"version":1,"network":"eip155:8453","resourceUrl":"https://example.com/api/buy/widget","payer":"0x2222222222222222222222222222222222222222","issuedAt":1767225600,"transaction":"0xabababababababababababababababababababababababababababababababab"},"jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZWlwMTU1Ojg0NTMiLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJwYXllciI6IjB4MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMiIsImlzc3VlZEF0IjoxNzY3MjI1NjAwLCJ0cmFuc2FjdGlvbiI6IjB4YWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYiJ9.ejuDi0c6vesOF34lLIZDZZ2oAKj_Bbfnm87SsyjDWJ_uw1BOLDVZ18vj08qWC7HsKGs-ZxxlrfZc6fVWeLNPBQ"},{"name":"offer_expired_but_wellformed","expect":"accept","live":false,"note":"CORRECTLY SIGNED, SCHEMA-COMPLETE, AND PAST ITS WINDOW. Conformance and liveness are two questions and this vector is the one that separates them: the document is genuine, so a verifier auditing history should ACCEPT it, while a client about to pay must see that it is expired. A verifier that folds expiry into validity fails this vector; so does one that never reports expiry at all. Report both.","payload":{"version":1,"resourceUrl":"https://example.com/api/buy/widget","scheme":"exact","network":"eip155:8453","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","payTo":"0x1111111111111111111111111111111111111111","amount":"10000","validUntil":1735689600},"jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiIxMDAwMCIsInZhbGlkVW50aWwiOjE3MzU2ODk2MDB9.2FIYFVeY5oen8Nd3R3cxqWujjvjaItVO77Xu3U62Q35YJONS7RfehMi-JXCKSFNGqWVFjboxW53Ji8Y7CNKkDQ"}],"invalid":[{"name":"offer_missing_version","expect":"reject","reject_at":"schema","note":"The signature itself VERIFIES — the payload lacks the required version field. Signature validity and schema validity are separate checks and a conformant verifier runs both.","jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiIxMDAwMCIsInZhbGlkVW50aWwiOjQxMDI0NDQ4MDB9.KiQp7_L8gCBxlmbqmrz0UOP4T6u-FYH4EkQCWF1rBVT-pWMYPrGFL7IlnXfVew-iSVl-o02kWKOzkRqc_AdnCQ"},{"name":"offer_wrong_key","expect":"reject","reject_at":"signature","note":"Signed by a key other than the one published above. A verifier that does not resolve the kid to the expected key would wrongly accept this.","jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiIxMDAwMCIsInZhbGlkVW50aWwiOjQxMDI0NDQ4MDB9.PWrVTn_PFln5UhUs9fAOyJUoT7bnUO9CgLTngFyKGABoHG7ByT34hTYSbSP6pc2sfCSBq2Tei47I12HjjKgyAQ"},{"name":"offer_tampered_payload","expect":"reject","reject_at":"signature","note":"amount altered after signing; the signature no longer covers the payload served.","jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiI5OTk5OSIsInZhbGlkVW50aWwiOjQxMDI0NDQ4MDB9.I3V9b8co0MqWfOrg9NyUfw3FEx_5JZf1l4sb23XKyyNlNXIf1GTl979S1SbGfoluAtK2evPqSanpA9QBI7InAA"},{"name":"offer_alg_none","expect":"reject","reject_at":"alg","note":"Header says alg:none and there is no signature at all. A verifier that dispatches on the header's algorithm instead of REQUIRING EdDSA accepts an unsigned document. Oldest attack on this format; still worth a vector.","jws":"eyJhbGciOiJub25lIiwia2lkIjoiZGlkOndlYjpzY3ZkLnN0b3JlI2NvbmZvcm1hbmNlLXRlc3Qta2V5In0.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiIxMDAwMCIsInZhbGlkVW50aWwiOjQxMDI0NDQ4MDB9."},{"name":"offer_alg_confusion_hs256","expect":"reject","reject_at":"alg","note":"THE MOST IMPORTANT VECTOR HERE. Header claims HS256 and the signature is a genuine HMAC-SHA256 over the signing input using the ED25519 PUBLIC KEY as the secret. The public key is public, so anybody can produce this. A verifier that reads alg from the header and hands 'the key it has' to a generic verify() will recompute the same MAC and ACCEPT a forgery from a stranger. Require EdDSA before you look at anything else.","jws":"eyJhbGciOiJIUzI1NiIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiIxMDAwMCIsInZhbGlkVW50aWwiOjQxMDI0NDQ4MDB9.8QE55sOamtAtYwzBQyqECpIynuGQn_hDa4yvOr_lPMM"},{"name":"offer_missing_payTo","expect":"reject","reject_at":"schema","note":"A required field other than version, so the schema check has to be a loop rather than one special case. This is the exact shape CDP's own validator rejected three live listings for: a declaration whose required field is absent from what it ships.","jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwiYW1vdW50IjoiMTAwMDAiLCJ2YWxpZFVudGlsIjo0MTAyNDQ0ODAwfQ.KKDiYOl59xl7qB7r0eF7siFeSpZCp69DN0CM4YjVjkIactXUznzS5Uda7V29AD6prvIFO4XsGaPRzsvnxlagCQ"},{"name":"receipt_missing_payer","expect":"reject","reject_at":"schema","note":"Receipt-side schema. Every invalid vector before v2 was an OFFER, which meant a verifier with no receipt validation whatsoever passed the entire suite and looked conformant.","jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZWlwMTU1Ojg0NTMiLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJpc3N1ZWRBdCI6MTc2NzIyNTYwMCwidHJhbnNhY3Rpb24iOiIweGFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWIifQ.sT59O1pjWkfYlHROWNgCVPbsuYNGLt1u-25qOsz0iJKmjBKTJyF3iTPXc0X9IbZcuhXJSh7gtno6y-UUZ7zfBQ"},{"name":"offer_no_kid","expect":"reject","reject_at":"kid","note":"Valid EdDSA signature, no kid in the header. A verifier must not silently reach for whichever key it happens to hold; without a kid there is nothing saying WHICH key was meant, and guessing is how the wrong issuer gets credited.","jws":"eyJhbGciOiJFZERTQSJ9.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiIxMDAwMCIsInZhbGlkVW50aWwiOjQxMDI0NDQ4MDB9.Ry3X1qTCVm39jNTafoM41rOFM5g76jy7vJXAmzUlFJVW0SNqPT5nqivvrP-gn33zSt3QIkmuuI8tXfGTVt6MDA"},{"name":"offer_two_segments","expect":"reject","reject_at":"parse","note":"A JWS with the signature segment absent entirely. Must be refused, and must not throw: a malformed input from a stranger is an ordinary Tuesday for a verifier.","jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiIxMDAwMCIsInZhbGlkVW50aWwiOjQxMDI0NDQ4MDB9"},{"name":"offer_truncated_signature","expect":"reject","reject_at":"signature","note":"Structure is fine and the signature is half the bytes it should be. Some verifiers refuse this at parse on a length check, which is also conformant — rejecting EARLIER than reject_at is always fine. Accepting is not.","jws":"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6c2N2ZC5zdG9yZSNjb25mb3JtYW5jZS10ZXN0LWtleSJ9.eyJ2ZXJzaW9uIjoxLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYXBpL2J1eS93aWRnZXQiLCJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsiOiJlaXAxNTU6ODQ1MyIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwicGF5VG8iOiIweDExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJhbW91bnQiOiIxMDAwMCIsInZhbGlkVW50aWwiOjQxMDI0NDQ4MDB9.I3V9b8co0MqWfOrg9NyUfw3FEx_5JZf1l4sb23XKyyN"}],"@context":"https://schema.org","@type":"Dataset","name":"x402 offer-receipt conformance vectors","url":"https://scvd.store/.well-known/conformance/offer-receipt-vectors.json","creator":{"@type":"Organization","name":"SCVD General Store","url":"https://scvd.store"},"isAccessibleForFree":true,"conditionsOfAccess":"Free to fetch. No account, no key.","distribution":{"@type":"DataDownload","encodingFormat":"application/json","contentUrl":"https://scvd.store/.well-known/conformance/offer-receipt-vectors.json"},"live_counterpart":"https://scvd.store/api/buy/hello — a real 402 whose PAYMENT-REQUIRED header carries live signed offers under extensions['offer-receipt'], signed by the production key at https://scvd.store/.well-known/did.json (never the test key in these vectors).","verifier_guidance":"Allow a few seconds of clock-skew leeway when comparing validUntil against your own clock — issuance is strict, consumption should be tolerant, and NTP drift on your side against a 300-second window makes small leeway harmless.","regenerate":"node scripts/generate-conformance-vectors.mjs in the repository reproduces this file byte for byte."}