Skip to content

Commit 1450e56

Browse files
giskard09claude
andcommitted
execution-join-ref-v1: canonicalization_profile_id + failure code taxonomy
Add canonicalization_profile_id as required field in decision evidence preimage. Verifier reads profile before comparing digests; absent or unsupported profile returns a distinct failure code rather than DIGEST_MISMATCH. Failure code taxonomy (5 buckets + VERIFIER_OFFLINE): AUTHORIZED_EFFECTIVE_CALL / MALFORMED_EVIDENCE / UNSUPPORTED_CANONICAL_PROFILE / DIGEST_MISMATCH / EXPIRED_POLICY_BINDING Byte-verified fixture for each bucket. currently defined profile: jcs-rfc8785-v1. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4b3aaf2 commit 1450e56

2 files changed

Lines changed: 203 additions & 1 deletion

File tree

docs/spec/execution-join-ref-v1.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,31 @@ attempt's bytes is a binding failure even if the proof itself is valid.
4343
"version": "execution-join-ref-v1"
4444
}
4545

46-
// decision_id
46+
// decision_id (decision evidence object)
4747
{
4848
"action_ref": "<sha256-hex>",
49+
"canonicalization_profile_id": "<string>",
4950
"outcome": "permit | deny | defer",
5051
"policy_id": "<string>",
5152
"ts_ms": <integer>,
5253
"version": "execution-join-ref-v1"
5354
}
55+
```
56+
57+
`canonicalization_profile_id` is a required field in the decision evidence
58+
object. The verifier reads it before comparing any digests. If the field is
59+
absent or names a profile the verifier does not support, the verifier must
60+
return a failure code specific to that condition — not DIGEST_MISMATCH. This
61+
ensures that a verifier that only implements JCS cannot silently pass evidence
62+
encoded with a different canonicalization scheme.
63+
64+
Currently defined profiles:
65+
66+
| profile_id | canonical form |
67+
|---|---|
68+
| `jcs-rfc8785-v1` | JSON Canonicalization Scheme (RFC 8785) |
69+
70+
```json
5471

