agent-receipt-verify 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/bench/methodefout.mjs +204 -0
  2. package/bin/cli.mjs +6 -0
  3. package/package.json +5 -3
  4. package/src/attempt-identity.mjs +278 -0
  5. package/src/x402-conformance-check.mjs +9 -1
  6. package/vectors/attempt-identity/all-paths-agree.json +126 -0
  7. package/vectors/attempt-identity/changed-amount-announced.json +86 -0
  8. package/vectors/attempt-identity/changed-amount-no-event.json +82 -0
  9. package/vectors/attempt-identity/changed-amount-silent-collapse.json +81 -0
  10. package/vectors/attempt-identity/client-supplied-key-unverified.json +29 -0
  11. package/vectors/attempt-identity/countersignature-matches.json +28 -0
  12. package/vectors/attempt-identity/countersigned-despite-mismatch.json +29 -0
  13. package/vectors/attempt-identity/derive-once-honoured.json +122 -0
  14. package/vectors/attempt-identity/idempotency-key-bound.json +28 -0
  15. package/vectors/attempt-identity/key-order-changes-identity.json +63 -0
  16. package/vectors/attempt-identity/key-order-stable-identity.json +62 -0
  17. package/vectors/attempt-identity/nothing-recorded-not-measured.json +21 -0
  18. package/vectors/attempt-identity/outcome-folded-into-identity.json +35 -0
  19. package/vectors/attempt-identity/outcome-kept-out-of-identity.json +34 -0
  20. package/vectors/attempt-identity/paths-disagree.json +81 -0
  21. package/vectors/attempt-identity/reference-canonicalized-first.json +28 -0
  22. package/vectors/attempt-identity/reference-over-transmitted.json +26 -0
  23. package/vectors/attempt-identity/settle-rederives-downstream.json +103 -0
  24. package/vectors/attempt-identity/settled-despite-mismatch.json +29 -0
