diff --git a/test_vectors/receipt_invalid_sig.json b/test_vectors/receipt_invalid_sig.json index adb91c9..3b420f8 100644 --- a/test_vectors/receipt_invalid_sig.json +++ b/test_vectors/receipt_invalid_sig.json @@ -5,6 +5,7 @@ "summary": "fixture" }, "metadata": { + "receipt_id": "rcpt_20260322_fixture_invalid_sig_001", "proof": { "alg": "ed25519-sha256", "canonical": "cl-stable-json-v1", diff --git a/test_vectors/receipt_malformed_pubkey.json b/test_vectors/receipt_malformed_pubkey.json index 65d6636..a11f72d 100644 --- a/test_vectors/receipt_malformed_pubkey.json +++ b/test_vectors/receipt_malformed_pubkey.json @@ -5,6 +5,7 @@ "summary": "fixture" }, "metadata": { + "receipt_id": "rcpt_20260322_fixture_malformed_pubkey_001", "proof": { "alg": "ed25519-sha256", "canonical": "cl-stable-json-v1", diff --git a/test_vectors/receipt_valid.json b/test_vectors/receipt_valid.json index 65d6636..c13717b 100644 --- a/test_vectors/receipt_valid.json +++ b/test_vectors/receipt_valid.json @@ -5,6 +5,7 @@ "summary": "fixture" }, "metadata": { + "receipt_id": "rcpt_20260322_fixture_valid_001", "proof": { "alg": "ed25519-sha256", "canonical": "cl-stable-json-v1", diff --git a/test_vectors/receipt_valid_v1.json b/test_vectors/receipt_valid_v1.json index 65d6636..eeedf86 100644 --- a/test_vectors/receipt_valid_v1.json +++ b/test_vectors/receipt_valid_v1.json @@ -5,6 +5,7 @@ "summary": "fixture" }, "metadata": { + "receipt_id": "rcpt_20260322_fixture_valid_v1_001", "proof": { "alg": "ed25519-sha256", "canonical": "cl-stable-json-v1", diff --git a/test_vectors/receipt_wrong_kid.json b/test_vectors/receipt_wrong_kid.json index 838f215..a8b4fd3 100644 --- a/test_vectors/receipt_wrong_kid.json +++ b/test_vectors/receipt_wrong_kid.json @@ -5,6 +5,7 @@ "summary": "fixture" }, "metadata": { + "receipt_id": "rcpt_20260322_fixture_wrong_kid_001", "proof": { "alg": "ed25519-sha256", "canonical": "cl-stable-json-v1", diff --git a/typescript-sdk/README.md b/typescript-sdk/README.md index 59e1532..357c15b 100644 --- a/typescript-sdk/README.md +++ b/typescript-sdk/README.md @@ -114,3 +114,11 @@ npm run typecheck npm test npm run test:integration ``` + +## Receipt verification semantics + +- `receipt.verb` is the canonical verb field returned by the runtime. +- `receipt.metadata.receipt_id` is an identifier for the receipt instance. +- `receipt.metadata.proof.hash_sha256` is the SHA-256 hash over the unsigned canonical receipt payload. +- `verifyReceipt()` succeeds when the declared algorithm/canonicalization match, the recomputed payload hash matches `hash_sha256`, and the Ed25519 signature validates over that hash. +- Legacy receipts that still place the verb under `receipt.x402.verb` continue to parse, but that path is fallback-only. diff --git a/typescript-sdk/src/index.ts b/typescript-sdk/src/index.ts index 0ec4795..5313fd6 100644 --- a/typescript-sdk/src/index.ts +++ b/typescript-sdk/src/index.ts @@ -31,6 +31,7 @@ export type CanonicalReceipt = { * Commons v1.1.0 receipts should not rely on or emit this block. */ x402?: { + /** @deprecated Legacy fallback only. Prefer the top-level receipt.verb field. */ verb?: string; version?: string; entry?: string; @@ -64,6 +65,8 @@ export type CommandResponse = { export type VerifyChecks = { hash_matches: boolean; signature_valid: boolean; + receipt_id_present: boolean; + /** @deprecated Legacy compatibility signal only. New receipts do not require receipt_id === hash_sha256. */ receipt_id_matches: boolean; alg_matches: boolean; canonical_matches: boolean; @@ -257,6 +260,13 @@ export async function resolveSignerKey(name: string, rpcUrl: string): Promise