5572
// attempt_id
5673
{
@@ -87,6 +104,22 @@ conformant implementation must pass all five positive invariants without any
87104
on-chain anchor. Anchoring may be layered on top via
88105
`anchoring-precedence-ref-v1` once the join record exists.
89106

107+
## Failure code taxonomy
108+
109+
All verifier results map to exactly one of these codes:
110+
111+
| code | condition |
112+
|---|---|
113+
| `AUTHORIZED_EFFECTIVE_CALL` | all invariants pass; effective call is bound to a valid decision |
114+
| `MALFORMED_EVIDENCE` | decision evidence object is missing a required field or is not valid JCS |
115+
| `UNSUPPORTED_CANONICAL_PROFILE` | `canonicalization_profile_id` is present but the verifier does not implement that profile |
116+
| `DIGEST_MISMATCH` | profile is supported, all fields are present, but recomputed digest does not match the claimed ref |
117+
| `EXPIRED_POLICY_BINDING` | decision evidence is structurally valid but `policy_id` was not in force at `ts_ms` |
118+
119+
`VERIFIER_OFFLINE` remains a distinct code when the verifier cannot access the
120+
preimage material needed to recompute any ref. It does not collapse into
121+
`MALFORMED_EVIDENCE`.
122+
90123
## Invariants
91124

92125
A verifier checks these in order; the first failure is the reported

examples/conformance/execution-join-ref/vectors.json

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
"spec_version": "execution-join-ref-v1",
33
"description": "Conformance vectors for execution-join-ref-v1. Five invariants are separately recomputable: canonical_envelope, chain_integrity, no_duplicate_attempt, result_uniqueness, proof_binding. A sixth invariant checks effective_call_binding: the dispatched call must bind to the same action_ref the guardrail decided on. Anchoring is optional and not required for any vector here. A verifier that only checks chain_integrity will silently pass DUPLICATE_ATTEMPT, RESULT_ID_CONFLICT, and EFFECTIVE_CALL_REBINDING_FAILED.",
44
"chain_rule": "decision_id preimage must include action_ref. result_id preimage must include attempt_id. proof_ref preimage (if present) must include this attempt_id, not a rewrite's.",
5+
"canonicalization_note": "decision evidence preimage must include canonicalization_profile_id. Verifier reads this field before comparing any digest. Absent or unsupported profile must return a failure code distinct from DIGEST_MISMATCH.",
6+
"failure_codes": ["AUTHORIZED_EFFECTIVE_CALL", "MALFORMED_EVIDENCE", "UNSUPPORTED_CANONICAL_PROFILE", "DIGEST_MISMATCH", "EXPIRED_POLICY_BINDING"],
57
"vectors": [
68
{
79
"id": "execution-join-positive-001",
@@ -83,6 +85,173 @@
8385
},
8486
"note": "This record is identical to execution-join-positive-001 as seen by the verifier. The failure is detected only because the registry already holds a record with this attempt_id."
8587
},
88+
{
89+
"id": "AUTHORIZED_EFFECTIVE_CALL",
90+
"description": "Positive case with canonicalization_profile_id present. decision evidence declares jcs-rfc8785-v1. Verifier reads the profile field first, confirms it is supported, then recomputes the digest. All invariants pass. Failure code: AUTHORIZED_EFFECTIVE_CALL.",
91+
"expected": "PASS",
92+
"failure_code": "AUTHORIZED_EFFECTIVE_CALL",
93+
"envelope": {
94+
"version": "execution-join-ref-v1",
95+
"action_ref": "53138a338b70ac7cb54165e4175f5432384b44092f2968329212a1661f973b02",
96+
"decision_id": "f7130bc102fe84edd3435eed4b42cf51c134b9c6d5840fda760a117ed7ec34f3",
97+
"attempt_id": "9c796e94c7cbfbc898791bdafffb9f83d676820435dfdecc40899fb341f3c2bf",
98+
"result_id": "121a8185d514f8458f0eee59647f9f4f7b84442c7de185680ae4e73d619e99f0",
99+
"proof_ref": null
100+
},
101+
"preimages": {
102+
"decision_id": {
103+
"action_ref": "53138a338b70ac7cb54165e4175f5432384b44092f2968329212a1661f973b02",
104+
"canonicalization_profile_id": "jcs-rfc8785-v1",
105+
"outcome": "permit",
106+
"policy_id": "stripe-payment-guardrail-v2",
107+
"ts_ms": 1782900000050,
108+
"version": "execution-join-ref-v1"
109+
}
110+
},
111+
"checks": {
112+
"canonical_envelope": true,
113+
"chain_integrity": true,
114+
"no_duplicate_attempt": true,
115+
"result_uniqueness": true,
116+
"proof_binding": true,
117+
"effective_call_binding": true
118+
}
119+
},
120+
{
121+
"id": "MALFORMED_EVIDENCE",
122+
"description": "decision evidence object is missing action_ref. Verifier must return MALFORMED_EVIDENCE before attempting digest comparison. A verifier that skips field validation and proceeds to digest comparison will produce an incorrect result.",
123+
"expected": "FAIL",
124+
"failure_code": "MALFORMED_EVIDENCE",
125+
"failure_detail": "decision_id preimage 69401bd3… does not contain action_ref. Required field absent. Verifier must not proceed to digest comparison.",
126+
"envelope": {
127+
"version": "execution-join-ref-v1",
128+
"action_ref": "53138a338b70ac7cb54165e4175f5432384b44092f2968329212a1661f973b02",
129+
"decision_id": "69401bd3e78ed1ba06657bb6334d5611476e45bf7862f2ad2f150b777d10b9e2",
130+
"attempt_id": "9c796e94c7cbfbc898791bdafffb9f83d676820435dfdecc40899fb341f3c2bf",
131+
"result_id": "121a8185d514f8458f0eee59647f9f4f7b84442c7de185680ae4e73d619e99f0",
132+
"proof_ref": null
133+
},
134+
"preimages": {
135+
"decision_id": {
136+
"canonicalization_profile_id": "jcs-rfc8785-v1",
137+
"outcome": "permit",
138+
"policy_id": "stripe-payment-guardrail-v2",
139+
"ts_ms": 1782900000050,
140+
"version": "execution-join-ref-v1"
141+
}
142+
},
143+
"checks": {
144+
"canonical_envelope": false,
145+
"chain_integrity": false,
146+
"no_duplicate_attempt": true,
147+
"result_uniqueness": true,
148+
"proof_binding": true,
149+
"effective_call_binding": false
150+
}
151+
},
152+
{
153+
"id": "UNSUPPORTED_CANONICAL_PROFILE",
154+
"description": "decision evidence declares canonicalization_profile_id cbor-deterministic-v1, which the verifier does not implement. Verifier must return UNSUPPORTED_CANONICAL_PROFILE, not DIGEST_MISMATCH. A verifier that falls through to its default JCS comparison will produce the wrong failure code.",
155+
"expected": "FAIL",
156+
"failure_code": "UNSUPPORTED_CANONICAL_PROFILE",
157+
"failure_detail": "canonicalization_profile_id cbor-deterministic-v1 is not in the verifier's supported set. Verifier must halt before digest comparison and return UNSUPPORTED_CANONICAL_PROFILE.",
158+
"envelope": {
159+
"version": "execution-join-ref-v1",
160+
"action_ref": "53138a338b70ac7cb54165e4175f5432384b44092f2968329212a1661f973b02",
161+
"decision_id": "f978ed243ff4bba7c72d82f5d1f591df6a3f1da88a10afa9886289ef3d80d23c",
162+
"attempt_id": "9c796e94c7cbfbc898791bdafffb9f83d676820435dfdecc40899fb341f3c2bf",
163+
"result_id": "121a8185d514f8458f0eee59647f9f4f7b84442c7de185680ae4e73d619e99f0",
164+
"proof_ref": null
165+
},
166+
"preimages": {
167+
"decision_id": {
168+
"action_ref": "53138a338b70ac7cb54165e4175f5432384b44092f2968329212a1661f973b02",
169+
"canonicalization_profile_id": "cbor-deterministic-v1",
170+
"outcome": "permit",
171+
"policy_id": "stripe-payment-guardrail-v2",
172+
"ts_ms": 1782900000050,
173+
"version": "execution-join-ref-v1"
174+
}
175+
},
176+
"checks": {
177+
"canonical_envelope": null,
178+
"chain_integrity": null,
179+
"no_duplicate_attempt": true,
180+
"result_uniqueness": true,
181+
"proof_binding": true,
182+
"effective_call_binding": null
183+
},
184+
"note": "null checks indicate the verifier halted before those checks could be performed. UNSUPPORTED_CANONICAL_PROFILE is reported before any digest comparison is attempted."
185+
},
186+
{
187+
"id": "DIGEST_MISMATCH",
188+
"description": "canonicalization_profile_id is jcs-rfc8785-v1 (supported), all required fields are present, but action_ref in the decision evidence preimage is wrong bytes (fff…). Recomputed decision_id does not match the claimed value. Failure code: DIGEST_MISMATCH.",
189+
"expected": "FAIL",
190+
"failure_code": "DIGEST_MISMATCH",
191+
"failure_detail": "decision_id 1c9de3c2… recomputes correctly from its declared preimage (which contains action_ref fff…). But fff… ≠ envelope action_ref 53138a33…. The decision evidence was computed over a different action. chain_integrity fails after digest verification.",
192+
"envelope": {
193+
"version": "execution-join-ref-v1",
194+
"action_ref": "53138a338b70ac7cb54165e4175f5432384b44092f2968329212a1661f973b02",
195+
"decision_id": "1c9de3c23a027ec8013ba709c2dfb5a4034bcabaf6374e7f28b40aa80e414beb",
196+
"attempt_id": "9c796e94c7cbfbc898791bdafffb9f83d676820435dfdecc40899fb341f3c2bf",
197+
"result_id": "121a8185d514f8458f0eee59647f9f4f7b84442c7de185680ae4e73d619e99f0",
198+
"proof_ref": null
199+
},
200+
"preimages": {
201+
"decision_id": {
202+
"action_ref": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
203+
"canonicalization_profile_id": "jcs-rfc8785-v1",
204+
"outcome": "permit",
205+
"policy_id": "stripe-payment-guardrail-v2",
206+
"ts_ms": 1782900000050,
207+
"version": "execution-join-ref-v1"
208+
}
209+
},
210+
"checks": {
211+
"canonical_envelope": true,
212+
"chain_integrity": false,
213+
"no_duplicate_attempt": true,
214+
"result_uniqueness": true,
215+
"proof_binding": true,
216+
"effective_call_binding": false
217+
},
218+
"note": "canonical_envelope passes because decision_id correctly hashes its own preimage. chain_integrity fails because the preimage's action_ref does not match the envelope's action_ref. DIGEST_MISMATCH is reported after the profile check succeeds."
219+
},
220+
{
221+
"id": "EXPIRED_POLICY_BINDING",
222+
"description": "decision evidence is structurally valid and canonicalization_profile_id is supported, but policy_id stripe-payment-guardrail-v1 expired at 1782800000000 ms, which is before the decision ts_ms 1782900000050. The guardrail was no longer in force when the decision was issued. Failure code: EXPIRED_POLICY_BINDING.",
223+
"expected": "FAIL",
224+
"failure_code": "EXPIRED_POLICY_BINDING",
225+
"failure_detail": "policy_id stripe-payment-guardrail-v1 policy_expires_ms=1782800000000 < ts_ms=1782900000050. Policy was retired 100s before the decision timestamp. Decision was issued under an expired policy binding.",
226+
"envelope": {
227+
"version": "execution-join-ref-v1",
228+
"action_ref": "53138a338b70ac7cb54165e4175f5432384b44092f2968329212a1661f973b02",
229+
"decision_id": "7b5e4e5af9832b5d71586179f64070437a76a5dc5c0bef585bbad118177b02e3",
230+
"attempt_id": "9c796e94c7cbfbc898791bdafffb9f83d676820435dfdecc40899fb341f3c2bf",
231+
"result_id": "121a8185d514f8458f0eee59647f9f4f7b84442c7de185680ae4e73d619e99f0",
232+
"proof_ref": null
233+
},
234+
"preimages": {
235+
"decision_id": {
236+
"action_ref": "53138a338b70ac7cb54165e4175f5432384b44092f2968329212a1661f973b02",
237+
"canonicalization_profile_id": "jcs-rfc8785-v1",
238+
"outcome": "permit",
239+
"policy_expires_ms": 1782800000000,
240+
"policy_id": "stripe-payment-guardrail-v1",
241+
"ts_ms": 1782900000050,
242+
"version": "execution-join-ref-v1"
243+
}
244+
},
245+
"checks": {
246+
"canonical_envelope": true,
247+
"chain_integrity": true,
248+
"no_duplicate_attempt": true,
249+
"result_uniqueness": true,
250+
"proof_binding": true,
251+
"effective_call_binding": false
252+
},
253+
"note": "canonical_envelope and chain_integrity both pass — the evidence is internally consistent and the digest is correct. The failure is temporal: the policy that authorized the action was not in force at decision time."
254+
},
86255
{
87256
"id": "NONCONFORMANT_DECISION",
88257
"description": "CRITICAL: the result record claims a decision_id whose JCS preimage binds to a different action_ref (all-zeros placeholder). The guardrail never evaluated this specific proposed action. chain_integrity fails at the decision_id link.",

0 commit comments

Comments
 (0)