@@ -0,0 +1,126 @@
1
+ {
2
+ "schema_version": "3.0.0",
3
+ "verifies": "attempt-identity",
4
+ "requires_chain_read": false,
5
+ "provenance": [
6
+ "x402 specification v2 section 5.5, Settlement and Refusal Identity Invariant, as proposed in x402-foundation/x402 pull request 3776 on branch spec/settlement-refusal-identity-invariant and not on main",
7
+ "the gap these cover was named by ruiruii in erc-8004/erc-8004-contracts#99 on 18 October 2026: each of the four evidence layers depends on an attempt identity that none of them defines",
8
+ "section 5.5 closes by referring to \"the normative conformance tests in the specification repository for the required negative cases\"; at the time of writing those did not exist"
9
+ ],
10
+ "id": "attempt-identity-all-paths-agree",
11
+ "clause": "all-paths-same-artifact",
12
+ "encodes": "The positive control for section 5.5: four named paths, one identity, derived once at ingress.",
13
+ "description": "All four paths named in the clause resolve the same identity artifact, and each reports that it took that value from the ingress snapshot rather than re-deriving it. Without this vector the set would contain only refusals, which proves the harness says no and nothing about whether it can say yes.",
14
+ "input": {
15
+ "ingress": {
16
+ "snapshot": {
17
+ "method": "POST",
18
+ "path": "/api/paid/verify-ownership",
19
+ "query": "",
20
+ "body": {
21
+ "endpoint": "https://example.com/api/health",
22
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
23
+ "method": "dns-txt"
24
+ },
25
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
26
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
27
+ "network": "eip155:8453",
28
+ "amount": "1000",
29
+ "scheme": "exact"
30
+ },
31
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196"
32
+ },
33
+ "paths": [
34
+ {
35
+ "path": "challenge-quote",
36
+ "snapshot": {
37
+ "method": "POST",
38
+ "path": "/api/paid/verify-ownership",
39
+ "query": "",
40
+ "body": {
41
+ "endpoint": "https://example.com/api/health",
42
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
43
+ "method": "dns-txt"
44
+ },
45
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
46
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
47
+ "network": "eip155:8453",
48
+ "amount": "1000",
49
+ "scheme": "exact"
50
+ },
51
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
52
+ "derived_from": "ingress-snapshot"
53
+ },
54
+ {
55
+ "path": "settle",
56
+ "snapshot": {
57
+ "method": "POST",
58
+ "path": "/api/paid/verify-ownership",
59
+ "query": "",
60
+ "body": {
61
+ "endpoint": "https://example.com/api/health",
62
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
63
+ "method": "dns-txt"
64
+ },
65
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
66
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
67
+ "network": "eip155:8453",
68
+ "amount": "1000",
69
+ "scheme": "exact"
70
+ },
71
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
72
+ "derived_from": "ingress-snapshot"
73
+ },
74
+ {
75
+ "path": "refusal",
76
+ "snapshot": {
77
+ "method": "POST",
78
+ "path": "/api/paid/verify-ownership",
79
+ "query": "",
80
+ "body": {
81
+ "endpoint": "https://example.com/api/health",
82
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
83
+ "method": "dns-txt"
84
+ },
85
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
86
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
87
+ "network": "eip155:8453",
88
+ "amount": "1000",
89
+ "scheme": "exact"
90
+ },
91
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
92
+ "derived_from": "ingress-snapshot"
93
+ },
94
+ {
95
+ "path": "replay-lookup",
96
+ "snapshot": {
97
+ "method": "POST",
98
+ "path": "/api/paid/verify-ownership",
99
+ "query": "",
100
+ "body": {
101
+ "endpoint": "https://example.com/api/health",
102
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
103
+ "method": "dns-txt"
104
+ },
105
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
106
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
107
+ "network": "eip155:8453",
108
+ "amount": "1000",
109
+ "scheme": "exact"
110
+ },
111
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
112
+ "derived_from": "ingress-snapshot"
113
+ }
114
+ ],
115
+ "identity_field": {
116
+ "name": "attempt_id",
117
+ "bound_to": "ingress-snapshot"
118
+ }
119
+ },
120
+ "expected": {
121
+ "outcome": "PASS"
122
+ },
123
+ "limits": [
124
+ "This says the four paths agree. It says nothing about whether the identity was computed over the right fields."
125
+ ]
126
+ }
@@ -0,0 +1,86 @@
1
+ {
2
+ "schema_version": "3.0.0",
3
+ "verifies": "attempt-identity",
4
+ "requires_chain_read": false,
5
+ "provenance": [
6
+ "x402 specification v2 section 5.5, Settlement and Refusal Identity Invariant, as proposed in x402-foundation/x402 pull request 3776 on branch spec/settlement-refusal-identity-invariant and not on main",
7
+ "the gap these cover was named by ruiruii in erc-8004/erc-8004-contracts#99 on 18 October 2026: each of the four evidence layers depends on an attempt identity that none of them defines",
8
+ "section 5.5 closes by referring to \"the normative conformance tests in the specification repository for the required negative cases\"; at the time of writing those did not exist"
9
+ ],
10
+ "id": "attempt-identity-changed-amount-announced",
11
+ "clause": "changed-request-different-artifact",
12
+ "encodes": "The positive control for the changed-request half: new identity, and an event that names it.",
13
+ "description": "The amount changed, the identity changed, and an announcement event carries the new identity. This is what the clause asks for, and it is here so the two refusals above cannot be mistaken for \"any change fails\".",
14
+ "input": {
15
+ "ingress": {
16
+ "snapshot": {
17
+ "method": "POST",
18
+ "path": "/api/paid/verify-ownership",
19
+ "query": "",
20
+ "body": {
21
+ "endpoint": "https://example.com/api/health",
22
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
23
+ "method": "dns-txt"
24
+ },
25
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
26
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
27
+ "network": "eip155:8453",
28
+ "amount": "1000",
29
+ "scheme": "exact"
30
+ },
31
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196"
32
+ },
33
+ "paths": [
34
+ {
35
+ "path": "challenge-quote",
36
+ "snapshot": {
37
+ "method": "POST",
38
+ "path": "/api/paid/verify-ownership",
39
+ "query": "",
40
+ "body": {
41
+ "endpoint": "https://example.com/api/health",
42
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
43
+ "method": "dns-txt"
44
+ },
45
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
46
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
47
+ "network": "eip155:8453",
48
+ "amount": "1000",
49
+ "scheme": "exact"
50
+ },
51
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196"
52
+ },
53
+ {
54
+ "path": "settle",
55
+ "snapshot": {
56
+ "method": "POST",
57
+ "path": "/api/paid/verify-ownership",
58
+ "query": "",
59
+ "body": {
60
+ "endpoint": "https://example.com/api/health",
61
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
62
+ "method": "dns-txt"
63
+ },
64
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
65
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
66
+ "network": "eip155:8453",
67
+ "amount": "2000",
68
+ "scheme": "exact"
69
+ },
70
+ "identity": "b1b9009e0c9e7d66fc4e47a66303e8ccdddce8cc3462bc9de007abf524855bcb"
71
+ }
72
+ ],
73
+ "events": [
74
+ {
75
+ "type": "announcement",
76
+ "identity": "b1b9009e0c9e7d66fc4e47a66303e8ccdddce8cc3462bc9de007abf524855bcb"
77
+ }
78
+ ]
79
+ },
80
+ "expected": {
81
+ "outcome": "PASS"
82
+ },
83
+ "limits": [
84
+ "The event is taken as recorded. Whether it was actually emitted to anyone is outside this layer."
85
+ ]
86
+ }
@@ -0,0 +1,82 @@
1
+ {
2
+ "schema_version": "3.0.0",
3
+ "verifies": "attempt-identity",
4
+ "requires_chain_read": false,
5
+ "provenance": [
6
+ "x402 specification v2 section 5.5, Settlement and Refusal Identity Invariant, as proposed in x402-foundation/x402 pull request 3776 on branch spec/settlement-refusal-identity-invariant and not on main",
7
+ "the gap these cover was named by ruiruii in erc-8004/erc-8004-contracts#99 on 18 October 2026: each of the four evidence layers depends on an attempt identity that none of them defines",
8
+ "section 5.5 closes by referring to \"the normative conformance tests in the specification repository for the required negative cases\"; at the time of writing those did not exist"
9
+ ],
10
+ "id": "attempt-identity-changed-amount-no-event",
11
+ "clause": "changed-request-different-artifact",
12
+ "encodes": "A changed request that correctly gets a new identity but surfaces nowhere, which the clause calls a silent divergence.",
13
+ "description": "The amount changed, the identity changed with it, and no settlement or announcement event names the new identity. Half the clause is satisfied and the half that makes it observable is not, so a reader sees two unrelated attempts instead of one attempt that changed.",
14
+ "input": {
15
+ "ingress": {
16
+ "snapshot": {
17
+ "method": "POST",
18
+ "path": "/api/paid/verify-ownership",
19
+ "query": "",
20
+ "body": {
21
+ "endpoint": "https://example.com/api/health",
22
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
23
+ "method": "dns-txt"
24
+ },
25
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
26
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
27
+ "network": "eip155:8453",
28
+ "amount": "1000",
29
+ "scheme": "exact"
30
+ },
31
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196"
32
+ },
33
+ "paths": [
34
+ {
35
+ "path": "challenge-quote",
36
+ "snapshot": {
37
+ "method": "POST",
38
+ "path": "/api/paid/verify-ownership",
39
+ "query": "",
40
+ "body": {
41
+ "endpoint": "https://example.com/api/health",
42
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
43
+ "method": "dns-txt"
44
+ },
45
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
46
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
47
+ "network": "eip155:8453",
48
+ "amount": "1000",
49
+ "scheme": "exact"
50
+ },
51
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196"
52
+ },
53
+ {
54
+ "path": "settle",
55
+ "snapshot": {
56
+ "method": "POST",
57
+ "path": "/api/paid/verify-ownership",
58
+ "query": "",
59
+ "body": {
60
+ "endpoint": "https://example.com/api/health",
61
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
62
+ "method": "dns-txt"
63
+ },
64
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
65
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
66
+ "network": "eip155:8453",
67
+ "amount": "2000",
68
+ "scheme": "exact"
69
+ },
70
+ "identity": "b1b9009e0c9e7d66fc4e47a66303e8ccdddce8cc3462bc9de007abf524855bcb"
71
+ }
72
+ ],
73
+ "events": []
74
+ },
75
+ "expected": {
76
+ "outcome": "FAIL",
77
+ "reason_code": "CHANGED_REQUEST_NO_EVENT"
78
+ },
79
+ "limits": [
80
+ "An empty event list is taken at face value. A checker cannot tell a missing event from an event it was not shown."
81
+ ]
82
+ }
@@ -0,0 +1,81 @@
1
+ {
2
+ "schema_version": "3.0.0",
3
+ "verifies": "attempt-identity",
4
+ "requires_chain_read": false,
5
+ "provenance": [
6
+ "x402 specification v2 section 5.5, Settlement and Refusal Identity Invariant, as proposed in x402-foundation/x402 pull request 3776 on branch spec/settlement-refusal-identity-invariant and not on main",
7
+ "the gap these cover was named by ruiruii in erc-8004/erc-8004-contracts#99 on 18 October 2026: each of the four evidence layers depends on an attempt identity that none of them defines",
8
+ "section 5.5 closes by referring to \"the normative conformance tests in the specification repository for the required negative cases\"; at the time of writing those did not exist"
9
+ ],
10
+ "id": "attempt-identity-changed-amount-silent-collapse",
11
+ "clause": "changed-request-different-artifact",
12
+ "encodes": "A changed amount kept under the same identity, so a price change disappears into a replay.",
13
+ "description": "The settle path carries an amount of 2000 where ingress saw 1000, and resolves the identity of the original request. The clause requires a different artifact. This is the case where a buyer is charged twice the quoted price and the record set still reads as one ordinary attempt.",
14
+ "input": {
15
+ "ingress": {
16
+ "snapshot": {
17
+ "method": "POST",
18
+ "path": "/api/paid/verify-ownership",
19
+ "query": "",
20
+ "body": {
21
+ "endpoint": "https://example.com/api/health",
22
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
23
+ "method": "dns-txt"
24
+ },
25
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
26
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
27
+ "network": "eip155:8453",
28
+ "amount": "1000",
29
+ "scheme": "exact"
30
+ },
31
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196"
32
+ },
33
+ "paths": [
34
+ {
35
+ "path": "challenge-quote",
36
+ "snapshot": {
37
+ "method": "POST",
38
+ "path": "/api/paid/verify-ownership",
39
+ "query": "",
40
+ "body": {
41
+ "endpoint": "https://example.com/api/health",
42
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
43
+ "method": "dns-txt"
44
+ },
45
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
46
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
47
+ "network": "eip155:8453",
48
+ "amount": "1000",
49
+ "scheme": "exact"
50
+ },
51
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196"
52
+ },
53
+ {
54
+ "path": "settle",
55
+ "snapshot": {
56
+ "method": "POST",
57
+ "path": "/api/paid/verify-ownership",
58
+ "query": "",
59
+ "body": {
60
+ "endpoint": "https://example.com/api/health",
61
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
62
+ "method": "dns-txt"
63
+ },
64
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
65
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
66
+ "network": "eip155:8453",
67
+ "amount": "2000",
68
+ "scheme": "exact"
69
+ },
70
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196"
71
+ }
72
+ ]
73
+ },
74
+ "expected": {
75
+ "outcome": "FAIL",
76
+ "reason_code": "CHANGED_REQUEST_SAME_IDENTITY"
77
+ },
78
+ "limits": [
79
+ "The vector shows the collapse. Whether the buyer was actually overcharged depends on the ledger and is not measured here."
80
+ ]
81
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "schema_version": "3.0.0",
3
+ "verifies": "attempt-identity",
4
+ "requires_chain_read": false,
5
+ "provenance": [
6
+ "x402 specification v2 section 5.5, Settlement and Refusal Identity Invariant, as proposed in x402-foundation/x402 pull request 3776 on branch spec/settlement-refusal-identity-invariant and not on main",
7
+ "the gap these cover was named by ruiruii in erc-8004/erc-8004-contracts#99 on 18 October 2026: each of the four evidence layers depends on an attempt identity that none of them defines",
8
+ "section 5.5 closes by referring to \"the normative conformance tests in the specification repository for the required negative cases\"; at the time of writing those did not exist"
9
+ ],
10
+ "id": "attempt-identity-client-supplied-key-unverified",
11
+ "clause": "name-independent-but-bound",
12
+ "encodes": "An Idempotency-Key the client chooses and the facilitator never checks against the ingress snapshot.",
13
+ "description": "The clause allows any field name, on the condition that the field is bound to the ingress snapshot. Here the client supplies the value and nothing verifies it, so two different requests can claim one identity and a buyer can make his second request look like a replay of his first.",
14
+ "input": {
15
+ "identity_field": {
16
+ "name": "Idempotency-Key",
17
+ "bound_to": "ingress-snapshot",
18
+ "client_supplied": true,
19
+ "verified_against_snapshot": false
20
+ }
21
+ },
22
+ "expected": {
23
+ "outcome": "FAIL",
24
+ "reason_code": "CLIENT_SUPPLIED_IDENTITY_NOT_VERIFIED"
25
+ },
26
+ "limits": [
27
+ "This is about the binding, not about whether the key is otherwise well formed."
28
+ ]
29
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "schema_version": "3.0.0",
3
+ "verifies": "attempt-identity",
4
+ "requires_chain_read": false,
5
+ "provenance": [
6
+ "x402 specification v2 section 5.5, Settlement and Refusal Identity Invariant, as proposed in x402-foundation/x402 pull request 3776 on branch spec/settlement-refusal-identity-invariant and not on main",
7
+ "the gap these cover was named by ruiruii in erc-8004/erc-8004-contracts#99 on 18 October 2026: each of the four evidence layers depends on an attempt identity that none of them defines",
8
+ "section 5.5 closes by referring to \"the normative conformance tests in the specification repository for the required negative cases\"; at the time of writing those did not exist"
9
+ ],
10
+ "id": "attempt-identity-countersignature-matches",
11
+ "clause": "countersignature-recompute-or-refuse",
12
+ "encodes": "The positive control for the countersignature clause: recomputed, equal, signed, settled.",
13
+ "description": "The counterparty recomputed the fingerprint from the bytes it observed, arrived at the ingress value, signed, and the interaction settled. Both negatives of this clause are refusals, so without this vector the clause would only ever be able to say no.",
14
+ "input": {
15
+ "countersignature": {
16
+ "ingress_value": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
17
+ "recomputed": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
18
+ "signed": true,
19
+ "settled": true
20
+ }
21
+ },
22
+ "expected": {
23
+ "outcome": "PASS"
24
+ },
25
+ "limits": [
26
+ "This says the recomputed value matched. It does not verify the signature itself; that is the issuing party own verifier."
27
+ ]
28
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "schema_version": "3.0.0",
3
+ "verifies": "attempt-identity",
4
+ "requires_chain_read": false,
5
+ "provenance": [
6
+ "x402 specification v2 section 5.5, Settlement and Refusal Identity Invariant, as proposed in x402-foundation/x402 pull request 3776 on branch spec/settlement-refusal-identity-invariant and not on main",
7
+ "the gap these cover was named by ruiruii in erc-8004/erc-8004-contracts#99 on 18 October 2026: each of the four evidence layers depends on an attempt identity that none of them defines",
8
+ "section 5.5 closes by referring to \"the normative conformance tests in the specification repository for the required negative cases\"; at the time of writing those did not exist"
9
+ ],
10
+ "id": "attempt-identity-countersigned-despite-mismatch",
11
+ "clause": "countersignature-recompute-or-refuse",
12
+ "encodes": "A counterparty that recomputed, saw a different value, and signed anyway.",
13
+ "description": "The clause makes recomputation a verification obligation with a refusal attached. Here the recomputed fingerprint differs from the ingress value and the signature was produced regardless, which turns the countersignature into a second opinion about nothing.",
14
+ "input": {
15
+ "countersignature": {
16
+ "ingress_value": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
17
+ "recomputed": "b1b9009e0c9e7d66fc4e47a66303e8ccdddce8cc3462bc9de007abf524855bcb",
18
+ "signed": true,
19
+ "settled": false
20
+ }
21
+ },
22
+ "expected": {
23
+ "outcome": "FAIL",
24
+ "reason_code": "COUNTERSIGNED_DESPITE_MISMATCH"
25
+ },
26
+ "limits": [
27
+ "This cannot show whether the counterparty recomputed honestly, only that it reported a mismatch and signed."
28
+ ]
29
+ }
@@ -0,0 +1,122 @@
1
+ {
2
+ "schema_version": "3.0.0",
3
+ "verifies": "attempt-identity",
4
+ "requires_chain_read": false,
5
+ "provenance": [
6
+ "x402 specification v2 section 5.5, Settlement and Refusal Identity Invariant, as proposed in x402-foundation/x402 pull request 3776 on branch spec/settlement-refusal-identity-invariant and not on main",
7
+ "the gap these cover was named by ruiruii in erc-8004/erc-8004-contracts#99 on 18 October 2026: each of the four evidence layers depends on an attempt identity that none of them defines",
8
+ "section 5.5 closes by referring to \"the normative conformance tests in the specification repository for the required negative cases\"; at the time of writing those did not exist"
9
+ ],
10
+ "id": "attempt-identity-derive-once-honoured",
11
+ "clause": "derive-once-at-ingress",
12
+ "encodes": "The positive control for the core clause: every path states it took the identity from the ingress snapshot.",
13
+ "description": "Four paths, and each one declares its identity came from the ingress snapshot rather than from a fresh read of the request. The negative twin of this vector has one path recomputing at settle time while arriving at the same value, which is why the declared origin is what gets checked and not only the value.",
14
+ "input": {
15
+ "ingress": {
16
+ "snapshot": {
17
+ "method": "POST",
18
+ "path": "/api/paid/verify-ownership",
19
+ "query": "",
20
+ "body": {
21
+ "endpoint": "https://example.com/api/health",
22
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
23
+ "method": "dns-txt"
24
+ },
25
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
26
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
27
+ "network": "eip155:8453",
28
+ "amount": "1000",
29
+ "scheme": "exact"
30
+ },
31
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196"
32
+ },
33
+ "paths": [
34
+ {
35
+ "path": "challenge-quote",
36
+ "snapshot": {
37
+ "method": "POST",
38
+ "path": "/api/paid/verify-ownership",
39
+ "query": "",
40
+ "body": {
41
+ "endpoint": "https://example.com/api/health",
42
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
43
+ "method": "dns-txt"
44
+ },
45
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
46
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
47
+ "network": "eip155:8453",
48
+ "amount": "1000",
49
+ "scheme": "exact"
50
+ },
51
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
52
+ "derived_from": "ingress-snapshot"
53
+ },
54
+ {
55
+ "path": "settle",
56
+ "snapshot": {
57
+ "method": "POST",
58
+ "path": "/api/paid/verify-ownership",
59
+ "query": "",
60
+ "body": {
61
+ "endpoint": "https://example.com/api/health",
62
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
63
+ "method": "dns-txt"
64
+ },
65
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
66
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
67
+ "network": "eip155:8453",
68
+ "amount": "1000",
69
+ "scheme": "exact"
70
+ },
71
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
72
+ "derived_from": "ingress-snapshot"
73
+ },
74
+ {
75
+ "path": "refusal",
76
+ "snapshot": {
77
+ "method": "POST",
78
+ "path": "/api/paid/verify-ownership",
79
+ "query": "",
80
+ "body": {
81
+ "endpoint": "https://example.com/api/health",
82
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
83
+ "method": "dns-txt"
84
+ },
85
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
86
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
87
+ "network": "eip155:8453",
88
+ "amount": "1000",
89
+ "scheme": "exact"
90
+ },
91
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
92
+ "derived_from": "ingress-snapshot"
93
+ },
94
+ {
95
+ "path": "replay-lookup",
96
+ "snapshot": {
97
+ "method": "POST",
98
+ "path": "/api/paid/verify-ownership",
99
+ "query": "",
100
+ "body": {
101
+ "endpoint": "https://example.com/api/health",
102
+ "ownerAddress": "0x1111111111111111111111111111111111111111",
103
+ "method": "dns-txt"
104
+ },
105
+ "payTo": "0x2888f36548a900b9b08d584524e1f73bc563ec26",
106
+ "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
107
+ "network": "eip155:8453",
108
+ "amount": "1000",
109
+ "scheme": "exact"
110
+ },
111
+ "identity": "2ed52a521f9762c3adc24c8048a4ee04a3796202a34c335585fc953e15c97196",
112
+ "derived_from": "ingress-snapshot"
113
+ }
114
+ ]
115
+ },
116
+ "expected": {
117
+ "outcome": "PASS"
118
+ },
119
+ "limits": [
120
+ "A declared origin is a claim by the implementation. This vector tests that a checker reads it; it cannot verify the claim from outside."
121
+ ]
122
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "schema_version": "3.0.0",
3
+ "verifies": "attempt-identity",
4
+ "requires_chain_read": false,
5
+ "provenance": [
6
+ "x402 specification v2 section 5.5, Settlement and Refusal Identity Invariant, as proposed in x402-foundation/x402 pull request 3776 on branch spec/settlement-refusal-identity-invariant and not on main",
7
+ "the gap these cover was named by ruiruii in erc-8004/erc-8004-contracts#99 on 18 October 2026: each of the four evidence layers depends on an attempt identity that none of them defines",
8
+ "section 5.5 closes by referring to \"the normative conformance tests in the specification repository for the required negative cases\"; at the time of writing those did not exist"
9
+ ],
10
+ "id": "attempt-identity-idempotency-key-bound",
11
+ "clause": "name-independent-but-bound",
12
+ "encodes": "The positive control for the name-independence clause: a different field name that satisfies the discipline.",
13
+ "description": "An implementation using Idempotency-Key, bound to the ingress snapshot and verified against it, satisfies the invariant. The requirement is the discipline, not the field name.",
14
+ "input": {
15
+ "identity_field": {
16
+ "name": "Idempotency-Key",
17
+ "bound_to": "ingress-snapshot",
18
+ "client_supplied": true,
19
+ "verified_against_snapshot": true
20
+ }
21
+ },
22
+ "expected": {
23
+ "outcome": "PASS"
24
+ },
25
+ "limits": [
26
+ "Satisfying this clause says nothing about the other seven."
27
+ ]
28
+ }