@ziffer-io/types 0.1.0

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.
@@ -0,0 +1,4466 @@
1
+ import type { FidelityClass, Reversibility, Risk, SuiteId, TenantId, Tier, WebauthnAlg } from './generated.js';
2
+ /**
3
+ * The two acts an acknowledgement can be. Closed enum: the reference refuses
4
+ * any other value at ACK-1 by name, before the signature is checked, because
5
+ * a third value would be an act with no defined consequence and the release
6
+ * path would have to guess at it.
7
+ * Generated from acknowledgement.schema.json#/$defs/ack_decision.
8
+ */
9
+ export type AcknowledgementDecision = 'CONFIRM' | 'REPUDIATE';
10
+ /**
11
+ * What an Attestation Object may declare as its suite: a machine suite
12
+ * (`SuiteId`, the set CR-4's floor is registered in) or a human key type
13
+ * (`WebauthnAlg`, HM-4's two, which CR-8 puts outside that floor). The two
14
+ * are declared once each, in the SIGNED root, and pointed at from here.
15
+ *
16
+ * WHY A UNION AND NOT A THIRD LIST. A single enum naming all five values is
17
+ * the obvious way to write this field and it is the defect this repository
18
+ * publishes corrections for. The names would then exist in three places:
19
+ * CR-4 compares an object's suite against `min_suite`, which is drawn from
20
+ * `signature.schema.json`, and HM-3 compares it against a `webauthn`
21
+ * registry entry's `alg`, which is drawn from `attesters.schema.json`. A
22
+ * comparison between a copy and its original passes on a vocabulary nobody
23
+ * checked -- `decision_receipt.alg` records the same argument for `SuiteId`
24
+ * alone, and CR-8's restriction is exactly what forbids the cheaper fix of
25
+ * widening `SuiteId` to five values: `webauthn-es256` and `webauthn-ed25519`
26
+ * MUST NOT appear as a bundle, receipt or door suite, and every other holder
27
+ * of `SuiteId` is one of those three positions.
28
+ *
29
+ * WHY THE GENERATOR ACCEPTS IT. `tools/codegen.py` reads a `oneOf` as a
30
+ * union it must be able to DECIDE, and refuses one it would have to resolve
31
+ * by trying branches until something fits. A tagged union of objects proves
32
+ * its discriminator from the schema; this one is proved a different way and
33
+ * the proof is just as mechanical: the branches are named enums and the
34
+ * generator halts unless their value sets are pairwise DISJOINT, so exactly
35
+ * one branch can match any given string and the decode is by value rather
36
+ * than by order. Adding a value to either declaration that already exists in
37
+ * the other turns the generator red rather than emitting a type whose
38
+ * meaning depends on branch order.
39
+ *
40
+ * BOTH BRANCHES CROSS ROOTS, AND ONLY A NAMED SCALAR MAY (ACP-78 M1). The
41
+ * signed side owns the vocabulary and the wire carries it, the same
42
+ * direction as `SuiteId`, `Tier` and `FidelityClass`. This type is
43
+ * nevertheless declared HERE rather than in the bundle root, because the
44
+ * union is a fact about a MESSAGE: no signed table holds a value that may be
45
+ * either, and a registry entry's `alg` is always exactly one of the two.
46
+ * Generated from attestation_object.schema.json#/$defs/attestation_alg.
47
+ *
48
+ * The two branches are declared once each, elsewhere, and the generator
49
+ * proved their value sets disjoint before emitting this union, so narrowing
50
+ * it by value is total: a string belongs to at most one branch.
51
+ */
52
+ export type AttestationAlg = SuiteId | WebauthnAlg;
53
+ /**
54
+ * AU-9's seven event classes, in pipeline order: a door refusing a
55
+ * submission (F1.1), a Decision passing or failing §9.3 (F6.1), a receipt
56
+ * refused at the Executor with the offending receipt attached (EX-3), a
57
+ * floor-HIGH action held (DR-1), an irreversible below-HIGH action's notice
58
+ * committed before execution (DR-13), a held action released or refused
59
+ * (DR-7/9/10, counted under DR-12), and what the target reported (EX-2).
60
+ * Generated from audit_record.schema.json#/$defs/event_class.
61
+ */
62
+ export type AuditEventClass = 'ingress_refused' | 'decision' | 'verification_failed' | 'hold' | 'notice' | 'release' | 'execution';
63
+ /**
64
+ * EX-2's four execution outcomes, plus `refused` for a `release` record
65
+ * whose DR-* check failed closed. Which values a class admits is §11.1's
66
+ * table, enforced by the consumer under AU-9; the enum is the union so one
67
+ * type serves both records.
68
+ * Generated from audit_record.schema.json#/$defs/outcome.
69
+ */
70
+ export type AuditOutcome = 'executed' | 'failed' | 'not_attempted' | 'indeterminate' | 'refused';
71
+ /**
72
+ * A byte string carried as text under WE-4: the ASCII `"b64:"` followed by
73
+ * RFC 4648 §4 base64, WITH padding. The URL-safe alphabet (RFC 4648 §5) and
74
+ * unpadded encodings are different values and MUST be rejected rather than
75
+ * normalized.
76
+ *
77
+ * The prefix is part of the value, not a label on it. §11.2 pins `sha256:`
78
+ * to the character and records why it had to -- an implementer who fed
79
+ * forward raw digest bytes built a different chain while following the text
80
+ * -- and WE-4 exists because the same hole was open on every `b64:` value
81
+ * through v1.3.17, where the string appeared only inside §9.2's diagnostic
82
+ * JSON and no clause said whether the prefix was part of what gets hashed
83
+ * and signed. Two spellings of one byte string are two values, and at a
84
+ * verifier the mismatch is indistinguishable from a forgery.
85
+ *
86
+ * This type carries no length bound: WE-4 governs the encoding, and the size
87
+ * of any particular value is the business of the clause that defines it.
88
+ * Generated from signing_request.schema.json#/$defs/b64_bytes.
89
+ *
90
+ * The schema constrains this to `^b64:(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$`, which is NOT enforced here.
91
+ */
92
+ export type Base64Bytes = string;
93
+ /**
94
+ * The three outcomes of the section 8.4 grading fold: ALLOW executes, ATTEST
95
+ * routes to a human quorum, DENY refuses.
96
+ *
97
+ * NOT ORDERED. These are outcomes, not a severity scale, and a derived
98
+ * comparison over them would invite `>= ALLOW`-shaped code that has no
99
+ * meaning. P-4 makes DENY the default for no matching rule, unknown
100
+ * capability, unclassified resource or unavailable Context Store, each with
101
+ * its own reason code -- so DENY is where the fold lands when it knows
102
+ * nothing, not the far end of a ranking.
103
+ *
104
+ * EO-1: HIGH MUST NOT be auto-allowed by any configuration; there is no flag
105
+ * that makes step 11 emit ALLOW for HIGH.
106
+ * Generated from decision_receipt.schema.json#/$defs/decision_outcome.
107
+ */
108
+ export type DecisionOutcome = 'ALLOW' | 'ATTEST' | 'DENY';
109
+ /**
110
+ * The two doors of deferred release. `presentation` is the Attestation
111
+ * Presentation Service (the approval path); `notification` is DR-2's
112
+ * independent path. DELIBERATELY NOT ORDERED: neither outranks the other,
113
+ * and the two-compromise argument needs both.
114
+ * Generated from notification.schema.json#/$defs/door_role.
115
+ */
116
+ export type DoorRole = 'presentation' | 'notification';
117
+ /**
118
+ * A 128-bit nonce: `"b64:"` followed by RFC 4648 §4 base64 WITH padding,
119
+ * carried, hashed and signed as that string (WE-4). The size is AT-1 for the
120
+ * attestation nonce and L-17 for the receipt nonce.
121
+ *
122
+ * THE PATTERN IS TWO RULES, AND THEY REFUSE UNDER DIFFERENT NAMES. `b64:`
123
+ * plus the RFC 4648 §4 alphabet with padding is WE-4, the wire type; the
124
+ * twenty-four characters are the 128 bits. A verifier folding them into one
125
+ * check answers `WE-4` for a 64-bit nonce, which is a perfectly well-formed
126
+ * `b64:` value — the wrong clause name, and the cross-language differential
127
+ * compares names. `reference/src/acp_executor.py` and
128
+ * `crates/acp-decision/src/quorum.rs` both apply the type rule first and the
129
+ * size rule second, in that order, because an object wrong in both ways must
130
+ * stop at the same rule everywhere.
131
+ *
132
+ * WHY THE PREFIX IS PART OF THE VALUE. `attestation_id` is SHA-256 over the
133
+ * canonical CBOR of the Attestation Object (AT-8a), so an issuer carrying
134
+ * `b64:AAAA…` and a verifier carrying `AAAA…` derive TWO IDS FOR ONE OBJECT
135
+ * and claim TWO LEDGER SLOTS — one human approval, spent twice. That is Z4
136
+ * and T-14 reopening through a type nobody had written down, reached with no
137
+ * optional field and therefore untouched by AT-8b, which closes the field
138
+ * set one layer above this. §11.2 pins `sha256:` to the character for the
139
+ * same reason and records what happened without it: an implementer who fed
140
+ * forward raw digest bytes built a different chain while following the text.
141
+ *
142
+ * HISTORY, KEPT BECAUSE IT IS THE ARGUMENT. When this schema was first
143
+ * written the prefix was pinned HERE and nowhere else — `b64:` occurred
144
+ * exactly twice in ZIFFER-SPEC-001, both times inside §9.2's diagnostic
145
+ * JSON, and no clause said whether it was part of the value. Writing the
146
+ * schema is what surfaced that (ACP-87); it is now fixed at the root as WE-4
147
+ * in v1.3.18, so this pattern restates the specification rather than
148
+ * exceeding it. A type that exists only in a schema is a type the
149
+ * specification does not have, and a second implementation written from the
150
+ * document alone would never have seen it.
151
+ *
152
+ * The three definitions of this type — here, the Python reference and the
153
+ * Rust verifier — DID diverge, and no gate noticed: this pattern pinned
154
+ * twenty-four characters from its first day while the reference accepted any
155
+ * length, and no fixture ever fed a wrong-length nonce.
156
+ * `tools/nonce-type-vectors.json` is now the one corpus all three are
157
+ * evaluated against, and `tools/check-nonce-type.py` resolves this `$def`
158
+ * rather than one field's inline pattern, so the corpus is evaluated against
159
+ * the type every nonce field shares.
160
+ *
161
+ * IT IS ONE TYPE AND IT LIVES HERE. ACP-78 M1 found FIVE definitions of it
162
+ * inside `spec/schemas/wire/` alone — an inline pattern on this object's
163
+ * `att_nonce` and on `PendingRelease.receipt_nonce`, and named copies in the
164
+ * audit record and the ledger claim — with the sentence above, saying the
165
+ * definitions must move together, sitting inside two of them. `$ref` it; do
166
+ * not restate it.
167
+ * Generated from attestation_object.schema.json#/$defs/nonce128.
168
+ *
169
+ * The schema constrains this to `^b64:[A-Za-z0-9+/]{22}==$`, which is NOT enforced here.
170
+ */
171
+ export type Nonce128 = string;
172
+ /**
173
+ * A party identity: the operator an action is attributed to, and the same
174
+ * value DR-9 compares an acknowledger against.
175
+ *
176
+ * NO CLAUSE TYPES THIS AND THAT IS THE POINT OF SAYING SO HERE.
177
+ * ZIFFER-SPEC-001 carries `operator`, attester identities and notification
178
+ * recipients as bare strings everywhere and states no syntax, no length and
179
+ * no namespace for any of them. The bound is DERIVED, not restated: an
180
+ * operator must resolve in the bundle's attester registry for AT-2
181
+ * approver-distinctness and DR-9 to be decidable, and
182
+ * `spec/schemas/bundle/attesters.schema.json` bounds that registry's keys at
183
+ * 1..128 — itself a schema-level choice with no clause behind it. So this is
184
+ * a schema constraint the specification does not make, in the ACP-87 shape:
185
+ * a type that exists only in a schema is a type the specification does not
186
+ * have.
187
+ *
188
+ * IT IS DECLARED ONCE BECAUSE IT WAS DECLARED FIVE TIMES. ACP-78 M1 found
189
+ * this value written inline in five places across this root with TWO
190
+ * different bounds — 128 here, in `ContextRead.capability_grant`, in the
191
+ * receipt and in the pending-release record, and 2,048 in the audit record,
192
+ * where its author derived the bound from L-04 instead. Two lengths for one
193
+ * identity means a party who is nameable in an audit record and unnameable
194
+ * in the receipt that produced it.
195
+ * Generated from attestation_object.schema.json#/$defs/operator_id.
196
+ */
197
+ export type OperatorId = string;
198
+ /**
199
+ * EL-2's value domain: an integer or a string, and nothing else. A rule's
200
+ * environment binds each `params` entry to one of these; a value of any
201
+ * other JSON type -- a non-integer number, a boolean, null, an array, an
202
+ * object -- MUST refuse the whole Proposal (EL-2, closes the v1.3.17
203
+ * `22`/`22.0` defect). The integer is bounded by the width the deployment
204
+ * declares under AC-1a; the generated types carry `i64`, and a value above
205
+ * it is out of the domain rather than merely large.
206
+ * Generated from proposal.schema.json#/$defs/param_value.
207
+ *
208
+ * EL-2's domain and nothing else. A value of any other type refuses the
209
+ * Proposal; it is never coerced into one of these two.
210
+ */
211
+ export type ParamValue = number | string;
212
+ /**
213
+ * An instant on the wire: EXACTLY `YYYY-MM-DDTHH:MM:SSZ`, to one second. No
214
+ * offset, no fractional part, no lowercase `z`. Every temporal value in this
215
+ * root carries this type except the acknowledgement's, which ACK-6 declares
216
+ * numeric.
217
+ *
218
+ * WHY THE SPELLING IS PINNED AND NOT LEFT TO RFC 3339 AS WRITTEN. RFC 3339
219
+ * accepts `2026-08-09T14:02:11Z`, `2026-08-09t14:02:11z`,
220
+ * `2026-08-09T16:02:11+02:00` and `2026-08-09T14:02:11.000Z` as four
221
+ * renderings of ONE instant. Each is a distinct string, and these values go
222
+ * inside canonical CBOR and are hashed: the receipt's `issued_at` sits in
223
+ * the signed body, the attestation object's `expires_at` sits in the
224
+ * preimage of `attestation_id` (AT-1, AT-8a), and `occurred_at` sits in the
225
+ * AU-1 chain preimage. Four spellings of one instant are four ids for one
226
+ * object and four chain hashes for one history -- Z4 and T-14 reopening
227
+ * through a type nobody had written down, which is precisely what `nonce128`
228
+ * above records happening to `b64:`. Canonicalization cannot fix it: the
229
+ * ambiguity is in the VALUE, not the encoding. So the offset MUST be `Z`,
230
+ * the letters MUST be upper case, there MUST be no fractional part, and a
231
+ * verifier MUST REJECT any other rendering rather than normalizing it --
232
+ * normalizing here would make this verifier accept every spelling and hand
233
+ * the divergence to the next implementation, which is the reasoning
234
+ * `nonce128` gives for refusing a stripped `b64:` prefix.
235
+ *
236
+ * WHY IT IS A STRING AND NOT AN EPOCH NUMBER. AT-8a forbids a float in a
237
+ * canonical structure because a float is not deterministically encoded, and
238
+ * `canon()` enforces that at the TOP level only -- a nested float serialises
239
+ * happily in both languages. The one structure in this root whose times are
240
+ * numbers is the acknowledgement, and the reference put `1787598182.1470501`
241
+ * into it on every production path (`crates/acp-core/src/generated_wire.rs`
242
+ * records the observation beside `AcknowledgementObject.issued_at`). A
243
+ * string of this shape has exactly one encoding.
244
+ *
245
+ * HISTORY, KEPT BECAUSE IT IS THE ARGUMENT. Through v1.3.27 this type was
246
+ * written out on `audit_record.occurred_at` and NOWHERE ELSE; the other six
247
+ * temporal fields carried a bare `{"type": "string", "format": "date-time"}`
248
+ * with no pinned spelling, and BOTH implementations read all seven as POSIX
249
+ * numbers. So an Attestation Object that satisfied the schema was refused at
250
+ * `9.3-7b-iii` by both verifiers, and an object both verifiers accepted did
251
+ * not validate -- no client building from the schema could satisfy a
252
+ * floor-HIGH quorum. The cross-language differential could not see it,
253
+ * because the two implementations agreed with each other and disagreed with
254
+ * the document: the eighth lesson in `dossier/05-TEST-EVIDENCE.md`, one
255
+ * artifact over. Nothing validates a fixture against these schemas (ACP-52),
256
+ * so nothing else could either. Filed as ACP-167 by the first executable
257
+ * consumer to build a floor-HIGH fixture from the schema rather than from
258
+ * the reference, and closed in v1.3.28 by moving the IMPLEMENTATIONS --
259
+ * because AT-1, section 9.2's rendered receipt and seven schemas all said
260
+ * RFC 3339, and only the code said otherwise. `anchor.schema.json` had
261
+ * already written the fix down: "a divergence to close in one place, not
262
+ * two".
263
+ *
264
+ * THE PARSER ALREADY EXISTED, ONE ROOT OVER, AND THIS TYPE IS ITS GRAMMAR
265
+ * WRITTEN DOWN. PB-1 has to decide when a bundle stops being valid, so
266
+ * `bundle/manifest.schema.json` carries `expires_at` as RFC 3339 and both
267
+ * implementations parse it strictly: `Timestamp::parse` in Rust and
268
+ * `Timestamp.parse` in the reference, refusing offsets, fractional seconds
269
+ * and lowercase `z` in the same words, checking the day against the actual
270
+ * length of the month, and refusing a leap second because `datetime` does.
271
+ * They are already differential partners. This $def is that grammar, so the
272
+ * wire root gets ONE more reference to an existing type rather than a second
273
+ * parser -- a second parser for one format being the two-definitions defect
274
+ * this repository has published three corrections for. THE FRACTIONAL PART
275
+ * IS WHY THIS PARAGRAPH IS HERE: `occurred_at`'s pattern through v1.3.27
276
+ * permitted `(\.\d{1,9})?`, so `...:11Z` and `...:11.0Z` were two spellings
277
+ * of one instant inside the AU-1 chain preimage -- the exact hazard four
278
+ * paragraphs up, in the one field that had a pattern at all. v1.3.28 narrows
279
+ * it to the parsers' grammar. Nothing emitted a fractional second, because
280
+ * both implementations were emitting numbers.
281
+ *
282
+ * IT IS ONE TYPE AND IT LIVES HERE, for the reason `nonce128` states one
283
+ * $def above.
284
+ * Generated from attestation_object.schema.json#/$defs/rfc3339_instant.
285
+ *
286
+ * The schema constrains this to `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$`, which is NOT enforced here.
287
+ */
288
+ export type Rfc3339Instant = string;
289
+ /**
290
+ * The identifier half of SR-1's content address, carried on every Proposal
291
+ * by SR-5 and on the receipt that decided it by §9.2.
292
+ *
293
+ * ONE DECLARATION, AND WHY IT IS NOT SHARED WITH `task_type`. Before ACP-78
294
+ * M1 this pattern was written out three times in this root: here, on the
295
+ * receipt, and on the payload's `task_type`, which is a DIFFERENT value that
296
+ * happens to share a character class. The first two are one type and are now
297
+ * one declaration. The third is deliberately left as its own inline pattern,
298
+ * because V-11 requires `task_type` to `agree with` `schema_id` and does not
299
+ * say what agreement is -- string identity, or a registry lookup. Giving
300
+ * both fields one type would answer V-11 by writing a schema, which is the
301
+ * move this root exists to refuse. Two types with the same pattern are two
302
+ * types; one type with two declarations is the defect.
303
+ *
304
+ * THE PATTERN IS §6.3's, CHARACTER FOR CHARACTER, AND THE REFERENCE DOES NOT
305
+ * SATISFY IT: `reference/suites/conformance.py` builds `schema_id: "fw.v1"`
306
+ * and `.` is outside the class. It is kept as written, because widening a
307
+ * schema's own pattern to admit the fixture that violates it proves only
308
+ * that the instrument is adjustable. Whether a pattern stated inside a
309
+ * NORMATIVE EXAMPLE binds an implementation at all is unresolved and
310
+ * reported rather than assumed.
311
+ * Generated from proposal.schema.json#/$defs/schema_id.
312
+ *
313
+ * The schema constrains this to `^[a-z0-9_-]{1,32}$`, which is NOT enforced here.
314
+ */
315
+ export type SchemaId = string;
316
+ /**
317
+ * The version half of SR-1's content address (SR-5), declared once here and
318
+ * `$ref`d by the receipt, which carries the same triple. It is the axis SR-3
319
+ * decides change direction over: a change is NARROWING only where the new
320
+ * admissible set is a provable subset of the old in EVERY dimension of the
321
+ * bounded lattice -- character class, length bound, enum set, numeric range
322
+ * -- and anything not provably narrowing in every dimension is widening and
323
+ * takes the distinct-reviewer path. v1.2.0 left `narrowing` undefined, which
324
+ * let a net-widening change self-classify as narrowing and bypass review.
325
+ *
326
+ * The pattern is §6.3's and carries the same unresolved question as
327
+ * `schema_id`: it is stated inside a section titled `Reference Schemas
328
+ * (Normative Examples)`, and whether an example binds is not settled
329
+ * anywhere.
330
+ * Generated from proposal.schema.json#/$defs/schema_version.
331
+ *
332
+ * The schema constrains this to `^\d+\.\d+\.\d+$`, which is NOT enforced here.
333
+ */
334
+ export type SchemaVersion = string;
335
+ /**
336
+ * `"sha256:"` followed by a lowercase-hex SHA-256 digest — 71 characters.
337
+ * The type is pinned rather than left to the implementer because §11.2
338
+ * records an implementer who fed forward raw digest bytes and built a
339
+ * different chain while following the text.
340
+ * Generated from attestation_object.schema.json#/$defs/sha256.
341
+ *
342
+ * The schema constrains this to `^sha256:[0-9a-f]{64}$`, which is NOT enforced here.
343
+ */
344
+ export type Sha256Hash = string;
345
+ /**
346
+ * A signed acknowledgement of a floor-HIGH action being held for release.
347
+ * Carried on legs F5.4 and F5.5 of the leg register (ZIFFER-DEPLOY-001 Annex
348
+ * A): F5.4 is the DR-9 positive acknowledgement that releases an
349
+ * IRREVERSIBLE action, F5.5 is the DR-4 repudiation that vetoes one. It is
350
+ * the same artifact on both, discriminated by `decision`.
351
+ *
352
+ * THE DISCLOSURE THAT GOVERNS EVERY LINE BELOW: ACK-1 THROUGH ACK-6 ARE NOT
353
+ * CLAUSES OF ZIFFER-SPEC-001. The string `ACK-` does not occur in that
354
+ * document at all. They are reference-level rules, written for v1.3.12 in
355
+ * `reference/src/acp_ack.py` to close T-31, and the two places that already
356
+ * say so in as many words are the leg register (`DR-9, ACK-1..6
357
+ * (reference-level)`) and `spec/schemas/bundle/attesters.schema.json`
358
+ * (`ACK-4 (reference-level ... not a spec clause)`). ZIFFER-SPEC-001 states
359
+ * the OBLIGATIONS this object discharges -- DR-9 requires positive
360
+ * acknowledgement from at least one notified party who is not the operator,
361
+ * DR-4 admits repudiation from any notified party, DR-5 admits it only from
362
+ * a notified party and forbids the repudiation path from depending on the
363
+ * approval chain -- and states NOTHING about the artifact that carries them:
364
+ * no field list, no canonicalization, no validity bound, no identity type,
365
+ * no nonce type. Every field below therefore cites the reference-level ACK-*
366
+ * rule that put it there together with the ZIFFER-SPEC-001 clause it serves.
367
+ * A second implementation written from ZIFFER-SPEC-001 alone would not
368
+ * produce this object and would still be conformant with the document. That
369
+ * is the same hole ACP-87 was filed for one layer down, and it is wider
370
+ * here: there the type of one field was unstated, here the whole artifact
371
+ * is.
372
+ *
373
+ * ONE ARTIFACT OR TWO, AND WHY THIS SCHEMA IS ONE. The leg register makes
374
+ * them two rows with DIFFERENT clause sets -- F5.4 carries `DR-9, ACK-1..6`,
375
+ * F5.5 carries only `DR-4, DR-5` and names no ACK-* rule at all. The
376
+ * reference makes them ONE object: there is exactly one closed field set,
377
+ * and `confirm()` and `repudiate()` run the identical `_verify_ack` and
378
+ * differ only in which value of `decision` they will accept afterwards. Read
379
+ * literally, the register says a repudiation must satisfy DR-4 and DR-5 and
380
+ * nothing else -- which is the v1.3.11 bare-name form (T-31) still standing
381
+ * on the veto leg, unsigned, while the confirmation leg was fixed. It does
382
+ * not stand in the code. The register and the reference disagree, the
383
+ * disagreement is recorded rather than silently resolved (that is the ACP-57
384
+ * shape), and this schema follows the code because the code is what runs.
385
+ *
386
+ * WHY THE FIELD SET IS CLOSED. ACK-1: an acknowledgement is an object with a
387
+ * closed schema -- exactly these fields, no optional ones, no extension
388
+ * point. An object carrying an unknown field or omitting a declared one MUST
389
+ * be rejected, never normalized and never defaulted. That is why
390
+ * `additionalProperties` is false at both levels and why `required` lists
391
+ * every property, and it is why `x-acp-absent` appears here only on the
392
+ * signature map: an absent-value rule on a field of a closed object would be
393
+ * a defect rather than a control. The reason is Z4, stated for the
394
+ * Attestation Object and applying unchanged here because this object is on
395
+ * the same binding path: given ANY optional field, the object
396
+ * present-as-null and the object with the field omitted are each valid
397
+ * canonical encodings, hash to TWO DISTINCT ids, and therefore claim TWO
398
+ * ledger slots. One acknowledgement, spent twice; ACK-5's single use
399
+ * defeated without forging anything. Canonicalization cannot fix it, because
400
+ * the ambiguity is in the field set rather than the encoding.
401
+ *
402
+ * ENCODING, AND THE CLAUSE THAT IS MISSING. ACK-1 says the object is
403
+ * `canonically encoded` and NO clause anywhere says WHICH canonicalization.
404
+ * ZIFFER-SPEC-001 pins the other two: RFC 8785 (JCS) for the Proposal,
405
+ * canonical CBOR (RFC 8949 4.2) for the receipt and the Attestation Object
406
+ * (WE-1, WE-2, AT-8a). AT-8a's whole rationale is that object hashing moved
407
+ * onto the binding path, so any encoder disagreement between issuer and
408
+ * verifier becomes a freshness and single-use defect. This object is on that
409
+ * same path by construction: its id is a hash over its canonical encoding
410
+ * and the Consumption Ledger consumes that id (ACK-5). Two encoders means
411
+ * two ids for one acknowledgement, so a re-encoded acknowledgement claims a
412
+ * fresh slot and replays -- Y1b reopening in the machinery a later fix
413
+ * introduced, which is the RES-8/9/10 recurrence pattern this repository has
414
+ * now published five times. The rule AT-8a states for the Attestation Object
415
+ * is the rule this object needs and does not have. Filed, not invented here.
416
+ *
417
+ * WHAT IS NOT A FIELD. The acknowledgement id. ACK-5 recomputes it as a hash
418
+ * over the canonical object and consumes it in the ledger; it is never
419
+ * transmitted and never read from the message. That is Y1b's lesson applied
420
+ * before it could be rediscovered, and a schema property for it would invite
421
+ * exactly the read that was closed as Y1. There is likewise no field naming
422
+ * the pending action other than `proposal_hash`, no field asserting that the
423
+ * acknowledger was notified (that is the Executor's own record, tested under
424
+ * DR-5), and no field carrying the reversibility class (RV-3: recomputed
425
+ * from the signed bundle, never read from a message).
426
+ *
427
+ * SUITE 12 CLASSIFICATION. Every field here is class T at the SCHEMA layer,
428
+ * because a schema constrains shape and shape is not authority. What lifts
429
+ * them is stated on each property and enforced elsewhere: `acknowledger`
430
+ * becomes B once the signature over the canonical object verifies against
431
+ * the key the SIGNED BUNDLE registers for that name (ACK-2/ACK-4, and row 27
432
+ * of the classification table records that it was T through v1.3.11 -- that
433
+ * was T-31); `proposal_hash` becomes B against the Executor's own recomputed
434
+ * hash (ACK-3); `policy_bundle_hash` and `bundle_epoch` are compared against
435
+ * the bundle the Executor already holds and are never used to SELECT a
436
+ * bundle.
437
+ *
438
+ * WHAT THIS SCHEMA DOES NOT CONSTRAIN, STATED BEFORE ANYTHING IT DOES. (1)
439
+ * ORDER. The reference consumes the ledger slot inside `_verify_ack`, BEFORE
440
+ * the `decision`, DR-5 membership and DR-9 non-operator tests run -- so
441
+ * submitting a captured, validly signed REPUDIATE object to the confirmation
442
+ * endpoint burns its id, the genuine repudiation is then refused as a replay
443
+ * (CL-3), and a REVERSIBLE unsampled floor-HIGH action releases on silence
444
+ * under DR-7 with its veto suppressed. Executed, not reasoned about:
445
+ * `confirm(REPUDIATE)` refuses at ACK-1, `repudiate` then refuses at CL-3,
446
+ * `release` then succeeds. DR-5 exists to stop a veto being suppressed by
447
+ * the approval chain, and no shape constraint can express this. (2) The
448
+ * ACK-6 window LENGTH bound, and that the acknowledgement falls inside the
449
+ * hold window it is acknowledging. (3) CR-3's requirement that the signature
450
+ * key set exactly match the declared suite, and CR-4's containment against
451
+ * the bundle floor -- both are decided against the signed bundle, which a
452
+ * schema cannot see. (4) DR-3 delivery and DR-8 deliverability. (5) Whether
453
+ * any human read anything: A-8 is untouched, and acknowledgement remains an
454
+ * input to the model rather than evidence of comprehension.
455
+ * Generated from acknowledgement.schema.json.
456
+ */
457
+ export interface Acknowledgement {
458
+ /**
459
+ * The signed object. Carried in its own member rather than flattened beside
460
+ * the signature because the id and the signature are both taken over THIS
461
+ * map alone (ACK-2, ACK-5): flattening would put the signature inside the
462
+ * bytes it signs. The reference refuses an acknowledgement with no `obj` at
463
+ * ACK-1 by name, because the v1.3.11 form that this member's absence
464
+ * identifies is a bare string -- T-31, an identity anyone could type.
465
+ */
466
+ readonly obj: AcknowledgementObject;
467
+ /**
468
+ * CR-2: a signature under a suite is a MAP from primitive name to signature
469
+ * value, one entry per primitive of the suite named in `obj.alg`. A bare
470
+ * scalar signature MUST NOT be accepted under any suite, not even a
471
+ * single-primitive one -- format leniency is a downgrade in disguise, and
472
+ * the reference refuses a non-map before it looks at any value.
473
+ *
474
+ * OPEN MAP, DELIBERATELY. The suite registry lives in the SIGNED BUNDLE
475
+ * (CR-1) and CR-7 makes migration forward-only, requiring a deployment to
476
+ * verify structures signed under a HIGHER suite than its floor. A closed
477
+ * `{classical, pq}` object here would hard-code one suite into the wire
478
+ * format and make CR-7 unimplementable; it would also be a second registry
479
+ * of primitive names beside the bundle's, which is the two-definitions
480
+ * defect.
481
+ *
482
+ * CR-3 IS CONJUNCTIVE AND IS NOT EXPRESSIBLE HERE. Verification succeeds
483
+ * only if EVERY primitive of the declared suite verifies and the supplied
484
+ * set EXACTLY matches it -- no missing entries and no extra ones, an
485
+ * accepted extra primitive being an undeclared code path chosen by the party
486
+ * under verification. Whether this map's key set matches depends on
487
+ * `obj.alg` resolved against the bundle, so it is checked in code and not
488
+ * here.
489
+ *
490
+ * THE VALUE TYPE IS NOT PINNED BY ANY CLAUSE, AND THE TWO IMPLEMENTATIONS OF
491
+ * IT DISAGREE. WE-4 pins `b64:` plus padded RFC 4648 4 base64 for `any
492
+ * signature or byte string carried as text`, while
493
+ * `reference/src/acp_executor.py` hex-encodes every signature value with the
494
+ * comment that signatures travel through JSON and raw bytes do not, and
495
+ * `spec/schemas/bundle/attesters.schema.json` carries public keys as bare
496
+ * base64 with no prefix at all. Three encodings of one concept in one
497
+ * repository. A pattern is therefore NOT asserted here, because asserting
498
+ * one would decide a normative question by writing a schema -- exactly what
499
+ * ACP-87 was filed rather than done.
500
+ *
501
+ * ABSENT-ENTRY RULE, carried as documentation and NOT enforced by this type,
502
+ * because this is a message and not signed policy. An entry absent from this
503
+ * table is a REFUSAL under CR-3, never a default value: a primitive named by
504
+ * the declared suite and absent from this map is a hybrid signature verified
505
+ * on fewer primitives than it claims, which is the OR composition CR-3
506
+ * forbids: a hybrid verifier that accepts when EITHER half checks out is
507
+ * weaker than its weaker member, so an attacker who breaks one primitive is
508
+ * unconstrained by the other. Refuse, never treat a missing primitive as
509
+ * satisfied and never verify the subset that is present.
510
+ *
511
+ * Schema constraints, carried as documentation and NOT enforced by this
512
+ * type: values minLength 1.
513
+ */
514
+ readonly sig: Readonly<Record<string, string>>;
515
+ }
516
+ /**
517
+ * The lenient projection of Acknowledgement: every field optional, every enum
518
+ * degraded to `string`. What a verifier parses. Never make a decision on
519
+ * one — the fail-safe lookups live on the strict type only.
520
+ */
521
+ export interface RawAcknowledgement {
522
+ readonly obj?: RawAcknowledgementObject;
523
+ readonly sig?: Readonly<Record<string, string>>;
524
+ }
525
+ /**
526
+ * The signed acknowledgement object -- the bytes the signature covers and
527
+ * the bytes the id is derived from. ACK-1's closed field set, exactly
528
+ * `ACK1_FIELDS` in `reference/src/acp_ack.py`, in the order that file
529
+ * declares them. Adding a field here is a BREAKING change, never an additive
530
+ * one, for the Z4 reason on the root: a field set with an optional member
531
+ * gives one object two ids and two ledger slots.
532
+ * Generated from acknowledgement.schema.json#/$defs/acknowledgement_object.
533
+ */
534
+ export interface AcknowledgementObject {
535
+ /**
536
+ * THE BINDING, and the reason the object exists rather than a name. ACK-3:
537
+ * this MUST equal the hash the Executor recomputed for the action it is
538
+ * about to release; an acknowledgement of a different action is not an
539
+ * acknowledgement of this one. Without it a captured acknowledgement for a
540
+ * routine action is replayed onto a dangerous one, which is Y1 one layer up
541
+ * -- there a genuine quorum raised for P1 verified against an
542
+ * attacker-chosen P2.
543
+ *
544
+ * The Executor compares against ITS OWN recomputed hash, never against a
545
+ * hash the same message supplied: a transmitted identifier is a name for a
546
+ * binding, not evidence of one (RES-8).
547
+ */
548
+ readonly proposalHash: Sha256Hash;
549
+ /**
550
+ * The signed bundle the acknowledger was acting under. ACK-3 requires it to
551
+ * equal the hash of the bundle the Executor holds, so an acknowledgement
552
+ * raised under a superseded policy cannot release an action graded under the
553
+ * current one. It selects nothing: the Executor already has its bundle and
554
+ * this value is compared to it, because a hash that chose which policy to
555
+ * load would let the acknowledging party choose the policy that judges them.
556
+ */
557
+ readonly policyBundleHash: Sha256Hash;
558
+ /**
559
+ * Epoch of that bundle, compared for equality by ACK-3 alongside the hash.
560
+ * Carried as well as the hash because the two fail differently and a reader
561
+ * should see which: a hash mismatch means a different bundle, an epoch
562
+ * mismatch means a different version of the policy basis. Epoch MONOTONICITY
563
+ * is not a property of this field -- it is enforced at the KMS against a
564
+ * durable high-water mark (RAD-3), never from a value a message carries.
565
+ *
566
+ * Schema constraints, carried as documentation and NOT enforced by this
567
+ * type: minimum 0.
568
+ */
569
+ readonly bundleEpoch: number;
570
+ /**
571
+ * The identity acknowledging, INSIDE the signature. ACK-4 is the whole of
572
+ * T-31's lesson: the identity used for the DR-9 non-operator test and the
573
+ * DR-5 notified-membership test is taken FROM THE SIGNED BYTES, never from
574
+ * the call that delivered the object. Through v1.3.11 it was a bare string
575
+ * checked only for set membership and inequality with the operator -- the
576
+ * release gate held no key material at all, and an irreversible action
577
+ * released with `human_verified` true and zero signatures verified.
578
+ *
579
+ * ACK-2 makes the name resolvable rather than authoritative: it selects a
580
+ * key from the attester registry inside the SIGNED bundle, and an identity
581
+ * with no registered key cannot acknowledge anything. The signature
582
+ * establishes who signed; this field only says which enrolled key to check
583
+ * it against. It does not authorise anyone to name themselves.
584
+ *
585
+ * BOUNDED AT 128 BECAUSE THE REGISTRY IT MUST RESOLVE IN IS.
586
+ * `spec/schemas/bundle/attesters.schema.json` bounds its `propertyNames` at
587
+ * 1..128, so a longer value cannot name an enrolled attester and the same
588
+ * bound belongs on both sides of the lookup. It is also the bound
589
+ * `AttestationObject.operator` carries, which matters because DR-9 compares
590
+ * this value to that one: two bounds on one identity space would make a name
591
+ * that is a valid acknowledger and an invalid operator, and the non-operator
592
+ * test compares them.
593
+ *
594
+ * Schema constraints, carried as documentation and NOT enforced by this
595
+ * type: minLength 1, maxLength 128.
596
+ */
597
+ readonly acknowledger: string;
598
+ /**
599
+ * Which of the two acts this object is: CONFIRM is the DR-9 positive
600
+ * acknowledgement that permits an IRREVERSIBLE floor-HIGH action to release;
601
+ * REPUDIATE is the DR-4 veto that blocks release with a critical alert and
602
+ * requires a fresh quorum rather than a replay.
603
+ *
604
+ * HOISTED INTO $defs rather than written inline for the reason
605
+ * `attester_role` gives in the bundle schemas: an inline enum has no name,
606
+ * and a generator inventing one invents a name that drifts.
607
+ *
608
+ * DELIBERATELY NOT ORDERED. A repudiation does not outrank a confirmation on
609
+ * some scale; they are different acts with different consequences, and
610
+ * DR-4's precedence -- a repudiated action fails closed at release however
611
+ * many confirmations it also collected -- is enforced in the release step
612
+ * against `repudiated_by`, not by comparing two enum members.
613
+ *
614
+ * THIS FIELD IS INSIDE THE SIGNATURE, AND THAT IS THE POINT. It is the
615
+ * discriminator between the two legs, so an attacker who could rewrite it
616
+ * would turn a veto into an approval. Being an ACK-1 field it is
617
+ * signature-covered, exactly as CR-5 requires of `alg` and AT-1 of
618
+ * `operator` after Y4. A verifier MUST still check it against the endpoint
619
+ * that received the object: a valid REPUDIATE object is not a confirmation
620
+ * merely because it was posted to the confirmation path.
621
+ */
622
+ readonly decision: AcknowledgementDecision;
623
+ /**
624
+ * Freshness material, so that two acknowledgements of the same action under
625
+ * the same bundle by the same party are distinct objects with distinct ids.
626
+ * ACK-5 consumes the derived id in the ledger, so this is what stops a
627
+ * second honest acknowledgement colliding with the first.
628
+ *
629
+ * ITS TYPE AND ITS ENTROPY ARE UNSTATED, AND THE REFERENCE MAKES IT
630
+ * PREDICTABLE. AT-1 requires a 128-bit FRESH nonce for the Attestation
631
+ * Object and WE-4 pins that value's wire type; no clause says anything about
632
+ * this one, and `make_ack` builds it as the acknowledger's own name joined
633
+ * to a millisecond timestamp (observed: `ack-op_1121-1787598182147`). It is
634
+ * therefore neither random nor unique -- two acknowledgements by one party
635
+ * in one millisecond collide -- and, because every other field of the object
636
+ * is already known to the operator, the derived ledger id is guessable to
637
+ * within the timestamp resolution. The ledger id is the single-use key, so a
638
+ * guessed id can be claimed before the honest acknowledgement arrives, and
639
+ * the honest one is then refused as a replay.
640
+ *
641
+ * `minLength: 1` and nothing more, deliberately. A zero-length nonce
642
+ * contributes no freshness at all, which is a shape claim a schema may make;
643
+ * a length or an alphabet would be this schema inventing the normative rule
644
+ * that is missing, and inventing it here is how a type comes to exist in a
645
+ * schema and nowhere else -- the defect the sibling wire schema records
646
+ * against its own `att_nonce`.
647
+ *
648
+ * Schema constraints, carried as documentation and NOT enforced by this
649
+ * type: minLength 1.
650
+ */
651
+ readonly ackNonce: string;
652
+ /**
653
+ * When the acknowledgement was raised, in seconds since the Unix epoch.
654
+ * ACK-6 requires `issued_at <= now <= expires_at` at the consumer, evaluated
655
+ * against NTS-disciplined wall time within the L-20 skew allowance (leg F9.1
656
+ * carries that dependency).
657
+ *
658
+ * THIS IS THE ONE STRUCTURE IN THE WIRE FORMAT WHOSE TIMES ARE NUMBERS, AND
659
+ * THE REFERENCE PUTS FLOATS IN IT. Section 9.2's receipt and AT-1's
660
+ * `expires_at` are RFC 3339 strings; only the acknowledgement is numeric,
661
+ * and `make_ack` defaults to `time.time()`, so a float reaches the canonical
662
+ * bytes on every production path. AT-8a forbids a float in a canonical
663
+ * structure because a float is not deterministically encoded, and `canon()`
664
+ * enforces that only at the top level -- a nested float is serialised,
665
+ * observed as `{"ack_nonce":...,"issued_at":1787598182.1470501,...}`. Two
666
+ * runtimes formatting that value differently derive two ids for one
667
+ * acknowledgement, which is precisely the encoding split AT-8a exists to
668
+ * prevent and precisely the ledger-slot defect Y1b closed.
669
+ *
670
+ * `integer` is pinned here because it is the only one of the three encodings
671
+ * present in this repository -- float, integer, RFC 3339 -- that AT-8a
672
+ * permits. Which of the remaining two the acknowledgement SHOULD carry is a
673
+ * normative question ZIFFER-SPEC-001 does not answer, and pinning the
674
+ * narrower of what exists is not the same as answering it.
675
+ *
676
+ * Schema constraints, carried as documentation and NOT enforced by this
677
+ * type: minimum 0.
678
+ */
679
+ readonly issuedAt: number;
680
+ /**
681
+ * When the acknowledgement stops being valid, in seconds since the Unix
682
+ * epoch, on the same clock and with the same float caveat as `issued_at`.
683
+ *
684
+ * THE WINDOW LENGTH IS ENFORCED BY THE CONSUMER, NOT READ FROM THE MESSAGE.
685
+ * ACK-6 requires `expires_at - issued_at` to be bounded, and that bound is
686
+ * checked by the Executor -- the Y2 lesson, where receipt validity LENGTH
687
+ * was checked nowhere and lived only in the value the signing party wrote,
688
+ * permitting long-lived pre-positioned receipts. A schema cannot express a
689
+ * relation between two fields, so this constraint is stated and not
690
+ * asserted.
691
+ *
692
+ * THE BOUND ITSELF HAS NO CLAUSE. The reference uses 120 seconds and its
693
+ * comment says the figure mirrors L-14 -- but L-14 is the ALLOW receipt
694
+ * validity ceiling, a limit on a different artifact with a different
695
+ * lifetime, and the section 13 limits table has no row for an
696
+ * acknowledgement window. A borrowed number is not a normative bound;
697
+ * recorded rather than restated as one.
698
+ *
699
+ * Schema constraints, carried as documentation and NOT enforced by this
700
+ * type: minimum 0.
701
+ */
702
+ readonly expiresAt: number;
703
+ /**
704
+ * The signature suite `sig` is under. CR-1: every signed structure MUST
705
+ * carry an explicit `alg` naming a suite REGISTERED IN THE SIGNED BUNDLE,
706
+ * and an unregistered or unknown suite MUST fail closed. CR-5: it is
707
+ * signature-covered, so an issuer cannot rewrite the suite without
708
+ * invalidating the object -- an acknowledger cannot downgrade its own suite.
709
+ *
710
+ * NO ENUM, DELIBERATELY. The suite set is a bundle property, so an enum here
711
+ * would be a second registry of suite names beside the signed one, and two
712
+ * definitions of one object is the defect this repository is built around.
713
+ * It would also break CR-7, which makes migration forward-only and requires
714
+ * a deployment to verify structures signed under a suite HIGHER than its
715
+ * floor: a suite the schema had not been updated to list would be refused at
716
+ * the wrong layer, for the wrong reason, with the wrong clause name.
717
+ *
718
+ * CR-4 IS CONTAINMENT AND IS DECIDED AGAINST THE BUNDLE. The floor is
719
+ * satisfied when the named suite's primitive set CONTAINS every primitive of
720
+ * the bundle floor's suite -- not by rank. The rank table this replaced was
721
+ * a total order over sets that are not comparable, and it let a deployment
722
+ * whose signed floor said hash-based post-quantum silently accept a lattice
723
+ * one: not a stronger suite accepted, a DIFFERENT hardness assumption
724
+ * substituted, with the floor check reporting satisfaction. The reference
725
+ * checks the floor BEFORE verifying the signature, so a below-floor
726
+ * acknowledgement refuses at CR-4 rather than at ACK-2.
727
+ *
728
+ * Schema constraints, carried as documentation and NOT enforced by this
729
+ * type: minLength 1.
730
+ */
731
+ readonly alg: string;
732
+ }
733
+ /**
734
+ * The lenient projection of AcknowledgementObject: every field optional, every enum
735
+ * degraded to `string`. What a verifier parses. Never make a decision on
736
+ * one — the fail-safe lookups live on the strict type only.
737
+ */
738
+ export interface RawAcknowledgementObject {
739
+ readonly proposalHash?: string;
740
+ readonly policyBundleHash?: string;
741
+ readonly bundleEpoch?: number;
742
+ readonly acknowledger?: string;
743
+ readonly decision?: string;
744
+ readonly ackNonce?: string;
745
+ readonly issuedAt?: number;
746
+ readonly expiresAt?: number;
747
+ readonly alg?: string;
748
+ }
749
+ /**
750
+ * The external commitment to one per-tenant audit chain head: what an
751
+ * anchoring party receives under AU-3, and what a verifier running outside
752
+ * the production trust domain compares its own recomputation against under
753
+ * AU-4. Carried on leg F6.4 of the leg register (ZIFFER-DEPLOY-001 Annex A);
754
+ * leg F6.3 carries the `chain_hash` this object commits to.
755
+ *
756
+ * WHY THE OBJECT EXISTS AT ALL. AU-3 says it in one line: an unanchored
757
+ * in-database chain is tamper-DECORATED, not tamper-EVIDENT. The chain lives
758
+ * inside the production trust domain and a compromised audit writer can
759
+ * rewrite it; what such a writer cannot rewrite is an anchor already
760
+ * published. Everything below serves one comparison - recompute the head
761
+ * from the records under AU-1, then check it against what was anchored. AU-4
762
+ * makes a head that fails to extend a previously anchored head a critical
763
+ * integrity alert, and AU-7 makes coverage by an anchor a precondition of
764
+ * releasing a floor-HIGH action rather than a report written afterwards.
765
+ *
766
+ * NOTHING BELOW IS EVIDENCE ON ITS OWN (RES-8 / RES-9 / RES-10). Every field
767
+ * here is a value the publisher sends. The guarantee comes from two things
768
+ * outside this object: the store is append-only and sits outside the
769
+ * production trust domain (AU-4, ZIFFER-DEPLOY-001 DP-11), and the anchor is
770
+ * verifiable by any party holding the anchor public keys (AU-3a). A verifier
771
+ * that read a head from the chain it is auditing, or accepted an anchor
772
+ * because the audited party said it existed, has taken a derived security
773
+ * value from the party under verification - the class this architecture has
774
+ * hit five times, C2 -> X1 -> Y1 -> Z3 -> W1, each time inside machinery the
775
+ * previous fix introduced.
776
+ *
777
+ * WHY THE FIELD SET IS CLOSED, AND WHY THAT IS A SECURITY PROPERTY. Every
778
+ * field is REQUIRED and `additionalProperties` is false. An anchor is a
779
+ * signed structure (CR-1), so an optional field gives one semantic anchor
780
+ * two canonical encodings, two signed preimages and two values - the Z4
781
+ * defect that AT-8b closes for the Attestation Object one schema over. Here
782
+ * the consequence is worse than a duplicate id, because it lands on an
783
+ * honest party: a publisher that omits the field and a verifier that encodes
784
+ * it as absent-present compute different bytes, the signature fails, and
785
+ * AU-4 raises a critical integrity alert with no attacker anywhere. That is
786
+ * exactly what the AU-1 correction records happening to this subsystem
787
+ * already, from a formula rather than a field.
788
+ *
789
+ * WHAT THIS SCHEMA DOES NOT MODEL, STATED SO THE FILE IS NOT READ WIDER THAN
790
+ * IT IS.
791
+ * (1) The signature envelope. CR-2 makes a signature a map from primitive to
792
+ * signature value and CR-3 requires the supplied set to match the declared
793
+ * suite exactly. That map is keyed by the primitive names whose one home is
794
+ * `spec/schemas/bundle/signature.schema.json`, in the other schema root. A
795
+ * `$ref` crossing roots resolves now (ACP-78 M1, for a named scalar), so
796
+ * what still blocks the envelope is the OPEN MAP and not the reference: the
797
+ * generator's only rule for one is `x-acp-absent`, which states what the
798
+ * SIGNED POLICY says when a lookup misses, and a message carries no
799
+ * authority to answer that -- in the wire root such a rule is now carried as
800
+ * documentation and no accessor is generated from it. So the envelope is
801
+ * disclosed as absent rather than invented in a shape the specification
802
+ * never gave it. This follows the precedent of the sibling Attestation
803
+ * Object schema, which models the AT-1 body and not the surrounding `kind` /
804
+ * `attester` / `sig`.
805
+ * (2) AU-3's mechanism and its evidence. AU-3 permits a public transparency
806
+ * log with inclusion proofs, a threshold signature from at least 3
807
+ * independent parties, or RFC 3161 timestamping. Those three have
808
+ * incompatible evidence shapes - a Merkle inclusion proof, a set of party
809
+ * signatures, a CMS TimeStampToken - and no clause says which the anchor
810
+ * carries, how a verifier learns which was used, or which of the anchor
811
+ * public keys AU-3a means. An outside auditor cannot verify this object from
812
+ * its fields alone, and saying so is more useful than inventing a
813
+ * discriminator.
814
+ * (3) Ordering and rate. AU-3's <= 10 min interval (L-19) and its
815
+ * at-every-bundle-activation rule are properties of a SEQUENCE of anchors;
816
+ * AU-4's extension check is a relation between two of them; AU-7's
817
+ * anchor-before-release is an ordering between an anchor and an execution.
818
+ * None is a property of one object and none is expressible here. They are
819
+ * per-implementation obligations (`spec/vectors/OBLIGATIONS.md`), and
820
+ * `reference/suites/audit_suite.py` is where the reference discharges them.
821
+ * (4) AU-5 retention (>= 24 months or per regulation), AU-6 outage
822
+ * behaviour, and §11.3's eight reconciliation checks. All are about what a
823
+ * deployment does with anchors, never about what one is.
824
+ *
825
+ * ENCODING, AND A GAP. §11.2 fixes `canonical()` for §11 as the single
826
+ * canonicaliser AT-8a requires - canonical CBOR, RFC 8949 §4.2 - and
827
+ * requires chain values to be carried, stored, anchored and fed forward as
828
+ * the 71-character `sha256:` string. That is the only canonicaliser §11
829
+ * names, so it is the reading this schema is written against. But no clause
830
+ * states the anchor's own signed preimage: whether the signature covers this
831
+ * whole object, the `chain_hash` alone, or a mechanism-specific structure.
832
+ * AU-3a cannot hold until it is pinned, for the reason AU-1 already records
833
+ * - two implementations signing different bytes each refuse the other's
834
+ * anchor, and at the verifier a refusal is indistinguishable from tampering.
835
+ * JSON here is diagnostic form, never an accepted input.
836
+ * Generated from anchor.schema.json.
837
+ */
838
+ export interface Anchor {
839
+ /**
840
+ * The tenant whose chain this anchor commits to. AU-1 makes chains
841
+ * per-tenant and AU-2 makes `(tenant_id, seq)` unique and gapless, so a head
842
+ * with no tenant names no chain: a verifier holding anchors from many
843
+ * tenants cannot tell which recomputation to compare against, and AU-4's
844
+ * extension check has no predecessor to look for.
845
+ *
846
+ * It is a NAME for something already inside the chain, not a second source
847
+ * of it. AU-8 derives genesis from a tenant-creation record over
848
+ * `{tenant_id, created_at, bundle_epoch, schema_version}`, so the tenant is
849
+ * committed from `chain_hash_0` onward and a verifier that trusted this
850
+ * field over the chain would be trusting the publisher about which chain it
851
+ * published.
852
+ *
853
+ * The specification pins no form for a tenant identifier - §9.2 renders one
854
+ * as `ten_04qf` in diagnostic JSON and no clause types it. The only stated
855
+ * bound is L-04 (max string field length 2,048 chars, no exceptions), so it
856
+ * is applied here rather than inventing a narrower one. That L-04's
857
+ * enforcement point is the Input/Output Schema registry and is being read as
858
+ * a general wire bound is an assumption; it is recorded rather than made
859
+ * quietly.
860
+ */
861
+ readonly tenantId: TenantId;
862
+ /**
863
+ * The sequence number of the record whose head is anchored. AU-2 makes
864
+ * `(tenant_id, seq)` unique and gapless per tenant, and gaplessness is what
865
+ * makes deletion detectable at all - a sequence with holes has no missing
866
+ * record, only a shorter one.
867
+ *
868
+ * Zero is the genesis anchor. AU-8 requires it to be published IMMEDIATELY
869
+ * on tenant creation rather than on AU-3's <= 10 min schedule, because
870
+ * without an anchored genesis the destruction of an entire tenant chain is
871
+ * detectable only after the first scheduled anchor, and a chain destroyed
872
+ * inside that window leaves no evidence it ever existed.
873
+ *
874
+ * An anchor at `seq` commits transitively to every record from 0 to `seq`,
875
+ * because AU-1 chains each head over the previous one. That is derived from
876
+ * the chain construction rather than promised by this field, and it is what
877
+ * §11.3(g) relies on when it asks for an anchor COVERING a floor-HIGH
878
+ * execution record.
879
+ *
880
+ * Schema constraints, carried as documentation and NOT enforced by this
881
+ * type: minimum 0.
882
+ */
883
+ readonly seq: number;
884
+ /**
885
+ * The anchored head: `chain_hash_seq` as AU-1 constructs it, `H({prev,
886
+ * record})` over the canonical encoding of a two-key map whose key names are
887
+ * part of the preimage. Carried as the 71-character string §11.2 pins, never
888
+ * as the 32 raw digest bytes - that type is pinned in the specification
889
+ * because an implementer who fed forward raw bytes built a different chain
890
+ * while following the text.
891
+ *
892
+ * A NAME FOR A BINDING, NOT EVIDENCE OF ONE. The verifier's job under AU-4
893
+ * is to recompute the head from the records and compare, so this value is
894
+ * one half of a comparison whose other half the verifier computes itself. It
895
+ * is trustworthy only to the degree that the store holding it is append-only
896
+ * and outside the production trust domain; the field carries no authority of
897
+ * its own.
898
+ *
899
+ * THE AU-1 CORRECTION IS WHY THAT SENTENCE IS WORTH WRITING. Through v1.3.13
900
+ * the clause read `SHA-256(chain_hash_{n-1} || canonical(record_n))`, a
901
+ * concatenation, while the reference hashed the canonical encoding of the
902
+ * two-key map. Different preimage, different chain from record 1 onward. The
903
+ * damage was not that an implementer failed to match: an honest third party
904
+ * implementing the clause literally recomputes a different head on an
905
+ * untampered chain, so AU-3a's independent verifiability did not hold, and
906
+ * AU-4 routed an honest verifier into this system's own critical-alert
907
+ * channel with no attacker present. The clause moved and the implementations
908
+ * did not, and the reasoning was published rather than taken silently.
909
+ */
910
+ readonly chainHash: Sha256Hash;
911
+ /**
912
+ * When the anchor was published. §11.3(g) requires every floor-HIGH
913
+ * execution record to be matched to an anchor DATED AT OR BEFORE release,
914
+ * and AU-7 makes that ordering a release precondition rather than a report,
915
+ * so the comparison needs a date and this is it.
916
+ *
917
+ * WHO STAMPS IT IS THE WHOLE QUESTION, AND NO CLAUSE ANSWERS IT. If the
918
+ * anchoring party stamps it, the field is an external attestation of time
919
+ * and AU-7 means what it says. If the publisher stamps it, the party under
920
+ * verification dates its own alibi: a compromised audit writer backdates the
921
+ * anchor it has just published, and §11.3(g) - one of the mechanisms by
922
+ * which several disclosed residuals are said to be DETECTED rather than
923
+ * prevented - compares two numbers written by the same hand. Detection that
924
+ * can be erased is not detection, in AU-7's own words, and a timestamp the
925
+ * audited party chooses is erasure with an extra step. The Python reference
926
+ * is the second reading: `AnchorService.publish(tenant, seq, head, now)` in
927
+ * `reference/src/acp_audit.py` takes `now` from its caller, and the caller
928
+ * is `AuditChain.anchor_now`, inside the production trust domain. Recorded
929
+ * and filed; a schema cannot settle it.
930
+ *
931
+ * THE TYPE IS NOT THE SPECIFICATION'S EITHER. No clause types an anchor
932
+ * timestamp. RFC 3339 is used because §9.2's `issued_at` and `expires_at`
933
+ * are RFC 3339 instants and §12.5 disciplines every receipt-issuing and
934
+ * receipt-verifying clock with NTS inside the L-20 skew allowance, so a
935
+ * second time format in the one subsystem whose value is retrospective would
936
+ * be a second encoding of one thing. The reference stores a POSIX float, so
937
+ * the two disagree today; that is a divergence to close in one place, not
938
+ * two.
939
+ */
940
+ readonly anchoredAt: Rfc3339Instant;
941
+ /**
942
+ * The signature suite the anchor was signed under. CR-1 names the audit
943
+ * anchor explicitly among the structures that MUST carry an explicit `alg`
944
+ * identifying a suite registered in the signed bundle, and an unregistered
945
+ * or unknown suite MUST fail closed.
946
+ *
947
+ * IT IS IN THE BODY BECAUSE OF WHAT HAPPENS WHEN IT IS NOT. PB-8 records the
948
+ * reasoning for the bundle SIGNATURE file and it transfers unchanged: the
949
+ * declared suite must be inside what the signature covers, or an attacker
950
+ * relabels a hybrid structure as classical, the verifier obligingly requires
951
+ * one primitive, and CR-3's conjunctive guarantee is decorative at the point
952
+ * it is loaded. An anchor is the structure a downgrade is worth most against
953
+ * - receipts live 120 s (L-14) and attestations 60 min (AT-1), while the
954
+ * audit chain's non-repudiation value is retrospective and measured in
955
+ * years, which is why §1 tells deployments to prioritise post-quantum
956
+ * signing on anchoring and bundle signing over receipt signing.
957
+ *
958
+ * THE SUITE-NAME TYPE HAS ONE HOME AND IT IS NOT THIS FILE. The enumerated
959
+ * names are `SuiteId` at
960
+ * `spec/schemas/bundle/signature.schema.json#/$defs/suite_id`, in the other
961
+ * schema root, alongside the primitive names CR-2's signature map is keyed
962
+ * by. Declaring the enum a second time here would be two definitions of one
963
+ * object - the defect that generator exists to prevent, committed in a file
964
+ * that cites it. Where a type shared by both roots lives is now decided
965
+ * (ACP-78 M1): a named SCALAR is `$ref`d across roots and imported rather
966
+ * than redeclared, which is what the Decision Receipt's `alg` does. This
967
+ * field deliberately does NOT do that, and the reason is the conflict
968
+ * recorded below rather than a missing mechanism: AU-3 requires an EXTERNAL
969
+ * anchoring mechanism while CR-1/CR-4 require the deployment's registered
970
+ * floor, no public transparency log or timestamping authority signs under
971
+ * either registered suite, and typing this field as `SuiteId` would decide
972
+ * that conflict by writing a schema. So the constraint is stated and the
973
+ * type is pointed at rather than made in passing in a schema about anchors.
974
+ *
975
+ * CR-4 IS SATISFIED BY CONTAINMENT, NEVER BY RANK, and the floor is read
976
+ * from the signed bundle rather than from this field. A verifier that
977
+ * ordered suites would place hybrid above `slhdsa128s` and serve a lattice
978
+ * signature to a deployment that chose hash-based precisely to rest on no
979
+ * lattice assumption. CR-6 SHOULD-recommends SLH-DSA for audit anchoring,
980
+ * and `slhdsa128s` is declared and not implemented in either implementation
981
+ * (ZIFFER-DEPLOY-001 DP-27), so a deployment following CR-6 to the letter
982
+ * cannot run today. That tension is the specification's and is named here
983
+ * rather than resolved by a schema.
984
+ *
985
+ * Schema constraints, carried as documentation and NOT enforced by this
986
+ * type: minLength 1, maxLength 2048.
987
+ */
988
+ readonly alg: string;
989
+ }
990
+ /**
991
+ * The lenient projection of Anchor: every field optional, every enum
992
+ * degraded to `string`. What a verifier parses. Never make a decision on
993
+ * one — the fail-safe lookups live on the strict type only.
994
+ */
995
+ export interface RawAnchor {
996
+ readonly tenantId?: string;
997
+ readonly seq?: number;
998
+ readonly chainHash?: string;
999
+ readonly anchoredAt?: string;
1000
+ readonly alg?: string;
1001
+ }
1002
+ /**
1003
+ * One receipt per attestation (CL-3, T-14). A second receipt bearing the
1004
+ * same attester signatures and a fresh nonce is rejected at §9.3 step 7
1005
+ * because the id is already consumed.
1006
+ *
1007
+ * THE OBLIGATION THIS CLAIM DEPENDS ON IS NOT AT THE LEDGER. §9.3 step 7b(v)
1008
+ * requires the Executor to RECOMPUTE `attestation_id :=
1009
+ * SHA-256(canonical(obj))` per AT-1 and to claim the recomputed value, never
1010
+ * an id read from the receipt -- that is Y1b, and the Attestation Object
1011
+ * schema carries no `attestation_id` property for the same reason. The
1012
+ * ledger cannot tell a recomputed id from a transmitted one; both arrive
1013
+ * here as the same 71 characters. So this field is class T AT THE LEDGER and
1014
+ * the control lives entirely at the Executor. Recording that here is the
1015
+ * point: a reader who takes ledger consumption as the whole of the T-14
1016
+ * defence has the same misreading v1.3.2 shipped, where CL-3 guaranteed one
1017
+ * receipt per attestation and never asked whether it was the RIGHT receipt.
1018
+ * Generated from ledger_claim.schema.json#/$defs/attestation_id_claim.
1019
+ */
1020
+ export interface AttestationIdClaim {
1021
+ readonly tenantId: TenantId;
1022
+ readonly artifactClass: 'attestation_id';
1023
+ /**
1024
+ * The recomputed `attestation_id` (AT-1): SHA-256 over the canonical CBOR
1025
+ * encoding of the whole Attestation Object (AT-8a). Carried with its
1026
+ * `sha256:` prefix per §11.2, which pins the type to the character and
1027
+ * records why it had to -- an implementer who fed forward raw digest bytes
1028
+ * built a different chain while following the text. Here the same slip is
1029
+ * worse than a different chain: a prefixed issuer and an unprefixed verifier
1030
+ * derive two ids for one object and claim two ledger slots, which is Z4 and
1031
+ * T-14 reopening with no optional field anywhere in reach.
1032
+ */
1033
+ readonly identifier: Sha256Hash;
1034
+ }
1035
+ /**
1036
+ * The lenient projection of AttestationIdClaim: every field optional, every enum
1037
+ * degraded to `string`. What a verifier parses. Never make a decision on
1038
+ * one — the fail-safe lookups live on the strict type only.
1039
+ */
1040
+ export interface RawAttestationIdClaim {
1041
+ readonly tenantId?: string;
1042
+ readonly artifactClass?: string;
1043
+ readonly identifier?: string;
1044
+ }
1045
+ /**
1046
+ * A signed human act bound to a `proposal_hash`. Carried on leg F4.4 of the
1047
+ * leg register (ZIFFER-DEPLOY-001 Annex A). F4.3 named the signing act
1048
+ * inside the approval domain, which crosses no domain boundary and is
1049
+ * therefore not a leg (DP-83); ACP-86 retired the id rather than reusing it,
1050
+ * and AT-1 and AT-8a are cited on F4.4, where the object arrives.
1051
+ *
1052
+ * WHY THIS SCHEMA IS CLOSED, AND WHY THAT IS THE SECURITY PROPERTY RATHER
1053
+ * THAN A STYLE CHOICE. AT-8b: every AT-1 field is REQUIRED; there are no
1054
+ * optional fields, no defaultable fields, and no extension points. An object
1055
+ * carrying an unknown field, or omitting any AT-1 field, MUST be rejected —
1056
+ * never normalized, never defaulted. That is why `additionalProperties` is
1057
+ * false and why `required` lists every property, and it is the one place in
1058
+ * this repository where an absent-value rule would be a DEFECT rather than a
1059
+ * control: `x-acp-absent` deliberately appears nowhere below.
1060
+ *
1061
+ * The reason is Z4. AT-8a pinned the canonicalizer but not the field set.
1062
+ * Given ANY optional field, an object present-as-null and the same object
1063
+ * with the field omitted are EACH valid canonical CBOR, hash to TWO DISTINCT
1064
+ * ids, and therefore claim TWO ledger slots — one attestation, consumed
1065
+ * twice. T-14 attestation amplification reopens through the very mechanism
1066
+ * Y1b closed. Canonicalization cannot fix it: the ambiguity is in the field
1067
+ * set, not the encoding. Mechanized as `Z4_OptionalFieldYieldsTwoIds`.
1068
+ *
1069
+ * CONSEQUENCE FOR ANYONE EDITING THIS FILE: adding a field here is a
1070
+ * BREAKING change requiring a `receipt_version` increment, never an additive
1071
+ * one (AT-8b). A future revision that adds an optional field to be helpful
1072
+ * reopens T-14.
1073
+ *
1074
+ * ENCODING. The wire form is canonical CBOR, RFC 8949 §4.2 (AT-8a) — the
1075
+ * same canonicalization as the receipt (WE-1/WE-2). JCS (RFC 8785) MUST NOT
1076
+ * be used for this object; JCS is the Proposal's rule and mixing them is the
1077
+ * encoding split at the point it does the most damage. JSON here is the
1078
+ * diagnostic form the schema is written against, never an accepted input. An
1079
+ * object whose RECEIVED encoding is not the canonical one MUST be rejected
1080
+ * and MUST NOT be re-serialized and accepted.
1081
+ *
1082
+ * WHAT IS NOT A FIELD. `attestation_id` is DERIVED, not carried: it is
1083
+ * SHA-256 over the canonical CBOR encoding of this object (AT-1, AT-8a) and
1084
+ * the Executor recomputes it (§9.3 step 7b(v)). Transmitting it for trust is
1085
+ * what v1.3.2 did, and under one compromised KMS a receipt bearing a genuine
1086
+ * quorum raised for proposal P1 verified against an attacker-chosen P2 — Y1.
1087
+ * A schema that gave `attestation_id` a property would invite exactly that
1088
+ * read, so it has none.
1089
+ *
1090
+ * SUITE 12 CLASSIFICATION. Every field below is class T at the schema layer
1091
+ * — this document constrains SHAPE, and shape is not authority. The
1092
+ * Executor's obligations are elsewhere and are not restated here
1093
+ * (DP-3/DP-84): `proposal_hash` is bound (9.3-3), floor-only risk is
1094
+ * RECOMPUTED and never read from here (TR-8), and `required_count` is
1095
+ * subject to AT-9's two disjoint rules described on that property.
1096
+ * Generated from attestation_object.schema.json.
1097
+ */
1098
+ export interface AttestationObject {
1099
+ /**
1100
+ * The suite this attestation was raised under. CR-1: every signed structure
1101
+ * carries an explicit `alg` naming a suite registered in the signed bundle,
1102
+ * and an unregistered or unknown name fails closed rather than being
1103
+ * resolved to a known one.
1104
+ *
1105
+ * IT IS AN AT-1 FIELD, WHICH IS WHY IT IS HERE AND NOT BESIDE THE SIGNATURE.
1106
+ * CR-5 makes `alg` signature-covered, so it sits inside the canonical CBOR
1107
+ * whose SHA-256 is `attestation_id` (AT-8a). An issuer who could relabel the
1108
+ * suite without changing the object would hand a verifier a hybrid entry to
1109
+ * check as a classical one, and CR-3's conjunctive rule would be decorative
1110
+ * at the point it is applied. PB-8 records the same reasoning for the bundle
1111
+ * signature file and anchor.schema.json restates it for the anchor; this is
1112
+ * the third artifact it governs and the one where the field set is closed by
1113
+ * AT-8b, so a verifier reading `alg` from anywhere else would be reading a
1114
+ * field the object may not carry.
1115
+ *
1116
+ * WHAT THE VERIFIER DOES WITH IT IS NOT ONE RULE BUT TWO, AND THE SCHEMA
1117
+ * CANNOT PICK. For a `hybrid` registry entry the name is a machine suite and
1118
+ * CR-4 compares it against the bundle's registered floor by CONTAINMENT of
1119
+ * primitives, never by rank. For a `webauthn` entry it is one of HM-4's two
1120
+ * key types, CR-8 puts a human leg outside that floor entirely, and HM-3
1121
+ * requires it to EQUAL the registry entry's `alg`, refusing a mismatch
1122
+ * before a byte of the assertion is examined. Which rule applies is decided
1123
+ * by the registry, which this object does not name, so the shape layer
1124
+ * admits both vocabularies and narrows nothing. See `attestation_alg` below
1125
+ * for why that is a union of two declarations and not a third list.
1126
+ *
1127
+ * ADDED IN v1.3.31 (ACP-299), AS A CORRECTION AND NOT A FEATURE. This schema
1128
+ * had no `alg` property through v1.3.30 while CR-5 called it an AT-1 field,
1129
+ * `reference/src/acp_executor.py` listed it in `AT1_FIELDS` and
1130
+ * `crates/acp-decision/src/quorum.rs` listed it in `AT1_FIELDS` and
1131
+ * re-parsed it to pick the suite. The field set is CLOSED in both directions
1132
+ * (AT-8b), so an object built from this file was refused by both verifiers
1133
+ * for missing a field, and an object either verifier accepted carried a
1134
+ * field this file called additional. The cross-language differential could
1135
+ * not see it, because the two implementations agreed with each other and
1136
+ * disagreed with the document -- the eighth lesson in
1137
+ * `dossier/05-TEST-EVIDENCE.md`, and exactly the shape ACP-167 had one field
1138
+ * over. `tools/check-attestation-object.py` is the executable consumer that
1139
+ * would have caught it and now does.
1140
+ */
1141
+ readonly alg: AttestationAlg;
1142
+ /**
1143
+ * The canonical Proposal this attester signed. The binding the whole object
1144
+ * exists to carry: the Executor verifies the executed proposal hashes to
1145
+ * this value (9.3-3) rather than trusting that it does.
1146
+ */
1147
+ readonly proposalHash: Sha256Hash;
1148
+ /**
1149
+ * The signed bundle in force when the attestation was raised.
1150
+ */
1151
+ readonly policyBundleHash: Sha256Hash;
1152
+ /**
1153
+ * Epoch of that bundle. Monotonicity is enforced at the KMS against a
1154
+ * durable high-water mark (RAD-3), never from this value.
1155
+ *
1156
+ * Schema constraints, carried as documentation and NOT enforced by this
1157
+ * type: minimum 0.
1158
+ */
1159
+ readonly bundleEpoch: number;
1160
+ /**
1161
+ * The Context read the Decision was computed over (§8.8 `snapshot_hash()`).
1162
+ */
1163
+ readonly contextSnapshotHash: Sha256Hash;
1164
+ /**
1165
+ * FLOOR-ONLY risk, not effective risk. AT-3 requires the attester to have
1166
+ * been shown floor tiers and the bundle `rule_id`s that raised the risk, NOT
1167
+ * Context-asserted values — that separation is what closes A-8's display
1168
+ * path. The Executor recomputes this from the signed bundle and the
1169
+ * canonical Proposal (TR-8); a disagreement with the value here is a
1170
+ * critical alert, because it means the signing substrate emitted a Decision
1171
+ * the bundle does not support.
1172
+ */
1173
+ readonly floorOnlyRisk: Risk;
1174
+ /**
1175
+ * Attester roles the rule demanded. `uniqueItems` because a repeated role is
1176
+ * a second spelling of one requirement, and two spellings of one thing is
1177
+ * the defect this repository is built around.
1178
+ *
1179
+ * THE ARRAY MAY BE EMPTY, AND EMPTY HAS A MEANING: the rule demanded a
1180
+ * quorum and named no role, so any enrolled attester of the right kind
1181
+ * satisfies it. AT-8b keeps the FIELD required — an absent `required_roles`
1182
+ * is still a refusal, because an absent field and an empty array would be
1183
+ * two encodings of one object (Z4) — but nothing sets a lower bound on its
1184
+ * length.
1185
+ *
1186
+ * `minItems: 1` STOOD HERE THROUGH v1.3.31 AND WAS REMOVED IN v1.3.32
1187
+ * (ACP-308). It was a schema-level choice with no clause behind it — the
1188
+ * shape `operator_id` already records about its own length bound — and it
1189
+ * made the common case invalid: `tools/check-attestation-object.py` found on
1190
+ * its first run that the simulation builds a floor-HIGH object with
1191
+ * `required_roles: []` for every action whose rule names no role, which is
1192
+ * four of its five floor-HIGH actions. Neither verifier reads this field for
1193
+ * the quorum DECISION: the threshold is `quorum_k` from the signed bundle
1194
+ * (AT-9, PB-6, class R) and role membership is resolved against the
1195
+ * registry, so an empty list withholds nothing a verifier needed. Stating
1196
+ * the bound in AT-1 instead was the alternative, and it was rejected because
1197
+ * it would force every floor-HIGH rule an operator writes to invent a role
1198
+ * name for a requirement it does not have — a policy-authoring obligation
1199
+ * this document never asked for, imposed by a validator rather than by a
1200
+ * clause.
1201
+ *
1202
+ * Schema constraints, carried as documentation and NOT enforced by this
1203
+ * type: uniqueItems; items minLength 1, maxLength 128.
1204
+ */
1205
+ readonly requiredRoles: readonly string[];
1206
+ /**
1207
+ * The threshold the attester was shown and signed.
1208
+ *
1209
+ * AT-9 places TWO requirements on this field and NEITHER SUBSTITUTES FOR THE
1210
+ * OTHER. (1) It MUST NOT be an input to the quorum decision, in whole or in
1211
+ * part, INCLUDING where every entry agrees on it: the threshold is taken
1212
+ * from the signed bundle's `quorum_k` (PB-6) and is class R — recomputed.
1213
+ * `required_count` is signed by the attester, and the attester is exactly
1214
+ * the party under verification, so reading the threshold from it asks the
1215
+ * adversary how large a quorum to demand. (2) It MUST nonetheless equal
1216
+ * `quorum_k`, and a mismatch MUST fail closed with a critical alert —
1217
+ * because it is part of what the attester was SHOWN (AT-3), so a mismatch
1218
+ * means the humans approved under a policy the engine did not apply.
1219
+ *
1220
+ * An implementation that keeps only (1) satisfies INV-1-HIGH while silently
1221
+ * executing actions no attester agreed to. This clause exists because its
1222
+ * absence was exploited — see the v1.3.15 alert in §1(a).
1223
+ *
1224
+ * Schema constraints, carried as documentation and NOT enforced by this
1225
+ * type: minimum 1.
1226
+ */
1227
+ readonly requiredCount: number;
1228
+ /**
1229
+ * The operator the action is attributed to. Carried HERE, inside the
1230
+ * signature, so approver-distinctness (AT-2) and the §9.3 step-9 capability
1231
+ * recheck key on a signature-covered value rather than a receipt-body claim.
1232
+ * The receipt's top-level `operator` is diagnostic only. Mechanized as
1233
+ * `Y4_OperatorTamperDetected`.
1234
+ */
1235
+ readonly operator: OperatorId;
1236
+ /**
1237
+ * A fresh 128-bit attestation nonce (AT-1), making the derived
1238
+ * `attestation_id` unique per attestation. Single use is enforced by ledger
1239
+ * consumption of that id (AT-5, CL-3), not by assertion — v1.2.0 stated
1240
+ * single-use as a property with no enforcing mechanism, which permitted
1241
+ * attestation amplification (T-14).
1242
+ */
1243
+ readonly attNonce: Nonce128;
1244
+ /**
1245
+ * RFC 3339 instant, at most 60 minutes after issuance (AT-1). Expired
1246
+ * attestations are void and partial quorum at expiry is void (AT-5).
1247
+ * Evaluated against NTS-disciplined wall time within the L-20 skew
1248
+ * allowance, never against a monotonic clock — leg F9.1 carries that
1249
+ * dependency and DP-52 states the distinction.
1250
+ */
1251
+ readonly expiresAt: Rfc3339Instant;
1252
+ }
1253
+ /**
1254
+ * The lenient projection of AttestationObject: every field optional, every enum
1255
+ * degraded to `string`. What a verifier parses. Never make a decision on
1256
+ * one — the fail-safe lookups live on the strict type only.
1257
+ */
1258
+ export interface RawAttestationObject {
1259
+ readonly alg?: string;
1260
+ readonly proposalHash?: string;
1261
+ readonly policyBundleHash?: string;
1262
+ readonly bundleEpoch?: number;
1263
+ readonly contextSnapshotHash?: string;
1264
+ readonly floorOnlyRisk?: string;
1265
+ readonly requiredRoles?: readonly string[];
1266
+ readonly requiredCount?: number;
1267
+ readonly operator?: string;
1268
+ readonly attNonce?: string;
1269
+ readonly expiresAt?: string;
1270
+ }
1271
+ /**
1272
+ * VOCABULARY PINNED BY AU-9 (v1.3.27). `event_class` is one of seven values
1273
+ * declared in `$defs/event_class`; every record carries `event_class`,
1274
+ * `tenant_id`, `seq` and `occurred_at`; and each class carries EXACTLY the
1275
+ * further fields §11.1's table names for it -- a `decision` record the
1276
+ * twenty members this schema had always required, an `ingress_refused`
1277
+ * record the four a door can know, an `execution` record the EX-2 `outcome`
1278
+ * this schema had no place for. Every field below `seq` is therefore
1279
+ * optional AT THE SCHEMA LAYER and required BY CLASS, and the consumer
1280
+ * refuses a record whose set is not its class's before hashing it (AU-9),
1281
+ * because JSON Schema cannot say `required when event_class is X` in a form
1282
+ * this repository's generator has a rule for, and a consumer that hashed
1283
+ * first and checked second would have chained a record it then refused.
1284
+ * Until v1.3.27 this file was §11.1's decision record only: a door could not
1285
+ * build one, nothing recorded what RAN, and the timestamp's key name and the
1286
+ * class's value domain were both unpinned inside the AU-1 preimage -- so two
1287
+ * conformant implementations wrote different chain hashes for identical
1288
+ * histories (ACP-153, items 16-18; found by the first audit service).
1289
+ *
1290
+ * The record of one pipeline event. Carried on leg F6.1 of the leg register
1291
+ * (ZIFFER-DEPLOY-001 Annex A), Executor to Audit subsystem, cited there
1292
+ * against AU-2. It is `record_n` -- the object AU-1 places inside the chain
1293
+ * preimage -- and nothing else.
1294
+ *
1295
+ * WHY THIS IS A HASHED ARTIFACT AND NOT A LOG LINE. AU-1 (REVISED in
1296
+ * v1.3.15): `chain_hash_n = H({"prev": chain_hash_{n-1}, "record":
1297
+ * record_n})`, where §11.2 defines `H(x)` as the ASCII string `"sha256:"`
1298
+ * followed by the lowercase-hex SHA-256 of `canonical(x)`, and `canonical()`
1299
+ * is the single canonicaliser of AT-8a -- canonical CBOR, RFC 8949 §4.2. The
1300
+ * record therefore sits INSIDE a hash preimage, so its field set and its key
1301
+ * names are load-bearing in exactly the way AT-8b's are one artifact over.
1302
+ * AU-1 says as much about its own two keys: *the key names are part of the
1303
+ * preimage*. Two encodings of one event are two chain hashes, and from that
1304
+ * link onward two honest parties hold two different chains.
1305
+ *
1306
+ * WHAT THAT COSTS WHEN IT GOES WRONG, WHICH IS NOT A LOST COMPARISON. AU-3a
1307
+ * requires every anchor to be independently verifiable by any party holding
1308
+ * the anchor public keys, and AU-4 makes a head that fails to extend a
1309
+ * previously anchored head a CRITICAL INTEGRITY ALERT. So an encoding
1310
+ * ambiguity in this object routes an honest third-party verifier into this
1311
+ * system's own critical-alert channel with no attacker present. That is
1312
+ * ACP-57 -- the correction recorded in §1 and at AU-1, where the clause
1313
+ * specified a concatenation and `reference/src/acp_audit.py` had always
1314
+ * hashed a two-key map, so AU-3a did not hold as written. ACP-57 was a
1315
+ * defect in the clause immediately ABOVE this object. This schema is the
1316
+ * field set the same defect would live in, one layer down.
1317
+ *
1318
+ * WHY THE FIELD SET IS CLOSED, AND WHERE THAT CLOSURE EXCEEDS WHAT §11.1
1319
+ * STATES. Every property below is REQUIRED and `additionalProperties` is
1320
+ * false, because an optional field admits two canonical encodings of one
1321
+ * event -- present-as-null and omitted -- which is Z4 read at the chain
1322
+ * layer: not two ledger slots this time, but two chains, and the divergence
1323
+ * surfaces as AU-4's critical alert against an honest party. AT-8b closes
1324
+ * exactly this for the Attestation Object and L-26 records it as a limit; no
1325
+ * clause closes it here.
1326
+ *
1327
+ * AND THE SPECIFICATION DOES NOT SUPPORT THE CLOSED READING. §11.1 requires
1328
+ * *all relevant hashes*, and the word RELEVANT is the whole problem: it
1329
+ * makes the field set vary by event class, the document enumerates neither
1330
+ * the event classes nor which hashes are relevant to each, and it defines no
1331
+ * representation for an absent value. A conditional field set is Z4; a
1332
+ * closed one demands values a producer cannot always have (`raw_input_hash`
1333
+ * at the Executor is the clean case -- see that property). This schema takes
1334
+ * the closed reading because it is the only encoding-safe one, and records
1335
+ * the gap rather than resolving it by invention. Until §11.1 pins either a
1336
+ * per-event-class field set or an absent encoding, no schema for this object
1337
+ * can be both closed and conformant.
1338
+ *
1339
+ * §11.1 IS ALSO NOT THE WHOLE RECORD ON ITS OWN TERMS. SR-5 adds the
1340
+ * `(schema_id, schema_version, schema_hash)` triple and the fidelity class
1341
+ * to every audit record; B-2 requires every model-call record to assert that
1342
+ * provider-side tool features were disabled; EX-3 requires a
1343
+ * verification-failure record to carry the offending receipt; DR-11/L-29
1344
+ * require the confirmation-sampling draw to be recorded in audit; DR-12/L-30
1345
+ * require the unacknowledged-release counter to be. Five clauses add record
1346
+ * content that §11.1 does not list. The SR-5 triple is below because it is
1347
+ * stated of every audit record; the other four are event-class-specific and
1348
+ * are named here rather than guessed at.
1349
+ *
1350
+ * WHAT IS NOT A FIELD. `chain_hash` and `previous_hash` are AU-2's STORAGE
1351
+ * constraints on the row the receiver writes, not content of the record the
1352
+ * sender sends: the receiver computes them per AU-1 after this object
1353
+ * arrives (F6.1 carries the record, F6.2 carries the `chain_hash`). A record
1354
+ * carrying its own chain hash would be self-referential, and a record
1355
+ * carrying its own `previous_hash` would let the audited party name its own
1356
+ * link -- the audited party supplying the evidence of its own position,
1357
+ * RES-8 with the writer as the source. AU-8's tenant-creation record is
1358
+ * likewise not this object: it is a four-key map `{tenant_id, created_at,
1359
+ * bundle_epoch, schema_version}`, anchored immediately on tenant creation,
1360
+ * and modelling it here would put two objects in one schema. The anchor
1361
+ * (AU-3) is a third artifact on legs F6.3/F6.4. `audit_id` is absent because
1362
+ * §11.1 does not list it, even though §9.2's receipt carries one and R-1
1363
+ * returns one to a rejected caller.
1364
+ *
1365
+ * WHAT THIS SCHEMA DOES NOT CONSTRAIN, STATED SO THE CLAIM IS NOT READ WIDER
1366
+ * THAN IT IS. Every ordering property is invisible here. AU-7
1367
+ * anchor-before-release, AU-2's UNIQUE and GAPLESS `(tenant_id, seq)` and
1368
+ * its SERIALIZABLE or single-writer-per-tenant discipline, AU-6's outage
1369
+ * behaviour, AC-5's count-at-release, and all eight §11.3 reconciliation
1370
+ * checks are properties of a SEQUENCE of records and of the writer's
1371
+ * discipline. No instance check can see one of them: a schema-valid record
1372
+ * says nothing about whether it was anchored before its action was released.
1373
+ * They are per-implementation obligations (`spec/vectors/OBLIGATIONS.md`)
1374
+ * exercised in `reference/suites/audit_suite.py`, and a conformance claim
1375
+ * resting on this schema is a claim about shape only.
1376
+ *
1377
+ * SUITE 12 CLASSIFICATION. Every field below is class T at the schema layer,
1378
+ * and no consumer may promote any of them. An audit record is testimony
1379
+ * produced INSIDE the production trust domain -- the domain §11.3 places
1380
+ * under suspicion when it puts reconciliation in the anchor-verifier's
1381
+ * domain (AU-4) -- so a control input read from here would be the RES-8
1382
+ * defect with the audited party as the source. §9.2's authority note already
1383
+ * says `risk_level_effective`, `risk_level_floor_only` and `fidelity` are
1384
+ * diagnostic and audit fields only, and TR-8 requires the Executor to
1385
+ * recompute floor-only risk from the signed bundle and the canonical
1386
+ * Proposal. Reconciliation checks this record against ANCHORS and the
1387
+ * CONSUMPTION LEDGER, never against itself.
1388
+ *
1389
+ * ENCODING. Canonical CBOR, RFC 8949 §4.2, via §11.2's `H` and AT-8a's
1390
+ * single canonicaliser. The JSON here is the diagnostic form the schema is
1391
+ * written against, never an accepted input.
1392
+ *
1393
+ * NO `x-acp-absent` APPEARS BELOW, DELIBERATELY. No clause declares a
1394
+ * fail-safe default for a missing audit-record field. Inventing one would
1395
+ * let an unparseable record be silently completed and chained, which is the
1396
+ * opposite of what a tamper-evidence chain is for: P-4's default-DENY is
1397
+ * about policy evaluation and does not reach record parsing.
1398
+ * Generated from audit_record.schema.json.
1399
+ */
1400
+ export interface AuditRecord {
1401
+ /**
1402
+ * The class of pipeline event this record is of (§11.1, first item). It is
1403
+ * what makes §11.1's *all relevant hashes* even readable -- relevance is
1404
+ * relevance TO AN EVENT CLASS -- so it is the field the rest of the record's
1405
+ * conditionality would hang from.
1406
+ *
1407
+ * THE VALUE DOMAIN IS AU-9's (v1.3.27): `ingress_refused`, `decision`,
1408
+ * `verification_failed`, `hold`, `notice`, `release`, `execution` -- one per
1409
+ * pipeline event the flow register puts a record on (F1.1, F6.1, DR-1,
1410
+ * DR-13, DR-7/9/10/12, EX-2, EX-3). Through v1.3.26 no clause enumerated it,
1411
+ * and this repository held three vocabularies for one field: the reference's
1412
+ * `type` with `hold`/`release`, an AU-1 suite case's `event`, and §11.1's
1413
+ * words. The consequence was not cosmetic: AU-1 hashes this record, so
1414
+ * different strings for one event were different chain hashes for identical
1415
+ * histories, and AU-3a's independent verifier could recompute neither. The
1416
+ * reference now writes THIS key with THESE values, and AU-9 makes a value
1417
+ * outside the set a refusal rather than a fourth vocabulary.
1418
+ */
1419
+ readonly eventClass: AuditEventClass;
1420
+ /**
1421
+ * The tenant whose chain this record extends. §11.1 lists *tenant*; AU-1
1422
+ * makes chains PER-TENANT, AU-2 keys uniqueness and gaplessness on
1423
+ * `(tenant_id, seq)`, and AU-8 puts `tenant_id` in the genesis preimage, so
1424
+ * the identifier is the specification's own and not a name coined here.
1425
+ *
1426
+ * IT NAMES A CHAIN AND DOES NOT SELECT ONE (RES-8). A receiver that resolves
1427
+ * WHICH chain to append to from this field has let the writer choose its own
1428
+ * history; D-1 gives every tenant dedicated per-tenant executor credentials
1429
+ * and schemas precisely so the authenticated connection is what answers.
1430
+ * Carried anyway, because it is inside the AU-1 preimage: a record whose
1431
+ * tenant is implicit at write time and explicit at verification time is one
1432
+ * event with two encodings, and the chain would not survive that.
1433
+ */
1434
+ readonly tenantId: TenantId;
1435
+ /**
1436
+ * The session the event occurred in (§11.1, third item). The specification
1437
+ * uses the bare word `session` in exactly one other place that types it at
1438
+ * all -- §8.5's accumulator `group_by` lists `["operator","session"]` -- so
1439
+ * the key name here is that word and no other. It is load-bearing beyond
1440
+ * forensics: §4.2 scopes cross-session persistence OUT of the accumulator
1441
+ * model (*fresh session credentials reset session-scoped accumulators*),
1442
+ * which makes the session boundary an attacker-visible reset, and the audit
1443
+ * record is the only place a reconciler can see how an operator's activity
1444
+ * was partitioned across sessions.
1445
+ *
1446
+ * `maxLength` is L-04. No format is pinned because no clause pins one.
1447
+ *
1448
+ * Schema constraints, carried as documentation and NOT enforced by this
1449
+ * type: minLength 1, maxLength 2048.
1450
+ */
1451
+ readonly session?: string;
1452
+ /**
1453
+ * The operator the event is attributed to (§11.1, fourth item; §8.5 groups
1454
+ * accumulators by the same key). It is the principal AT-2
1455
+ * approver-distinctness and DR-9 acknowledgement are stated about, and the
1456
+ * principal AC-5's deny-effect accumulators can lock out -- which is why W1
1457
+ * mattered: counters inflated by Decisions that never executed are a
1458
+ * targeted lockout of a legitimate operator.
1459
+ *
1460
+ * THIS IS A RECORD OF AN ATTRIBUTION, NOT THE ATTRIBUTION ITSELF. The
1461
+ * authoritative operator identity for approver-distinctness and the §9.3
1462
+ * step-9 capability recheck is `attestations[].obj.operator`, which is
1463
+ * signature-covered (Y4); §9.2 says in as many words that the receipt's
1464
+ * top-level `operator` is diagnostic only. An audit record is one level
1465
+ * further from the signature than that receipt field, so nothing may key a
1466
+ * control decision on this value.
1467
+ */
1468
+ readonly operator?: OperatorId;
1469
+ /**
1470
+ * The fidelity class of the adapter the event descends from. Required twice
1471
+ * over: §11.1 lists it in the record, and SR-5 states that every Atom,
1472
+ * Proposal, Decision and audit record carries the `(schema_id,
1473
+ * schema_version, schema_hash)` triple AND the fidelity class.
1474
+ *
1475
+ * WHY IT IS AUDITED AT ALL. FC-1 makes the class the difference between
1476
+ * injection being grammatically impossible (F-HIGH) and containment resting
1477
+ * entirely on capability limits, confirmation and receipts (F-LOW), and FC-2
1478
+ * lets policy condition on it. A receipt asserting `fidelity: "F-HIGH"` for
1479
+ * a Proposal from an F-LOW-bound adapter MUST fail closed with a critical
1480
+ * alert (§14 suite 3), which is possible only because the true class is
1481
+ * recorded somewhere the adapter binding can be reconciled against. The key
1482
+ * name is §9.2's receipt field name `fidelity`; §11.1's phrase is *fidelity
1483
+ * class*.
1484
+ */
1485
+ readonly fidelity?: FidelityClass;
1486
+ /**
1487
+ * First member of SR-5's triple, which every audit record carries. The
1488
+ * triple is what lets a replayed Decision be re-derived against the schema
1489
+ * that actually admitted the input: §11.3(e) re-derives sampled Decisions by
1490
+ * replay and calls a mismatch critical, and SR-6 retires schemas after 180
1491
+ * days, so a record naming only a version would become unresolvable. Same
1492
+ * key name as §9.2's receipt field.
1493
+ *
1494
+ * Schema constraints, carried as documentation and NOT enforced by this
1495
+ * type: minLength 1, maxLength 2048.
1496
+ */
1497
+ readonly schemaId?: string;
1498
+ /**
1499
+ * Second member of SR-5's triple. Carried beside `schema_hash` rather than
1500
+ * instead of it because a version is a NAME for a schema and the hash is the
1501
+ * evidence of which bytes were used -- SR-3's narrowing rules move the
1502
+ * admissible set between versions, so a replay that trusted the version
1503
+ * alone would re-derive against a different grammar than the one that ran.
1504
+ * No format is pinned; no clause pins one.
1505
+ *
1506
+ * Schema constraints, carried as documentation and NOT enforced by this
1507
+ * type: minLength 1, maxLength 2048.
1508
+ */
1509
+ readonly schemaVersion?: string;
1510
+ /**
1511
+ * Third member of SR-5's triple, and the only member of it that is evidence
1512
+ * rather than a label. §8.4 step 1 admits a Proposal only against a
1513
+ * `(schema_id, version, hash)` valid in the bundle, so this is the value
1514
+ * that ties the recorded event to bundle-attested bytes.
1515
+ */
1516
+ readonly schemaHash?: Sha256Hash;
1517
+ /**
1518
+ * The hash of the external input the event descends from (§11.1, first of
1519
+ * the enumerated hashes).
1520
+ *
1521
+ * THIS FIELD IS WHERE §11.1 AND LEG F6.1 CONTRADICT EACH OTHER, AND THE
1522
+ * CONTRADICTION IS RECORDED RATHER THAN SMOOTHED. `raw_input_hash` occurs
1523
+ * exactly ONCE in ZIFFER-SPEC-001 -- in §11.1's own sentence -- and no
1524
+ * clause says what it covers, at which point of §5.1's flow it is taken, or
1525
+ * under which canonicalisation. For an F-LOW adapter the raw input is free
1526
+ * text, so *canonical* has no meaning for it at all. Worse for this leg: the
1527
+ * Executor never sees the raw input (§5.1 puts the Ingress Adapter and the
1528
+ * validators upstream of the Policy Engine, and D-2 gives the model path no
1529
+ * route to the Executor segment), and neither §9.2's receipt nor the
1530
+ * canonical Proposal carries the value -- so an Executor writing an F6.1
1531
+ * record can only forward a number it was handed, class T with no verifier
1532
+ * anywhere on the path, or omit it and split the encoding (Z4 at the chain
1533
+ * layer). The specification supports neither. Kept in the schema because
1534
+ * §11.1 states it unconditionally, and flagged because writing the field
1535
+ * down is what made the contradiction visible.
1536
+ */
1537
+ readonly rawInputHash?: Sha256Hash;
1538
+ /**
1539
+ * The canonical Proposal (B-1a) the event is about (§11.1). It is the join
1540
+ * key for most of §11.3: (a) matches execution records to receipts, and
1541
+ * DS-6f pins one immutable `proposal_hash -> origin_nonce` binding per
1542
+ * Proposal in the Consumption Ledger with INDEFINITE retention, which the
1543
+ * reconciler reads from the ledger and never from a record like this one.
1544
+ * Canonicalisation for the Proposal is JCS (RFC 8785) per WE-1/WE-2, not the
1545
+ * CBOR that encodes this record -- the two canonicalisers coexist by
1546
+ * artifact, and mixing them is the encoding split AT-8a exists to prevent.
1547
+ */
1548
+ readonly proposalHash?: Sha256Hash;
1549
+ /**
1550
+ * The signed bundle in force for the event (§11.1). Recorded so a Decision
1551
+ * can be re-derived (§11.3(e)) against the exact policy that produced it:
1552
+ * PB-1 expires bundles and §8.2 rules move, so a record naming only an epoch
1553
+ * would be replayable only while that bundle survived.
1554
+ */
1555
+ readonly policyBundleHash?: Sha256Hash;
1556
+ /**
1557
+ * The epoch of that bundle (§11.1). §11.3(d) reconciles every signing event
1558
+ * against a `bundle_epoch` greater than or equal to all prior epochs, which
1559
+ * is the rollback detection, and L-24 makes the epoch a strictly increasing
1560
+ * integer that is never reused.
1561
+ *
1562
+ * DETECTION HERE, ENFORCEMENT ELSEWHERE (RES-8). Monotonicity is enforced at
1563
+ * the KMS against a durable high-water mark (RAD-3) with indefinite
1564
+ * retention in the ledger (CL-4), never from an audited value: an epoch read
1565
+ * back from the chain the production domain writes would let a compromised
1566
+ * writer lower its own high-water mark. `minimum: 0` is the type's floor,
1567
+ * matching AU-8's genesis record, which carries an epoch before any signing
1568
+ * event exists.
1569
+ *
1570
+ * Schema constraints, carried as documentation and NOT enforced by this
1571
+ * type: minimum 0.
1572
+ */
1573
+ readonly bundleEpoch?: number;
1574
+ /**
1575
+ * The Context read the Decision was computed over (§11.1; §8.8
1576
+ * `snapshot_hash()`). Recorded because the Context Store is adversarial in
1577
+ * this threat model -- §8.3.2 leaves it able to raise tier or withhold
1578
+ * capability facts -- so re-deriving a sampled Decision (§11.3(e)) requires
1579
+ * knowing which snapshot was read, and INV-1-MEDIUM's disclosed residual is
1580
+ * only investigable against a pinned snapshot.
1581
+ */
1582
+ readonly contextSnapshotHash?: Sha256Hash;
1583
+ /**
1584
+ * The nonce of the Decision Receipt the event is about (§11.1, *receipt
1585
+ * nonce*; the receipt's own field name in §9.2 is `nonce`, and §11.1 does
1586
+ * not say which spelling belongs in the record).
1587
+ *
1588
+ * WHY IT IS IN THE RECORD. It is the identity a receipt is consumed under:
1589
+ * CL-2/CL-3 consume receipt nonces once against the linearizable ledger,
1590
+ * §9.3 step 6 claims it atomically, and DS-6a makes the FIRST receipt's
1591
+ * nonce the `origin_nonce` half of `action_id = (proposal_hash,
1592
+ * origin_nonce)`. §11.3(a) reconciles every execution record against exactly
1593
+ * one valid receipt, and orphan executions are critical -- that check needs
1594
+ * this value in the record.
1595
+ *
1596
+ * AND IT IS THE FIELD THAT MAKES THE CLOSED READING UNSATISFIABLE. A
1597
+ * pattern-constrained string has no empty encoding, so an event class with
1598
+ * no receipt -- an ingress rejection under R-3, a model call under B-2 --
1599
+ * cannot produce a schema-valid record without one, and no clause supplies
1600
+ * an absent form. `attestation_ids` escapes the same problem only because an
1601
+ * empty ARRAY is a single well-defined canonical encoding while an absent
1602
+ * field is two readings. Size is L-17's 128 bits; type is WE-4.
1603
+ */
1604
+ readonly receiptNonce?: Nonce128;
1605
+ /**
1606
+ * The `attestation_id`s of the Attestation Objects the event consumed
1607
+ * (§11.1, *attestation IDs*). Each is SHA-256 over the canonical CBOR
1608
+ * encoding of the whole object (AT-1, AT-8a), RECOMPUTED by the Executor at
1609
+ * §9.3 step 7b(v) and never taken as transmitted -- transmitting the id for
1610
+ * trust is what v1.3.2 did and what Y1 broke. So the values here are a
1611
+ * record of a recomputation, which is the only kind of derived value an
1612
+ * audit record may honestly carry.
1613
+ *
1614
+ * WHY IT MATTERS THAT THEY ARE RECORDED. §11.3(c) reconciles every
1615
+ * `attestation_id` against exactly one receipt and at most one execution --
1616
+ * the check whose absence permitted T-14, attestation amplification. A
1617
+ * reconciler running outside the production domain (AU-4) can only perform
1618
+ * it against records that name the ids.
1619
+ *
1620
+ * THESE ARE NOT `attestation_digests`. Under receipt version 3 (§8.6b, AB-1)
1621
+ * the receipt commits to entry digests taken over the whole entry -- object,
1622
+ * `kind`, `attester` and `sig` together -- while an `attestation_id` is over
1623
+ * the object alone. Two different values over overlapping bytes; recording
1624
+ * the wrong one makes §11.3(c) silently compare nothing.
1625
+ *
1626
+ * AN EMPTY ARRAY IS THE CORRECT ENCODING FOR AN EVENT THAT CONSUMED NO
1627
+ * ATTESTATION, and it is deliberately not modelled as an absent field: `[]`
1628
+ * has one canonical CBOR encoding, an omitted key has two readings, and two
1629
+ * readings is the chain divergence this schema exists to prevent.
1630
+ * `uniqueItems` because one attestation appearing twice in one record is one
1631
+ * approval counted twice, which is T-14 in miniature.
1632
+ *
1633
+ * Schema constraints, carried as documentation and NOT enforced by this
1634
+ * type: minItems 0, uniqueItems.
1635
+ */
1636
+ readonly attestationIds?: readonly Sha256Hash[];
1637
+ /**
1638
+ * The floor-only risk the Policy Engine computed (§11.1, *floor-only and
1639
+ * effective risk*; the key name is §9.2's receipt field). Floor-only means
1640
+ * derived from the SIGNED BUNDLE's tier floors and rules, with no
1641
+ * Context-asserted raise folded in -- §8.3.2's separation, which is what
1642
+ * keeps a compromised Context Store from suppressing escalation (TR-4,
1643
+ * `FloorDominance`).
1644
+ *
1645
+ * DIAGNOSTIC AND AUDIT ONLY, IN THE SPECIFICATION'S OWN WORDS. §9.2's
1646
+ * authority note classifies this field non-authoritative in the receipt, and
1647
+ * TR-8 requires the Executor to RECOMPUTE it from the signed bundle and the
1648
+ * canonical Proposal; a transmitted value disagreeing with the recomputation
1649
+ * is a critical alert at §9.3 step 7a. That history is why the note exists:
1650
+ * v1.3.0 keyed the Executor's quorum requirement on the receipt's value, so
1651
+ * one compromised KMS could sign a floor-T3 action as `LOW` with no
1652
+ * attestations and defeat INV-1-HIGH. A value one hop further from the
1653
+ * signature must not be treated any better.
1654
+ */
1655
+ readonly riskLevelFloorOnly?: Risk;
1656
+ /**
1657
+ * The effective risk -- floor composed with Context-asserted raises and
1658
+ * accumulator effects (§8.4, §8.5) -- recorded beside the floor-only value
1659
+ * because §11.1 requires BOTH. Recording only one loses the ability to
1660
+ * reconstruct why an action attested: the pair is the evidence of which side
1661
+ * raised it, and a raise that appears in the effective value and not the
1662
+ * floor is a Context Store claim rather than a bundle rule. Diagnostic and
1663
+ * audit only, per §9.2's authority note.
1664
+ */
1665
+ readonly riskLevelEffective?: Risk;
1666
+ /**
1667
+ * The disposition the Policy Engine reached (§8.4 step 11; §5.1's flow
1668
+ * renders the engine's output as `ALLOW/ATTEST/DENY`; §9.2's receipt field
1669
+ * carries the same name).
1670
+ *
1671
+ * THIS IS THE ONLY MEMBER OF §11.1'S *Decision fields* THIS SCHEMA PINS, AND
1672
+ * THE REST IS AN OPEN QUESTION RATHER THAN AN OMISSION. §11.1 says *Decision
1673
+ * fields* and enumerates none. §9.2's receipt body carries a dozen
1674
+ * candidates -- `rule_id`, `receipt_version`, `issued_at`, `expires_at`,
1675
+ * `idempotency_key` among them -- and guessing which of them §11.1 meant
1676
+ * would be this schema legislating where the specification is silent, which
1677
+ * is how a second definition of one object gets created. Only the
1678
+ * disposition itself is taken, because it is the one field both §8.4 and
1679
+ * §9.2 name as THE Decision.
1680
+ *
1681
+ * `DENY` is included on P-4's authority as much as step 11's: no matching
1682
+ * rule, unknown capability, unclassified resource or unavailable Context
1683
+ * Store each resolve to DENY with a distinct reason code, and R-3 requires
1684
+ * every rejection to produce an audit record -- so a record that could not
1685
+ * express a refusal would leave the entire fail-closed path unauditable.
1686
+ */
1687
+ readonly decision?: DecisionOutcome;
1688
+ /**
1689
+ * The monotonic sequence number of this record within its tenant's chain
1690
+ * (§11.1, last item). AU-2 makes `(tenant_id, seq)` UNIQUE and GAPLESS, with
1691
+ * inserts appending under SERIALIZABLE or single-writer-per-tenant
1692
+ * discipline.
1693
+ *
1694
+ * WHY THE MINIMUM IS 1 AND NOT 0. AU-8 defines `chain_hash_0` over the
1695
+ * tenant-creation record, and AU-1 pairs `chain_hash_n` with `record_n`, so
1696
+ * index 0 is genesis and the first pipeline event is n = 1. The
1697
+ * tenant-creation record is a different object (see this schema's
1698
+ * description) and never appears here.
1699
+ *
1700
+ * IT NAMES A POSITION AND IS NOT EVIDENCE OF ONE (RES-8). Gaplessness and
1701
+ * uniqueness are properties the WRITER enforces under AU-2's own discipline;
1702
+ * a receiver that appends at the sequence number its sender chose has let
1703
+ * the sender place its own link, and a chain whose positions are supplied by
1704
+ * the party under audit is not tamper-evident. The value is carried because
1705
+ * §11.1 puts it in the record and AU-1 hashes the record -- but the receiver
1706
+ * assigns and verifies it.
1707
+ *
1708
+ * RESOLVED IN v1.3.28 (ACP-164): WRITER PROPOSES, RECEIVER VERIFIES. The
1709
+ * finding this paragraph used to record was that the reference refused a
1710
+ * writer-supplied `seq` and stamped its own, while this schema makes the
1711
+ * field REQUIRED -- so the generated `AuditRecord`, which types `seq` as a
1712
+ * required integer, could not express a message the reference would accept,
1713
+ * and no F6.1 vector could satisfy both. The resolution is the
1714
+ * compare-and-retry shape ziffer's audit service already shipped for three
1715
+ * concurrent writers per chain: the writer names the position it believes is
1716
+ * next, and `AuditChain.append` refuses a mismatch under AU-2 with the
1717
+ * EXPECTED position in the refusal, which is the whole retry protocol. The
1718
+ * receiver still decides -- it never appends AT the writer's number, it
1719
+ * refuses unless that number is the one it was about to assign -- so the
1720
+ * RES-8 paragraph above survives unchanged: naming the right position gains
1721
+ * a writer nothing, and naming any other gains it a refusal. The preimage is
1722
+ * the record as written, `seq` included.
1723
+ *
1724
+ * Schema constraints, carried as documentation and NOT enforced by this
1725
+ * type: minimum 1.
1726
+ */
1727
+ readonly seq: number;
1728
+ /**
1729
+ * RFC 3339 instant at which the event occurred, in the rendering §9.2 uses
1730
+ * for `issued_at` and `expires_at`.
1731
+ *
1732
+ * THE CLAUSE THAT REQUIRES IT IS NOT §11.1, AND THAT IS THE POINT. §11.1's
1733
+ * record content lists NO timestamp. §11.3(g) nonetheless reconciles every
1734
+ * floor-HIGH execution record against *an anchor covering it, dated at or
1735
+ * before release* -- a comparison the reconciler makes from outside the
1736
+ * production trust domain (AU-4), where the record and the anchor are the
1737
+ * only two things it can read. Without a time in the record the AU-7
1738
+ * enforcement check has nothing to compare the anchor's date against, and
1739
+ * AU-7 is what closed W3, the pre-anchor rewrite gap.
1740
+ * `reference/src/acp_audit.py` carries the value under the key `at` and
1741
+ * `reconcile()` compares `a["at"] > r["at"]` exactly as described.
1742
+ *
1743
+ * THE KEY NAME IS `occurred_at`, PINNED BY AU-9 (v1.3.27). The reference
1744
+ * carried this value under `at`, and the anchor's date under `at` beside it;
1745
+ * both are renamed to the names the wire schemas already used (`occurred_at`
1746
+ * here, `anchored_at` in anchor.schema.json), because an unpinned key name
1747
+ * for a required value inside the AU-1 preimage was a chain divergence
1748
+ * waiting to happen.
1749
+ *
1750
+ * EVALUATED AGAINST NTS-DISCIPLINED WALL TIME within the L-20 skew allowance
1751
+ * (§12.5, leg F9.1), never a monotonic clock: an anchor and a record are
1752
+ * dated by different processes, and only wall time is comparable across
1753
+ * them.
1754
+ */
1755
+ readonly occurredAt: Rfc3339Instant;
1756
+ /**
1757
+ * What happened, on the records AU-9 puts it on: an `execution` record
1758
+ * carries EX-2's `executed`, `failed`, `not_attempted` or `indeterminate`
1759
+ * (§9.5 -- `indeterminate` is what DS-3's reconciliation is keyed on, and it
1760
+ * is a value here because a chain that cannot say an outcome was unknown
1761
+ * cannot answer §11.3 (f)); a `release` record carries `executed` or
1762
+ * `refused` (DR-7, DR-9, DR-10). Absent on every other class. Through
1763
+ * v1.3.26 nothing in the record said what ran, so §11.3 (a) orphan
1764
+ * executions, (f) indeterminate outcomes and (h) released executions could
1765
+ * not be answered from a chain written in this vocabulary (ACP-153, item
1766
+ * 17).
1767
+ */
1768
+ readonly outcome?: AuditOutcome;
1769
+ /**
1770
+ * On a `notice` record: who the signed bundle's `notice_targets.json` names
1771
+ * for the action class (DR-13), as committed BEFORE execution. At least one,
1772
+ * because DR-13 refuses to execute where the bundle names none -- a notice
1773
+ * with no addressee is not a detection channel -- and a record that could
1774
+ * carry an empty set would be that refusal written down as success.
1775
+ *
1776
+ * Schema constraints, carried as documentation and NOT enforced by this
1777
+ * type: minItems 1, maxItems 100.
1778
+ */
1779
+ readonly recipients?: readonly OperatorId[];
1780
+ /**
1781
+ * On a `release` record: the notified parties whose acknowledgements the
1782
+ * Executor verified (ACK-1..6) before releasing. EMPTY for a silent release,
1783
+ * and DR-12's per-tenant counter is the count of `release` records whose set
1784
+ * is empty -- recorded in audit, as the clause requires, by being derivable
1785
+ * from it rather than by a counter field a writer could set.
1786
+ *
1787
+ * Schema constraints, carried as documentation and NOT enforced by this
1788
+ * type: maxItems 100.
1789
+ */
1790
+ readonly acknowledgedBy?: readonly OperatorId[];
1791
+ /**
1792
+ * On an `ingress_refused` or `verification_failed` record: the clause id the
1793
+ * refusal fired under -- `B-6`, `V-1`, `9.3-7b(ii)` -- in the spelling
1794
+ * `tools/clauses.py` indexes. It is the join key between a chain and the
1795
+ * specification: an auditor reading `verification_failed` records grouped by
1796
+ * clause is reading the threat table's countermeasures firing, which is what
1797
+ * EX-3 puts the record there for.
1798
+ *
1799
+ * Schema constraints, carried as documentation and NOT enforced by this
1800
+ * type: pattern `^[A-Za-z0-9.]{1,12}-[0-9]{1,3}[a-z]?(\([a-z-]{1,8}\))?$`,
1801
+ * minLength 1, maxLength 32.
1802
+ */
1803
+ readonly clause?: string;
1804
+ /**
1805
+ * On an `execution` record: the target system's correlation identifier for
1806
+ * the invocation (EX-2's `target correlation IDs`), so an `indeterminate`
1807
+ * outcome can be resolved against the target's own record of what it did
1808
+ * (DS-3). Opaque here; its meaning is the target's.
1809
+ *
1810
+ * Schema constraints, carried as documentation and NOT enforced by this
1811
+ * type: minLength 1, maxLength 256.
1812
+ */
1813
+ readonly correlationId?: string;
1814
+ }
1815
+ /**
1816
+ * The lenient projection of AuditRecord: every field optional, every enum
1817
+ * degraded to `string`. What a verifier parses. Never make a decision on
1818
+ * one — the fail-safe lookups live on the strict type only.
1819
+ */
1820
+ export interface RawAuditRecord {
1821
+ readonly eventClass?: string;
1822
+ readonly tenantId?: string;
1823
+ readonly session?: string;
1824
+ readonly operator?: string;
1825
+ readonly fidelity?: string;
1826
+ readonly schemaId?: string;
1827
+ readonly schemaVersion?: string;
1828
+ readonly schemaHash?: string;
1829
+ readonly rawInputHash?: string;
1830
+ readonly proposalHash?: string;
1831
+ readonly policyBundleHash?: string;
1832
+ readonly bundleEpoch?: number;
1833
+ readonly contextSnapshotHash?: string;
1834
+ readonly receiptNonce?: string;
1835
+ readonly attestationIds?: readonly string[];
1836
+ readonly riskLevelFloorOnly?: string;
1837
+ readonly riskLevelEffective?: string;
1838
+ readonly decision?: string;
1839
+ readonly seq?: number;
1840
+ readonly occurredAt?: string;
1841
+ readonly outcome?: string;
1842
+ readonly recipients?: readonly string[];
1843
+ readonly acknowledgedBy?: readonly string[];
1844
+ readonly clause?: string;
1845
+ readonly correlationId?: string;
1846
+ }
1847
+ /**
1848
+ * The bundle-epoch high-water mark consulted by RAD-3 (CL-3, T-15).
1849
+ * Retention is INDEFINITE (CL-4): an expiring epoch mark reopens rollback,
1850
+ * because a genuine but superseded and more permissive bundle becomes
1851
+ * acceptable again the moment the mark that refused it is forgotten.
1852
+ *
1853
+ * THIS CLASS DOES NOT BEHAVE LIKE THE OTHER THREE, AND THE SPECIFICATION
1854
+ * CALLS IT A CONSUMPTION ANYWAY. CL-3 lists bundle epochs under "Consumed
1855
+ * identifier classes", but an epoch high-water mark is not consumed: many
1856
+ * receipts legitimately reference the same epoch, and a ledger that treated
1857
+ * this as at-most-once would refuse the second receipt issued under one
1858
+ * bundle. CL-4 says as much by giving the mark indefinite retention while
1859
+ * nonces and attestation ids expire. CL-3's own text does that disclosure
1860
+ * for the fourth class -- "Unlike the three classes above this is a
1861
+ * *binding*, not a consumption" -- and does not do it for this one. The
1862
+ * divergence is recorded with this schema rather than resolved here, because
1863
+ * deciding it is a normative act.
1864
+ * Generated from ledger_claim.schema.json#/$defs/bundle_epoch_claim.
1865
+ */
1866
+ export interface BundleEpochClaim {
1867
+ readonly tenantId: TenantId;
1868
+ readonly artifactClass: 'bundle_epoch';
1869
+ /**
1870
+ * The `bundle_epoch` of the receipt under verification (§9.2). An integer,
1871
+ * not a digest, which is why the four classes cannot share one object:
1872
+ * `identifier` is a string in the other three branches and there is no
1873
+ * encoding of this value that is simultaneously both. Monotonicity is a
1874
+ * LEDGER property compared against the durable mark, never a property of
1875
+ * this value -- a claim asserting it is the highest epoch ever seen would be
1876
+ * the claimant setting its own bar, which is what RAD-3 replaced the v1.2.0
1877
+ * history window to prevent.
1878
+ *
1879
+ * Schema constraints, carried as documentation and NOT enforced by this
1880
+ * type: minimum 0.
1881
+ */
1882
+ readonly identifier: number;
1883
+ }
1884
+ /**
1885
+ * The lenient projection of BundleEpochClaim: every field optional, every enum
1886
+ * degraded to `string`. What a verifier parses. Never make a decision on
1887
+ * one — the fail-safe lookups live on the strict type only.
1888
+ */
1889
+ export interface RawBundleEpochClaim {
1890
+ readonly tenantId?: string;
1891
+ readonly artifactClass?: string;
1892
+ readonly identifier?: number;
1893
+ }
1894
+ /**
1895
+ * One asserted answer of `has_capability(operator, capability, resource)`
1896
+ * (section 8.8). All three arguments are carried because all three identify
1897
+ * the fact: an entry naming fewer of them answers a question nobody asked,
1898
+ * and at section 9.3 step 9 the question is specific -- capability of THIS
1899
+ * operator on THIS target. The reference implementation is the
1900
+ * counterexample and it is worth keeping in view: `acp_executor.py` keys
1901
+ * capabilities by the joined string `f"{task_type}:{target}"`, so a
1902
+ * capability name containing a colon aliases a different (capability,
1903
+ * resource) pair, and the three-argument interface section 8.8 declares has
1904
+ * no three-argument representation anywhere in the reference.
1905
+ * Generated from context_read.schema.json#/$defs/capability_grant.
1906
+ */
1907
+ export interface CapabilityGrant {
1908
+ /**
1909
+ * The principal the capability is asserted for. This is the SAME value the
1910
+ * Attestation Object carries as `operator` and the same one section 9.3 step
1911
+ * 7b(iii-a) hands to step 9, so the bound is copied from
1912
+ * `attestation_object.schema.json` rather than chosen: if the two disagreed,
1913
+ * an operator an attestation can name would be an operator this read cannot
1914
+ * answer for, and step 9 could not run. Neither bound is stated by the
1915
+ * specification -- see the note on `resource`.
1916
+ */
1917
+ readonly operator: OperatorId;
1918
+ /**
1919
+ * The capability asserted. Section 8.8 names this argument and nothing
1920
+ * anywhere declares its identifier space, its syntax or its relation to a
1921
+ * rule's required capabilities at evaluation-order step 4; the bound is the
1922
+ * neighbouring one rather than a fourth invention.
1923
+ *
1924
+ * Schema constraints, carried as documentation and NOT enforced by this
1925
+ * type: minLength 1, maxLength 256.
1926
+ */
1927
+ readonly capability: string;
1928
+ /**
1929
+ * The resource the capability is asserted over -- the same identifier space
1930
+ * `floors.json` and `tier_raises` are keyed by, so the bound is copied from
1931
+ * `floors.schema.json`. THE SPECIFICATION BOUNDS NO IDENTIFIER LENGTH
1932
+ * ANYWHERE except L-04's 2,048-character maximum string field, whose
1933
+ * enforcement point is 'Schema' and whose scope (a Proposal's fields, or
1934
+ * every string on every wire) it does not state. Three schemas in this
1935
+ * repository already pin three different bounds. Copying a neighbour's is
1936
+ * the least bad of the available moves and is not a reading of the document.
1937
+ *
1938
+ * Schema constraints, carried as documentation and NOT enforced by this
1939
+ * type: minLength 1, maxLength 256.
1940
+ */
1941
+ readonly resource: string;
1942
+ }
1943
+ /**
1944
+ * The lenient projection of CapabilityGrant: every field optional, every enum
1945
+ * degraded to `string`. What a verifier parses. Never make a decision on
1946
+ * one — the fail-safe lookups live on the strict type only.
1947
+ */
1948
+ export interface RawCapabilityGrant {
1949
+ readonly operator?: string;
1950
+ readonly capability?: string;
1951
+ readonly resource?: string;
1952
+ }
1953
+ /**
1954
+ * One snapshot-consistent read of the Context Store, carried on legs F3.1
1955
+ * (Context Store -> Policy Engine) and F3.5 (Context Store -> Executor) of
1956
+ * the flow-leg register (ZIFFER-DEPLOY-001 Annex A). Section 8.7 requires
1957
+ * that Context reads be snapshot-consistent, which is why ONE read is ONE
1958
+ * object carrying every fact read under ONE `snapshot_hash`, rather than a
1959
+ * separate response per function of the section 8.8 interface. Split into
1960
+ * per-function responses, a Policy Engine can answer capability at
1961
+ * evaluation-order step 4 from one snapshot and tier at step 7 from another,
1962
+ * and the consistency section 8.7 requires holds of neither.
1963
+ *
1964
+ * THE RULE THIS OBJECT EXISTS TO CARRY, AND THE ONE A CONSUMER MUST NOT GET
1965
+ * WRONG: A CONTEXT READ RAISES, AND CAN NEVER LOWER. TR-1 puts a resource's
1966
+ * floor tier in the signed bundle (`floors.json`), read-only to the runtime
1967
+ * under PB-4. TR-2 lets the Context Store serve a RAISE and nothing else.
1968
+ * TR-3 composes them at the consumer: `effective_tier(r) = max(floor(r),
1969
+ * raise(r))`, absent raise = T0, and evaluation order step 7 states the same
1970
+ * composition. Because risk functions only escalate (RK-3), TR-4 is then a
1971
+ * theorem rather than a hope: a compromised Context Store can drive
1972
+ * effective tier UP, producing more attestation, which is safe, and can
1973
+ * never drive it below the signed floor. It is mechanized as
1974
+ * `FloorDominance` (Annex B, theorem 2, SMT-discharged over the 4x4 lattice
1975
+ * and killed by mutation M1), and section 4.3 classes the Context Store on
1976
+ * exactly that basis -- 'Trusted for RAISES only. Cannot lower effective
1977
+ * tier.'
1978
+ *
1979
+ * WHAT BREAKS IF A CONSUMER TREATS THIS OBJECT AS ABLE TO LOWER. The whole
1980
+ * of v1.2.0's most serious defect returns, and section 8.3.2 opens by naming
1981
+ * it: tier data in two mutually exclusive places, so RK-5's two-person
1982
+ * control over floor changes protects a copy that does not feed evaluation.
1983
+ * That is C2, and its attack is T-16, tier suppression -- falsify a
1984
+ * resource's tier downward and the escalation trigger never fires, so a
1985
+ * floor-T3 action grades LOW, takes step 11's ALLOW branch, and executes
1986
+ * with no quorum. INV-1-HIGH is stated over floor-only risk (TR-5) precisely
1987
+ * so that no single runtime component can forge it downward; a consumer that
1988
+ * took an effective tier, or a floor, from this object would move the value
1989
+ * back under the control of the component section 10's invariant table says
1990
+ * must not have it. Annex B's proof would still discharge, and it would be a
1991
+ * proof about a composition the implementation no longer performs. Note also
1992
+ * which direction is cheap: TR-9 makes lowering a floor an offline-key
1993
+ * deploy under RK-5 while raising stays fast through this object, and that
1994
+ * asymmetry is the design.
1995
+ *
1996
+ * WHAT IS NOT A FIELD, EACH ABSENT ON PURPOSE. (1) A FLOOR TIER, and (2) AN
1997
+ * EFFECTIVE TIER. The floor is TR-1's, comes from the signed bundle the
1998
+ * consumer already holds, and a second copy of it here is C2 restated at the
1999
+ * wire; the effective tier is TR-3's `max`, which the consumer computes, and
2000
+ * a transmitted one is a derived security value accepted from the party
2001
+ * under verification (TR-8's rule, RES-8's wording). (3) A `granted: false`
2002
+ * capability answer -- see `capability_grants`. (4) THE PROVIDER CLASS. CP-1
2003
+ * declares provider selection per tenant IN THE SIGNED BUNDLE, and CP-3
2004
+ * requires evaluation logic to be identical across providers; a provider
2005
+ * class carried here is both a value read from the party being verified and
2006
+ * a field a consumer could branch on, and CP-3 forbids the branch. (5) A
2007
+ * TENANT IDENTIFIER. DP-43 puts tenant separation for the Context Store at
2008
+ * the CREDENTIAL boundary -- a credential scoped to one tenant must not be
2009
+ * able to read another tenant's rows -- so tenancy is a property of the
2010
+ * connection rather than a claim in the payload. A claimed `tenant_id` would
2011
+ * be self-asserted scope from the store, which is the same shape again. The
2012
+ * residual is real and is disclosed rather than closed: nothing in the
2013
+ * payload lets a consumer detect a store that answered with another tenant's
2014
+ * facts, and DP-78 already records that this repository reads no credential
2015
+ * domain and can audit no such boundary. (6) A SIGNATURE. WE-1 makes
2016
+ * COSE_Sign1 the receipt's carrier and AT-8a pins the Attestation Object's
2017
+ * encoding; no clause states a signature over a Context read. Section 4.3
2018
+ * authenticates this leg at the transport (mTLS REQUIRED between components)
2019
+ * and accepts the store as trusted-for-raises-only, which is coherent only
2020
+ * because a compromised store fails safe in both available directions.
2021
+ * Section 8.7.1's 'Context Store signing key SHOULD be in an offline HSM' is
2022
+ * INFORMATIVE and names no bytes it signs, so a `signature` property here
2023
+ * would invent an envelope the specification does not have.
2024
+ *
2025
+ * WHY THE FIELD SET IS CLOSED, AND WHY THAT CLAIM IS WEAKER HERE THAN IT IS
2026
+ * FOR THE ATTESTATION OBJECT. `additionalProperties` is false and every
2027
+ * property is required. NO CLAUSE STATES THIS RULE. AT-8b closes the
2028
+ * Attestation Object's field set and calls the closure the security property
2029
+ * itself; there is no AT-8b for a Context read. Closure is adopted because
2030
+ * it is the fail-safe direction -- an unknown key is refused rather than
2031
+ * ignored, a missing key is refused rather than defaulted -- and because of
2032
+ * Z4: any optional field gives one read two valid encodings, and the receipt
2033
+ * carries `context_snapshot_hash` (AT-1, section 9.2) as a name for exactly
2034
+ * this object, so two encodings of one snapshot are two names for it. That
2035
+ * argument is CONDITIONAL on a preimage the specification does not define
2036
+ * (see `snapshot_hash`), which is why it is recorded as a gap rather than
2037
+ * presented as a reading. The consequence of an optional field here is
2038
+ * therefore not Z4's amplification but DIVERGENCE: one consumer refuses the
2039
+ * unknown key, another ignores it, and at a verifier a divergence is
2040
+ * indistinguishable from a compromise.
2041
+ *
2042
+ * ENCODING IS NOT PINNED BY ANY CLAUSE, AND THAT IS A HOLE RATHER THAN A
2043
+ * CHOICE. Section 9.2 pins JCS (RFC 8785) for the Proposal and canonical
2044
+ * CBOR (RFC 8949 section 4.2) for the receipt and the Attestation Object
2045
+ * (WE-1, WE-2, AT-8a). Nothing pins a canonicalization for the Context read,
2046
+ * and section 8.8 gives `snapshot_hash()` a return type and never a
2047
+ * preimage. So the one artifact in this register whose digest is carried
2048
+ * inside a signed receipt is the one artifact with no stated canonical form.
2049
+ * The JSON below is written as diagnostic form, in the same sense WE-3 gives
2050
+ * that phrase for receipts, and it is NOT an assertion that JSON is the wire
2051
+ * form; no clause says what the wire form is.
2052
+ *
2053
+ * SUITE 12 CLASSIFICATION. Every field below is class T at the schema layer,
2054
+ * because this document constrains SHAPE and shape is not authority. Two of
2055
+ * them are T at the SYSTEM layer as well, with residuals the specification
2056
+ * already discloses rather than residuals this file is introducing.
2057
+ * Capability facts: section 8.7's note and section 10's invariant table
2058
+ * state that a compromised Context Store can withhold a capability (DENY,
2059
+ * safe), raise a tier (more attestation, safe), or FALSIFY A GRANT AT
2060
+ * MEDIUM, which is INV-1-MEDIUM's disclosed residual -- INV-1-HIGH survives
2061
+ * because floor-HIGH already required a quorum and step 9 is a revocation
2062
+ * recheck closing the issuance-to-execution window (T-10), not the
2063
+ * authorization itself. `snapshot_hash`: TR-10's rule is that a transmitted
2064
+ * identifier is a NAME for a binding and not EVIDENCE of one, and this
2065
+ * identifier is not merely unverified but unverifiable, because no clause
2066
+ * says what it covers.
2067
+ *
2068
+ * WHAT THIS SCHEMA DOES NOT CONSTRAIN, listed so that shape validity is
2069
+ * never read as conformance. (a) SNAPSHOT CONSISTENCY (section 8.7) is a
2070
+ * property of how the facts were read, not of the object; a store that
2071
+ * assembles a `ContextRead` from three different snapshots and stamps one
2072
+ * hash on it is shape-valid here. (b) THE FRESHNESS BOUND. CP-2 resolves
2073
+ * stale freshness to DENY and L-18 caps revocation staleness at 5 minutes,
2074
+ * but the contract that applies is the one section 8.8's provider table
2075
+ * gives for the bundle-declared provider class (CP-1), so the bound is
2076
+ * evaluated by the consumer against the signed bundle and never parsed here
2077
+ * -- see `freshness_seconds`. (c) THAT THE READ IS LIVE. Step 9 requires the
2078
+ * Executor to RE-QUERY at execution time; a cached `ContextRead` replayed
2079
+ * inside its freshness window is indistinguishable from a fresh one at this
2080
+ * layer, and that is the whole of T-10. (d) THAT THE ANSWERS MATCH THE
2081
+ * QUESTIONS. The specification defines no query message, so nothing binds
2082
+ * this response to the read that was requested. (e) ORDERING, of
2083
+ * `capability_grants` or of anything else.
2084
+ *
2085
+ * NOTHING IN THIS REPOSITORY PRODUCES OR CONSUMES THIS OBJECT, AND SAYING SO
2086
+ * IS THE POINT. The Python reference models Context as an in-process dict of
2087
+ * operator -> set of `"task_type:target"` strings (`acp_executor.py`,
2088
+ * `Executor.context`), read once at step 9. It implements none of section
2089
+ * 8.8's four functions: there is no `snapshot_hash()`, no `freshness()`, and
2090
+ * `tier_raise` appears nowhere in `reference/` or `crates/` -- both
2091
+ * evaluators bind `resource.effective_tier` to the FLOOR (TR-5's floor-only
2092
+ * computation), so TR-3's `max` has no implementation anywhere and the raise
2093
+ * half of the floor/raise model is proof and prose only.
2094
+ * `context_snapshot_hash` appears in every fixture as the literal string
2095
+ * `"sha256:ctx"`, which does not even match the `sha256` type this file
2096
+ * references. ACP-52 already records that nothing validates a fixture
2097
+ * against these schemas; DP-57 records that the fail-closed response to an
2098
+ * unreachable or stale Context Store is a normative MUST with NOTHING in
2099
+ * this repository that goes red when it is violated. Per DP-86 a component
2100
+ * gets a register row whether or not anyone built it, and by the same
2101
+ * argument it gets a schema.
2102
+ * Generated from context_read.schema.json.
2103
+ */
2104
+ export interface ContextRead {
2105
+ /**
2106
+ * The identifier of the snapshot every fact in this object was read from
2107
+ * (section 8.8, `snapshot_hash() -> sha256`). It is what the Decision
2108
+ * carries forward as `context_snapshot_hash` in the receipt (section 9.2)
2109
+ * and inside every Attestation Object (AT-1), so the value a human
2110
+ * attester's device shows and signs over is this one.
2111
+ *
2112
+ * IT IS A NAME FOR A BINDING, NOT EVIDENCE OF ONE (TR-10; RES-8 is the
2113
+ * dossier's wording of the same rule). Worse than the usual case: it is not
2114
+ * merely unverified, it is UNVERIFIABLE. Section 8.8 declares the return
2115
+ * type and no clause anywhere says WHAT BYTES the digest covers or under
2116
+ * which canonicalization, so no consumer can recompute it, no two
2117
+ * implementations can be shown to agree on it, and a store is free to emit a
2118
+ * constant. The consequence is that the receipt's `context_snapshot_hash` --
2119
+ * a field carried through the signed body and through the attested object,
2120
+ * the field that is supposed to say WHICH Context the Decision was computed
2121
+ * over -- discharges nothing today. Section 9.3's checklist has no step that
2122
+ * reads it, which is consistent: there is nothing to check it against.
2123
+ *
2124
+ * This is a normative gap found by writing the schema rather than by reading
2125
+ * the text, which is the same way AT-1's `b64:` nonce type was found to be
2126
+ * unstated (ACP-87, closed at the root as WE-4 in v1.3.18). It is recorded
2127
+ * here and NOT closed here: pinning a preimage in a schema would make this
2128
+ * file the definition of a type the specification does not have, and a
2129
+ * second implementation written from the document alone would never see it.
2130
+ */
2131
+ readonly snapshotHash: Sha256Hash;
2132
+ /**
2133
+ * The age of the last authoritative update behind this read, in whole
2134
+ * seconds (section 8.8, `freshness() -> age of last authoritative update`).
2135
+ * CP-2 makes it a control input rather than telemetry: all providers MUST
2136
+ * honour P-4, and stale freshness resolves to DENY with a distinct reason
2137
+ * code -- unknown is never LOW. What the bound IS depends on the provider
2138
+ * class the signed bundle declares for the tenant (CP-1) and on section
2139
+ * 8.8's provider table: a static signed entitlement bundle has staleness
2140
+ * near zero, workload attestation is per-request, and live directory sync
2141
+ * must be within L-18's 5 minutes for revocations or the sync is broken and
2142
+ * the deployment fails closed.
2143
+ *
2144
+ * THERE IS NO MAXIMUM HERE, DELIBERATELY. Encoding L-18 as a schema bound
2145
+ * would turn a control decision into a parse error: the consumer must DENY
2146
+ * with P-4's distinct reason code, and a malformed-message rejection loses
2147
+ * the reason code, loses the audit record of a Decision that was refused for
2148
+ * staleness, and applies the live-sync bound to providers section 8.8 gives
2149
+ * a different contract. `minimum` is 0 because an age cannot be negative and
2150
+ * a value outside the domain MUST refuse rather than take the permissive
2151
+ * branch -- EL-2 states that reading for the evaluation environment and P-4
2152
+ * states it generally; a negative age read as 'very fresh' is the permissive
2153
+ * branch arriving through a type error.
2154
+ *
2155
+ * THE NAME AND THE UNIT ARE THIS FILE'S, NOT THE SPECIFICATION'S, AND BOTH
2156
+ * ARE GAPS. Section 8.8 names the function `freshness()` and its return 'age
2157
+ * of last authoritative update'; it fixes no unit, no wire type and no name,
2158
+ * so `_seconds` is carried in the field name because a bare duration with
2159
+ * its unit in a comment is a unit nobody reads.
2160
+ *
2161
+ * AND THE RES-8 OBJECTION, STATED RATHER THAN SILENTLY FIXED. An AGE is
2162
+ * computed by the Context Store, on the Context Store's clock, and the
2163
+ * staleness decision that fires CP-2's DENY is then keyed on a derived value
2164
+ * supplied by the party being read -- TR-8's shape exactly. Carrying the
2165
+ * INSTANT of the last authoritative update instead would let the consumer
2166
+ * derive the age against its own NTS-disciplined clock within the L-20 skew
2167
+ * allowance, which is what section 12.5 and leg F9.1 exist to make
2168
+ * available, and would move this row from 'derived value accepted as
2169
+ * transmitted' to 'raw fact plus consumer-computed derivation'. This file
2170
+ * models the return type section 8.8 states. Changing it is a text change,
2171
+ * and making it silently inside a schema is how a register acquires a claim
2172
+ * nobody decided (the ACP-86 shape).
2173
+ *
2174
+ * Schema constraints, carried as documentation and NOT enforced by this
2175
+ * type: minimum 0.
2176
+ */
2177
+ readonly freshnessSeconds: number;
2178
+ /**
2179
+ * The capability facts this snapshot asserts: one entry per (operator,
2180
+ * capability, resource) triple that `has_capability` answered TRUE for
2181
+ * (section 8.8). Consumed at evaluation-order step 4 (operator holds every
2182
+ * required capability, else DENY) and again at section 9.3 step 9, where for
2183
+ * recomputed floor-HIGH the Executor re-queries capability of the operator
2184
+ * established in step 7b(iii-a) -- taken from the verified Attestation
2185
+ * Object, never from the receipt body -- and fails closed if it has been
2186
+ * revoked.
2187
+ *
2188
+ * THERE IS NO BOOLEAN, AND ABSENCE IS THE ANSWER. `has_capability` returns a
2189
+ * bool, but under P-4 and CP-2 the two false-ish outcomes are ONE outcome:
2190
+ * an unknown operator, an unknown resource and a capability the store simply
2191
+ * declined to assert all resolve to DENY. Representing them differently
2192
+ * would give one outcome two encodings while no decision anywhere depends on
2193
+ * the difference -- two spellings of one thing, which is the defect class
2194
+ * this repository is built around, and here it would also split the snapshot
2195
+ * digest.
2196
+ *
2197
+ * `uniqueItems` because a repeated grant is a second spelling of one fact.
2198
+ * Note that a grant's identity is the WHOLE TRIPLE, which is why
2199
+ * `x-acp-keyed-by` is deliberately absent: a lookup keyed by `operator`
2200
+ * alone answers 'granted' for the wrong capability on the wrong resource,
2201
+ * and a generator that emitted one would be building the vulnerability. That
2202
+ * used to cost a check that would not run: `tools/codegen.py` read an
2203
+ * array's `x-acp-absent` only alongside `x-acp-keyed-by`, so the fail-safe
2204
+ * declared below was dropped in silence -- a rule written down that reached
2205
+ * nothing, with every gate green. It was declared anyway, because the schema
2206
+ * is the normative artifact and the rule belongs in it, and the gap was
2207
+ * disclosed rather than worked around by mis-keying the lookup. ACP-78 M1
2208
+ * closed it from the generator's side: an array carrying an absent rule with
2209
+ * no key now HALTS in the root that emits accessors, and in this root the
2210
+ * rule is carried into the generated field's documentation. No multi-field
2211
+ * key was invented, because the key is the decision and inventing it here
2212
+ * would be building the vulnerability.
2213
+ *
2214
+ * No `maxItems`. L-05 bounds list length at ingress, on a request; a Context
2215
+ * read is not a request, and a store whose honest answer exceeds an invented
2216
+ * cap would have to truncate, which silently converts grants into DENYs.
2217
+ *
2218
+ * ABSENT-ENTRY RULE, carried as documentation and NOT enforced by this type,
2219
+ * because this is a message and not signed policy. An entry absent from this
2220
+ * table is a REFUSAL under P-4, CP-2, never a default value: a triple absent
2221
+ * from this list is a capability the snapshot does not assert, and P-4's
2222
+ * default is DENY: no matching rule, unknown capability, unclassified
2223
+ * resource and unavailable Context Store each refuse, with a distinct reason
2224
+ * code. CP-2 binds every provider class to that rule. An accessor that
2225
+ * returns a bare Option here is one unwrap away from treating 'the store
2226
+ * said nothing' as 'the store said yes', which at section 9.3 step 9 is a
2227
+ * revoked operator executing a floor-HIGH action. NO ACCESSOR IS GENERATED
2228
+ * FOR THIS LIST AND THE RULE IS CARRIED AS DOCUMENTATION (the wire root's
2229
+ * `absent_emits` is `doc`), so the sentence above describes what a CONSUMER
2230
+ * must do rather than what a generated lookup does for it. Two things forced
2231
+ * that. A grant's identity is the whole triple `(operator, capability,
2232
+ * resource)` -- section 8.8's `has_capability` takes all three -- and
2233
+ * `x-acp-keyed-by` names ONE field, so a generated lookup keyed by any
2234
+ * single one of them answers 'granted' for the wrong resource, which is
2235
+ * building the vulnerability rather than typing it. And ACP-78 M1 found that
2236
+ * an array's `x-acp-absent` was read only ALONGSIDE `x-acp-keyed-by`:
2237
+ * declared without a key it was dropped in silence, which is the failure the
2238
+ * generator's keyword audit refuses one level up. That is now a HALT in the
2239
+ * root that emits accessors.
2240
+ *
2241
+ * Schema constraints, carried as documentation and NOT enforced by this
2242
+ * type: uniqueItems.
2243
+ */
2244
+ readonly capabilityGrants: readonly CapabilityGrant[];
2245
+ /**
2246
+ * Resource identifier -> RAISE tier (section 8.8, `tier_raise(resource) ->
2247
+ * T0..T3`; TR-2). A RAISE and nothing else: the consumer holds the floor
2248
+ * from the signed bundle (TR-1) and computes `effective_tier(r) =
2249
+ * max(floor(r), raise(r))` itself (TR-3, evaluation order step 7). Absent
2250
+ * raise is T0, so an unclassified resource is governed entirely by its floor
2251
+ * -- which under RK-1 is T3 when `floors.json` does not name it. The two
2252
+ * defaults compose in the safe direction on purpose: absent floor is the
2253
+ * HIGHEST tier, absent raise is the LOWEST, because the floor is the value a
2254
+ * compromised runtime must not be able to lower and the raise is the value
2255
+ * it must not be able to invent downward.
2256
+ *
2257
+ * The key space is `floors.json`'s key space and the bound is copied from
2258
+ * `floors.schema.json` for that reason: TR-3's `max` is taken over the two
2259
+ * maps keyed by the same resource identifier, so a resource nameable in one
2260
+ * map and unnameable in the other has no defined effective tier. It is an
2261
+ * open map for the same reason floors is -- resources are
2262
+ * deployment-specific -- and the VALUES are closed.
2263
+ *
2264
+ * A T0 ENTRY AND AN ABSENT ENTRY MEAN THE SAME THING, AND THE SPECIFICATION
2265
+ * PERMITS BOTH. TR-2 gives the return type as T0..T3 and TR-3 makes
2266
+ * `max(floor, T0) = floor`, so an explicit T0 is a second encoding of 'no
2267
+ * raise'. The enum is not narrowed to T1..T3 here because narrowing the
2268
+ * stated range would be this file exceeding the document; the consequence is
2269
+ * recorded instead, and it is Z4-shaped -- two encodings of one snapshot are
2270
+ * two `snapshot_hash` values for it, in whatever preimage a future clause
2271
+ * defines.
2272
+ *
2273
+ * ABSENT-ENTRY RULE, carried as documentation and NOT enforced by this type,
2274
+ * because this is a message and not signed policy. An entry absent from this
2275
+ * table reads as `T0` under TR-3: TR-3 states it in as many words: absent
2276
+ * raise = T0. This is the one place in this file where the LOWEST value is
2277
+ * the fail-safe one, and the reason is that the raise is not the tier -- the
2278
+ * floor is, it comes from the signed bundle, and RK-1 already defaults an
2279
+ * unnamed resource to T3. A lookup that refused here, or that defaulted
2280
+ * upward, would let a silent Context Store escalate every resource it has
2281
+ * never heard of and hand a compromised store a denial of service against
2282
+ * the attestation queue that AQ-1 to AQ-3 exist to protect. THIS OBJECT
2283
+ * SETTLED A TENSION INSIDE THE GENERATOR AND THE ANSWER IS WORTH READING
2284
+ * WITH THE RULE. `tools/codegen.py` HALTED on a map with no absent rule --
2285
+ * every table a caller can miss needs its absent case stated in the schema
2286
+ * -- while its wire-root header note said no fail-safe accessor should be
2287
+ * emitted in that root at all, because an absent rule on a wire field would
2288
+ * be a default supplied by the party under verification. This is the only
2289
+ * MESSAGE in the register that is a LOOKUP TABLE, so it is the case neither
2290
+ * was written for. ACP-78 M1 resolved it per root: the wire root declares
2291
+ * `absent_emits="doc"`, the rule below is CARRIED into the generated field's
2292
+ * documentation, and NO accessor is emitted. The deciding argument is one
2293
+ * root over: `floors.schema.json` already generates the accessor that
2294
+ * answers T3 on a miss (RK-1, P-4), and a second generated accessor
2295
+ * answering T0 on a miss over a TRANSMITTED map would put the permissive
2296
+ * answer behind an API shaped like a control, one import away from the
2297
+ * signed one. The rule stated here is TR-3's and is right; where it has
2298
+ * effect is in the consumer's `max(floor, raise)`, against a floor that came
2299
+ * from the signed bundle.
2300
+ */
2301
+ readonly tierRaises: Readonly<Record<string, Tier>>;
2302
+ }
2303
+ /**
2304
+ * The lenient projection of ContextRead: every field optional, every enum
2305
+ * degraded to `string`. What a verifier parses. Never make a decision on
2306
+ * one — the fail-safe lookups live on the strict type only.
2307
+ */
2308
+ export interface RawContextRead {
2309
+ readonly snapshotHash?: string;
2310
+ readonly freshnessSeconds?: number;
2311
+ readonly capabilityGrants?: readonly RawCapabilityGrant[];
2312
+ readonly tierRaises?: Readonly<Record<string, string>>;
2313
+ }
2314
+ /**
2315
+ * The SIGNED BODY of a Decision Receipt under receipt version 3, carried on
2316
+ * leg F3.3 of the flow-leg register (ZIFFER-DEPLOY-001 Annex A, Policy
2317
+ * Engine -> Executor; the row read KMS -> Executor until ACP-110, and the
2318
+ * KMS cannot be that sender: B-1a puts the canonical Proposal beside the
2319
+ * receipt on this leg and the KMS never holds it, so the signed receipt
2320
+ * returns to the Policy Engine on F3.2's response). The Executor trusts a
2321
+ * signature, not a channel (9.1): B-4 forbids it accepting any instruction
2322
+ * from any other source, so this object is the only thing in the system that
2323
+ * can cause an external effect.
2324
+ *
2325
+ * WHERE THE EDGE OF THIS OBJECT IS. Two clauses fix it. WE-2: the COSE
2326
+ * payload is the canonical encoding of the receipt structure. AB-5: the
2327
+ * bytes covered by the receipt signature are the canonical encoding of the
2328
+ * receipt body WITHOUT the attestation entries. Every property below is
2329
+ * inside the signature and nothing else is, which is also what makes AB-6
2330
+ * measurable -- the 4,096-byte bound is on THIS object's canonical encoding,
2331
+ * and it is the whole reason version 3 exists.
2332
+ *
2333
+ * WHAT IS NOT A FIELD, EACH ABSENT ON PURPOSE.
2334
+ * (1) The SIGNATURE. WE-1 makes COSE_Sign1 the carrier, so the signature is
2335
+ * the envelope around this payload, never a member of it. The Python
2336
+ * reference carries a sibling `sig` key and excludes it from the signed body
2337
+ * by name (`SIGNED_EXCLUDE`), a stand-in for a COSE envelope it does not yet
2338
+ * emit and which its own CRYPTO DISCLOSURE names as a gap. Giving `sig` a
2339
+ * property here would make that stand-in look normative.
2340
+ * (2) The ATTESTATION ENTRIES. AB-1: the entries MUST travel alongside the
2341
+ * receipt and MUST NOT appear in the signed body; they are bound solely by
2342
+ * `attestation_digests` below. Note what the specification does not say --
2343
+ * it names no envelope structure, no field name and no ordering for that
2344
+ * beside-channel. `attestations` is the reference implementation's choice of
2345
+ * name, not the document's.
2346
+ * (3) `reversibility`. RV-2 is explicit: reversibility lives only in the
2347
+ * signed bundle and there is 'no receipt field of record'. RV-3 recomputes
2348
+ * it, because a receipt-asserted REVERSIBLE would suppress DR-9's
2349
+ * acknowledgement exactly as v1.3.0's forged floor-only risk suppressed
2350
+ * attestation (X1). The reference nevertheless reads a `reversibility` key
2351
+ * when one is present; under this closed field set such a receipt is
2352
+ * malformed, and that disagreement is recorded rather than reconciled.
2353
+ * (4) `attestation_id` and `action_id`. Both are DERIVED: the first is
2354
+ * SHA-256 over the canonical Attestation Object (AT-1, AT-8a) and the
2355
+ * Executor recomputes it (9.3 step 7b(v)); the second is the pair
2356
+ * (proposal_hash, origin_nonce) pinned by the ledger (DS-6a, DS-6f). A
2357
+ * transmitted identifier is a name for a binding, not evidence of one
2358
+ * (RES-9), and a schema that gave either a property would invite exactly
2359
+ * that read.
2360
+ *
2361
+ * WHY THE FIELD SET IS CLOSED, AND THE CAVEAT THAT GOES WITH IT.
2362
+ * `additionalProperties` is false and every property but `origin_nonce` is
2363
+ * required. THE SPECIFICATION DOES NOT STATE THIS RULE. AT-8b closes the
2364
+ * Attestation Object field set and calls the closure the security property
2365
+ * itself; there is no AT-8b for the receipt. Closure is adopted here because
2366
+ * it is the fail-safe direction -- an unknown key is refused rather than
2367
+ * ignored, a missing key is refused rather than defaulted -- and because the
2368
+ * alternative is a per-implementation field set on the one object whose
2369
+ * canonical bytes are signed and whose size is capped. It is recorded as a
2370
+ * gap, not presented as a reading: a clause saying for the receipt what
2371
+ * AT-8b says for the object is a text change this schema cannot make.
2372
+ *
2373
+ * Z4 DOES NOT REACH THIS OBJECT THE WAY IT REACHES THE ATTESTATION OBJECT,
2374
+ * AND THE DIFFERENCE IS WORTH STATING RATHER THAN ASSUMED. An Attestation
2375
+ * Object's ledger identity is a digest of itself, so two encodings are two
2376
+ * ids and one human approval claims two slots (T-14). A receipt's ledger
2377
+ * identity is `nonce`, a carried field consumed under CL-3, so two encodings
2378
+ * of one receipt still claim one slot. What an optional field costs here is
2379
+ * not amplification but DIVERGENCE: one verifier refuses the unknown key,
2380
+ * another ignores it, and at the verifier a divergence is indistinguishable
2381
+ * from a forgery. That is a weaker consequence than Z4's and it is still a
2382
+ * reason to close the set.
2383
+ *
2384
+ * ENCODING. Canonical CBOR, RFC 8949 section 4.2 (WE-1, WE-2) -- the same
2385
+ * canonicalization AT-8a pins for the Attestation Object. JCS (RFC 8785) is
2386
+ * the Proposal's rule and MUST NOT be used here. WE-3: a reference decoder
2387
+ * emits JSON for review and JSON MUST NOT be accepted as receipt input, so
2388
+ * the shape below is diagnostic form and never a wire form.
2389
+ *
2390
+ * WHAT THIS SCHEMA CANNOT CONSTRAIN. Listed so that shape validity is never
2391
+ * read as conformance; each item is a per-implementation obligation and
2392
+ * several carry their own conformance case and mutant (AB-5).
2393
+ * - AB-2's ORDERING. `attestation_digests` MUST be sorted strictly ascending
2394
+ * over the UTF-8 bytes of the digest strings including the `sha256:` prefix.
2395
+ * JSON Schema has no sortedness keyword, so an unsorted list is shape-valid
2396
+ * here and MUST still be refused under AB-2.
2397
+ * - AB-6's SIZE BOUND. 4,096 bytes of canonical encoding is a property of an
2398
+ * encoding, not of a JSON shape, and AB-6 requires it be checked on measured
2399
+ * bytes rather than on any proxy such as an entry count -- the first draft
2400
+ * capped the count at 50 and measured 4,107 bytes at that cap.
2401
+ * - AB-4's CARDINALITY and AB-1's MEMBERSHIP. Both relate this object to
2402
+ * entries that are not members of it, so neither is expressible here.
2403
+ * - L-14 / 9.3 step 5's WINDOW LENGTH: `expires_at - issued_at` <= 120 s is
2404
+ * arithmetic across two fields.
2405
+ * - CR-3's conjunctive verification and CR-4's containment floor. `alg`
2406
+ * names the suite; whether every declared primitive verified, and whether
2407
+ * the suite contains every primitive the bundle floor names, are decided
2408
+ * elsewhere and never from a transmitted value.
2409
+ * - WHETHER THIS IS A RE-DRIVE. DS-6b says a re-drive receipt MUST carry
2410
+ * `origin_nonce`, and no field in the receipt declares that it is one, so
2411
+ * the conditional obligation cannot be expressed at any layer that reads
2412
+ * only this object.
2413
+ *
2414
+ * SHARED TYPES ARE REFERENCED, NEVER REPEATED. `sha256` and `risk_level`
2415
+ * come from attestation_object.schema.json, the 128-bit `b64:` nonce from
2416
+ * ledger_claim.schema.json where CL-3 consumes it, and two come from the
2417
+ * SIGNED BUNDLE root across the root boundary: `suite_id` from
2418
+ * signature.schema.json, where CR-4's floor is registered, and
2419
+ * `fidelity_class` from adapters.schema.json, the table TR-8 verifies this
2420
+ * receipt's `fidelity` against (ACP-73; it lived in proposal.schema.json, on
2421
+ * the side being verified, until that table existed). Each is defined once
2422
+ * and pointed at from here. A second copy would be a second statement of one
2423
+ * object, which is the defect this repository publishes corrections for --
2424
+ * and the generator halts on a duplicate `x-acp-name` within a root and
2425
+ * across the two, so the copy would not even be silent. A `$ref` may cross
2426
+ * roots for a named SCALAR only (ACP-78 M1): the vocabulary a receipt is
2427
+ * compared against is owned by the signed side, and an object crossing would
2428
+ * drag its field graph with it. This paragraph had said the opposite -- that
2429
+ * a cross-root `$ref` was not a rule the generator had and `suite_id` was
2430
+ * therefore one type in two files -- for the whole of M1, while the `alg`
2431
+ * property below already crossed; prose describing a generator is exactly as
2432
+ * trustworthy as prose describing anything else nothing executes.
2433
+ *
2434
+ * That the homes of the wire-root types are scattered across message schemas
2435
+ * is an accident of which file happened to need them first, not a design.
2436
+ * The durable answer is one shared definitions file in this root that every
2437
+ * message schema references; there is none, so the placement is recorded
2438
+ * here rather than presented as intentional.
2439
+ *
2440
+ * SUITE 12 CLASSIFICATION. At the schema layer every field is class T,
2441
+ * because this document constrains SHAPE and shape is not authority. The
2442
+ * Executor's obligations are elsewhere and are named on the individual
2443
+ * properties rather than restated here: `proposal_hash` and the bundle basis
2444
+ * are BOUND (9.3 steps 3 and 4), floor-only risk and fidelity are RECOMPUTED
2445
+ * and the transmitted values only ever raise a critical alert on
2446
+ * disagreement (TR-8, 9.3 step 7a), the operator identity is taken from the
2447
+ * verified Attestation Objects and never from this body (Y4), and the origin
2448
+ * nonce is read from the immutable ledger binding (DS-6f).
2449
+ *
2450
+ * DISAGREEMENTS SURFACED BY WRITING THIS SCHEMA, PUBLISHED RATHER THAN
2451
+ * SMOOTHED OVER. Section 9.2's example is the only enumeration of receipt
2452
+ * fields the specification gives, and it omits two fields other clauses make
2453
+ * mandatory: `alg` (CR-1 names the receipt explicitly, CR-5 makes it
2454
+ * signature-covered) and `idempotency_key` (DS-6b: 'MUST be carried as a
2455
+ * distinct receipt field'). An implementer building a receipt from that
2456
+ * block alone emits one that is CR-1 non-conformant while conforming to the
2457
+ * text they read, which is the PB-8 shape the document has already published
2458
+ * a correction for. In the other direction, both current implementations
2459
+ * omit nine fields the example carries -- `schema_id`, `schema_version`,
2460
+ * `schema_hash`, `fidelity`, `context_snapshot_hash`, `rule_id`,
2461
+ * `risk_level_effective`, `risk_level_floor_only` and `audit_id` -- and
2462
+ * spell `issued_at` and `expires_at` as numeric epoch seconds where the
2463
+ * example spells RFC 3339. Modelling the specification therefore makes every
2464
+ * fixture in the repository invalid against this file. That is disclosed,
2465
+ * not designed around; ACP-52 already records that nothing validates a
2466
+ * fixture against these schemas and that the fixtures are schema-invalid.
2467
+ * Generated from decision_receipt.schema.json.
2468
+ */
2469
+ export interface DecisionReceipt {
2470
+ /**
2471
+ * AB-0: exactly 3, verified BEFORE any other check, and every other value
2472
+ * rejected including its absence. Written as an inclusive bound of exactly
2473
+ * one value because that is what AB-0 admits.
2474
+ *
2475
+ * THERE IS NO COMPATIBILITY WINDOW AND THE REASON IS NOT COMPATIBILITY.
2476
+ * Version 2 is the weaker representation; a verifier that accepts both lets
2477
+ * an attacker present version 2 and never face the digest binding at all. A
2478
+ * transition window is therefore not a migration aid, it is a downgrade path
2479
+ * -- the CR-4 shape one layer up, with the FORMAT rather than the suite
2480
+ * negotiated by the party under verification.
2481
+ *
2482
+ * The number is 3 and not 2 because section 9.2's published example already
2483
+ * carried `"receipt_version": 2` for the containment form, and one version
2484
+ * number with two meanings is the two-definitions defect at the wire.
2485
+ *
2486
+ * A verifier MUST NOT let shape validation stand in for this check: a
2487
+ * version-2 receipt has to be refused by the name AB-0, because the
2488
+ * cross-language differential compares refusal names and a generic schema
2489
+ * error is not one.
2490
+ *
2491
+ * Schema constraints, carried as documentation and NOT enforced by this
2492
+ * type: minimum 3, maximum 3.
2493
+ */
2494
+ readonly receiptVersion: number;
2495
+ /**
2496
+ * CR-1: every signed structure MUST carry an explicit `alg` naming a
2497
+ * signature suite registered in the signed bundle, and the receipt is named
2498
+ * in that clause. CR-5 makes it signature-covered, so an issuer cannot
2499
+ * rewrite the suite without invalidating the receipt it travelled on.
2500
+ *
2501
+ * Carrying the suite is not the same as choosing it. The FLOOR comes from
2502
+ * the signed bundle and is satisfied by CONTAINMENT of primitives, never by
2503
+ * rank (CR-4): a suite is a set, `hybrid-ed25519-mldsa65` and `slhdsa128s`
2504
+ * are incomparable, and every implementation that built a rank table from
2505
+ * the pre-v1.3.15 wording served a lattice signature to a deployment that
2506
+ * had chosen hash-based precisely to avoid one. Reading the floor from
2507
+ * anything transmitted asks the party under verification how strictly to
2508
+ * judge it.
2509
+ *
2510
+ * NOTE that section 9.2's receipt example does not show this field, while
2511
+ * CR-1 and CR-5 require it. The clauses govern; the example is incomplete.
2512
+ *
2513
+ * THE VALUE DOMAIN IS DECLARED ONCE, IN THE BUNDLE ROOT, and this field
2514
+ * `$ref`s it across roots:
2515
+ * `spec/schemas/bundle/signature.schema.json#/$defs/suite_id`. It was
2516
+ * declared in both roots until ACP-78 M1 -- one vocabulary, two definitions,
2517
+ * with each copy carrying its own note saying the two must move together,
2518
+ * which is the arrangement that let the strict-Ed25519 rule sit in two
2519
+ * languages. CR-4 compares the receipt's suite against the bundle's
2520
+ * registered floor, so the two artifacts must draw from ONE set or the
2521
+ * comparison is between two vocabularies that merely look alike. It is
2522
+ * deliberately NOT `const`: listing only the hybrid suite would make a
2523
+ * receipt signed under `ed25519` fail SHAPE validation when the required
2524
+ * outcome is a CR-4 refusal naming the floor. A name outside the set is
2525
+ * refused under CR-1 by the verifier and never defaulted to a known suite.
2526
+ */
2527
+ readonly alg: SuiteId;
2528
+ /**
2529
+ * The graded outcome of section 8.4 step 11. 9.3 step 2: the Executor
2530
+ * proceeds only on ALLOW.
2531
+ *
2532
+ * DELIBERATELY NOT `const: "ALLOW"`, for the reason `suite_id` in the bundle
2533
+ * signature schema is not a single-value enum: pinning the only acceptable
2534
+ * value here would make a DENY receipt fail SHAPE validation when the
2535
+ * required outcome is a refusal named 9.3-2. A check that fires under the
2536
+ * wrong name is a divergence waiting to happen, because the cross-language
2537
+ * differential compares names.
2538
+ *
2539
+ * EO-1: HIGH is never auto-allowed by any configuration, so an ALLOW over a
2540
+ * floor-HIGH action is a claim about a quorum, and that claim is checked
2541
+ * against `attestation_digests` and the entries beside the receipt rather
2542
+ * than believed.
2543
+ */
2544
+ readonly decision: DecisionOutcome;
2545
+ /**
2546
+ * The canonical Proposal this receipt authorises. 9.3 step 3: the Executor
2547
+ * hashes the Proposal IT received over the same channel (B-1a),
2548
+ * canonicalized identically, and compares -- it hashes what it will execute,
2549
+ * never 'whatever the receipt describes'. Class B.
2550
+ *
2551
+ * This field is a name for a binding. The binding is the comparison, and it
2552
+ * exists because a receipt that merely asserts what it covers is a claim by
2553
+ * the party under verification (RES-9).
2554
+ */
2555
+ readonly proposalHash: Sha256Hash;
2556
+ /**
2557
+ * SR-1: schemas are content-addressed by `schema_id` + `schema_version` +
2558
+ * `schema_hash`, and SR-5 requires every Decision to carry the triple. The
2559
+ * pattern is section 6.3's `AtomEnvelope`, so the receipt's identifier is
2560
+ * the same type the ingress validated.
2561
+ *
2562
+ * The triple is not decoration: TR-8 has the Executor verify `fidelity`
2563
+ * against the bundle-registered adapter binding for THIS `schema_id`, so an
2564
+ * unregistered id refuses rather than grading.
2565
+ */
2566
+ readonly schemaId: SchemaId;
2567
+ /**
2568
+ * Semver, per SR-1 and section 6.3's `AtomEnvelope`. Part of the content
2569
+ * address, not a compatibility hint: SR-3 decides narrowing against a
2570
+ * bounded lattice precisely because v1.2.0 left 'narrowing' undefined and a
2571
+ * net-widening change could self-classify and bypass distinct-reviewer
2572
+ * approval.
2573
+ */
2574
+ readonly schemaVersion: SchemaVersion;
2575
+ /**
2576
+ * The third leg of the SR-1 content address. SR-2: the validator refuses to
2577
+ * start on a signature or hash mismatch, so a receipt naming a schema hash
2578
+ * the bundle does not carry describes a grammar nobody signed.
2579
+ */
2580
+ readonly schemaHash: Sha256Hash;
2581
+ /**
2582
+ * The adapter-stamped intent-fidelity class of the Proposal (SR-5).
2583
+ * DIAGNOSTIC AND AUDIT ONLY, stated normatively in section 9.2's authority
2584
+ * note.
2585
+ *
2586
+ * 9.3 step 7 has the Executor verify fidelity against the bundle-registered
2587
+ * adapter binding for `schema_id`, and 7a makes disagreement with this
2588
+ * transmitted value a CRITICAL ALERT rather than a preference: a
2589
+ * validly-signed receipt asserting `F-HIGH` for a Proposal from an
2590
+ * F-LOW-bound adapter means the signing substrate is emitting Decisions the
2591
+ * bundle does not support (T-18).
2592
+ *
2593
+ * TWO VALUES, NOT THREE, AND THE TYPE IS DEFINED ONCE. There is no
2594
+ * `F-MEDIUM`: this class is what actually varied between the retired Door A
2595
+ * / Door B binary, and it is a property of an INGRESS ADAPTER rather than a
2596
+ * separate architecture; it is also orthogonal to the provider class (static
2597
+ * entitlements against live directory sync), a conflation the document
2598
+ * already carries a correction for in rows 7 and 8 of its applicability
2599
+ * table. The type itself lives in the signed bundle's adapters.schema.json
2600
+ * -- the table this value is verified AGAINST (ACP-73), where it was homed
2601
+ * once that table existed; before that it lived in proposal.schema.json, on
2602
+ * the side being verified -- and is referenced here rather than repeated:
2603
+ * two definitions of one type is the defect this repository publishes
2604
+ * corrections for, and the generator halts on a duplicate `x-acp-name`
2605
+ * within a root and across the two.
2606
+ */
2607
+ readonly fidelity: FidelityClass;
2608
+ /**
2609
+ * The signed bundle the Decision was computed against. 9.3 step 4: it MUST
2610
+ * match the bundle the Executor already trusts. Class B, and the direction
2611
+ * matters -- the Executor compares against its own bundle rather than
2612
+ * loading the one the receipt names, which would be the verifier taking its
2613
+ * policy from the party under verification.
2614
+ */
2615
+ readonly policyBundleHash: Sha256Hash;
2616
+ /**
2617
+ * Epoch of that bundle. 9.3 step 4 requires it to be at least the Executor's
2618
+ * last-seen epoch, and the ledger records the high-water mark (CL-3), so a
2619
+ * receipt naming an older epoch is a rollback attempt and fails closed.
2620
+ *
2621
+ * Monotonicity is ALSO enforced one hop earlier, at the KMS, against a
2622
+ * durable high-water mark before it will sign at all (9.1.1 step 5, RAD-3).
2623
+ * Two enforcement points, neither redundant: the KMS check stops a
2624
+ * stale-bundle Decision being minted, the Executor check stops one being
2625
+ * replayed.
2626
+ *
2627
+ * Schema constraints, carried as documentation and NOT enforced by this
2628
+ * type: minimum 0.
2629
+ */
2630
+ readonly bundleEpoch: number;
2631
+ /**
2632
+ * The Context read the Decision was computed over (section 8.8
2633
+ * `snapshot_hash()`). Context reads are snapshot-consistent and AC-3 makes
2634
+ * the consumed snapshot part of the audit record, so replay is exact rather
2635
+ * than approximate.
2636
+ *
2637
+ * Recording the snapshot does not make Context trustworthy, and the receipt
2638
+ * is not where that is decided: TR-8 has the Executor recompute floor-only
2639
+ * risk while IGNORING every Context-Store raise, which is why a compromised
2640
+ * Context Store can still withhold a capability fact (DENY, safe) or raise a
2641
+ * tier (more attestation, safe) but cannot suppress escalation.
2642
+ */
2643
+ readonly contextSnapshotHash: Sha256Hash;
2644
+ /**
2645
+ * The bundle rule that decided. AT-3 makes the rule identity part of what an
2646
+ * attester is SHOWN -- floor tiers and the bundle `rule_id`s that raised the
2647
+ * risk, deliberately not Context-asserted values, which is the separation
2648
+ * that closes A-8's display path -- and section 9.2 carries one in the
2649
+ * receipt for replay.
2650
+ *
2651
+ * TWO CAVEATS, BOTH GAPS RATHER THAN DESIGN. No clause pins this
2652
+ * identifier's syntax; the only pattern the specification attaches to a
2653
+ * thing called `rule_id` belongs to a DIFFERENT object -- the firewall rule
2654
+ * identifier inside section 6.3's `ModifyFirewallRule` payload, constrained
2655
+ * to `^[a-z0-9-]{1,32}$`, which the receipt example's own `RULE-002` would
2656
+ * fail. One name, two normative meanings, which is the CL-7 shape the
2657
+ * document has published a correction for. And AT-3 speaks of the rule idS
2658
+ * that raised the risk, plural, while the receipt carries one; nothing says
2659
+ * which one that is when several raised it. No pattern is asserted here
2660
+ * because inventing one would be a third meaning.
2661
+ *
2662
+ * Schema constraints, carried as documentation and NOT enforced by this
2663
+ * type: minLength 1, maxLength 128.
2664
+ */
2665
+ readonly ruleId: string;
2666
+ /**
2667
+ * Risk after Context raises and accumulator effects (section 8.4 step 9,
2668
+ * AC-4, RK-3 monotone max). DIAGNOSTIC AND AUDIT ONLY -- section 9.2's
2669
+ * authority note is explicit that this field is NOT authoritative for any
2670
+ * Executor control decision.
2671
+ *
2672
+ * It is carried because replay needs it and refused authority because X1 was
2673
+ * exactly this field's neighbour being believed. Effective risk can only
2674
+ * ever be at or above floor-only risk, and the Executor keys every
2675
+ * attestation requirement on the RECOMPUTED floor-only value (TR-8), so a
2676
+ * receipt inflating this field buys an attacker nothing and deflating it
2677
+ * changes nothing.
2678
+ */
2679
+ readonly riskLevelEffective: Risk;
2680
+ /**
2681
+ * Floor-only risk: the value derived from the target resource's signed tier
2682
+ * floor and the bundle's risk functions, with every Context-Store raise
2683
+ * ignored. DIAGNOSTIC AND AUDIT ONLY.
2684
+ *
2685
+ * THIS IS THE FIELD RES-8 IS ABOUT. In v1.3.0 the Executor read it, and a
2686
+ * forged `risk_level_floor_only` below the bundle-derived value suppressed
2687
+ * the attestation requirement entirely (X1). TR-8 now has the Executor
2688
+ * RECOMPUTE it from the trusted bundle's floors and risk functions applied
2689
+ * to the independently-received canonical Proposal, and 9.3 step 7a makes
2690
+ * disagreement with the value transmitted here a CRITICAL ALERT, not a log
2691
+ * line: it means the signing substrate produced a Decision the bundle does
2692
+ * not support (T-18).
2693
+ *
2694
+ * The fail-safe defaults that feed the recomputation live in the bundle
2695
+ * schemas and are not restated here: a resource absent from `floors.json` is
2696
+ * T3 (RK-1), an action with no risk function is REFUSED at 8.4-3 rather than
2697
+ * graded HIGH, and unknown is never LOW (P-4).
2698
+ */
2699
+ readonly riskLevelFloorOnly: Risk;
2700
+ /**
2701
+ * AB-1: the receipt's commitment to the attestations, one digest per entry,
2702
+ * with the entries themselves travelling alongside and never inside the
2703
+ * signed body. THIS ARRAY IS THE ENTIRE BINDING under version 3.
2704
+ *
2705
+ * THE PREIMAGE IS THE WHOLE ENTRY, AND THAT IS THE TRAP. Each digest is
2706
+ * `sha256:` over the canonical encoding of the complete entry -- `obj`,
2707
+ * `kind`, `attester` and `sig` together -- and MUST NOT be `attestation_id`,
2708
+ * nor any digest whose preimage is the Attestation Object alone.
2709
+ * `attestation_id` already exists, is already normative, and is the obvious
2710
+ * thing to reuse; it is wrong. The AT-1 field set is closed over the OBJECT
2711
+ * and contains no `kind`, no `attester` and no `sig`, while quorum
2712
+ * composition is decided from the ENTRY -- `kind` selects approval or
2713
+ * confirmation, `attester` supplies the identity distinctness is counted
2714
+ * over -- and the attester signs neither. Under version 2 containment sealed
2715
+ * them. Commit only to the object and an attacker on the transport path
2716
+ * flips one entry's `kind` from `confirmation` to `approval` while every
2717
+ * digest still matches. Two identifiers now exist and MUST NOT be merged:
2718
+ * `attestation_id` is what the ledger consumes for single use under CL-3,
2719
+ * this digest is what the receipt authorised byte for byte.
2720
+ *
2721
+ * WHAT `uniqueItems` DOES AND DOES NOT SAY. AB-3 forbids repeated digests
2722
+ * and requires the check at THIS layer rather than delegated to CL-3's
2723
+ * `attestation_id` claim, because CL-3 is object-scoped and needs a
2724
+ * Consumption Ledger -- a stateless verifier has no ledger for it to fire
2725
+ * in, and there AB-3 is the only defence. `uniqueItems` is the shape
2726
+ * statement of that rule; a duplicate MUST still be refused under the name
2727
+ * AB-3, not as a generic validation error. In an Executor that does hold a
2728
+ * ledger AB-3 is masked by CL-3 and kills no mutant, which is recorded as a
2729
+ * positive-path obligation rather than dressed up as a control.
2730
+ *
2731
+ * AN EMPTY ARRAY IS VALID AND `minItems` IS DELIBERATELY ABSENT. A LOW or
2732
+ * MEDIUM Decision carries no attestations. Requiring one entry would make
2733
+ * every fast-path receipt malformed, and requiring the quorum size here
2734
+ * would be reading the size of the quorum off the receipt -- AB-4 says
2735
+ * quorum MUST be counted from VERIFIED attestations and never derived from
2736
+ * the length of this list, which is AT-3's defect one layer down.
2737
+ *
2738
+ * ORDERING IS NOT CONSTRAINED HERE AND IS NORMATIVE: AB-2 requires strictly
2739
+ * ascending lexicographic order over the UTF-8 bytes of the digest strings
2740
+ * INCLUDING the `sha256:` prefix, because without a declared order one
2741
+ * quorum yields two canonical encodings of one receipt and therefore two
2742
+ * receipt identities -- the Z4 encoding split reached with no optional field
2743
+ * involved.
2744
+ *
2745
+ * Schema constraints, carried as documentation and NOT enforced by this
2746
+ * type: uniqueItems.
2747
+ */
2748
+ readonly attestationDigests: readonly Sha256Hash[];
2749
+ /**
2750
+ * The tenant the Decision was made for. 9.3 step 8: it MUST equal the tenant
2751
+ * of the independently-received Proposal, so a validly-signed receipt from
2752
+ * another tenant fails closed rather than executing across the boundary.
2753
+ *
2754
+ * The ledger is shared per tenant and its uniqueness constraints are keyed
2755
+ * on `(tenant_id, artifact_class, identifier)` (CL-5), so this value also
2756
+ * decides which single-use registry a nonce is claimed in. No pattern is
2757
+ * pinned because the specification pins none.
2758
+ */
2759
+ readonly tenantId: TenantId;
2760
+ /**
2761
+ * The operator the action is attributed to. DIAGNOSTIC ONLY, and this is
2762
+ * stated normatively rather than as guidance (section 9.2's authority note,
2763
+ * Y4).
2764
+ *
2765
+ * The authoritative operator identity for approver-distinctness (AT-2) and
2766
+ * for the 9.3 step 9 capability recheck is `obj.operator` inside each
2767
+ * verified Attestation Object, which is signature-covered; 9.3 step
2768
+ * 7b(iii-a) requires it to be identical across every entry and requires the
2769
+ * identity to be taken from the verified object, NEVER resolved in favour of
2770
+ * this body field. A receipt whose body `operator` differs from the attested
2771
+ * one fails closed. Mechanized as `Y4_OperatorTamperDetected`.
2772
+ *
2773
+ * Below floor-HIGH no quorum exists and there is no attested operator to
2774
+ * prefer, so this field is what audit records -- which is a disclosed
2775
+ * asymmetry, not a second authority.
2776
+ */
2777
+ readonly operator: OperatorId;
2778
+ /**
2779
+ * RFC 3339 instant of issuance, as section 9.2 renders it. 9.3 step 5: not
2780
+ * future beyond a 5 s skew allowance.
2781
+ *
2782
+ * Evaluated against NTS-disciplined wall time within the L-20 skew
2783
+ * allowance, never against a monotonic clock -- the two are different
2784
+ * quantities and DP-52 states the distinction; leg F9.1 carries the
2785
+ * dependency.
2786
+ *
2787
+ * TWO DISAGREEMENTS ARE RECORDED HERE RATHER THAN RESOLVED. Both current
2788
+ * implementations spell this field as numeric epoch seconds, because 9.3
2789
+ * step 5 does arithmetic on it, while the specification renders it RFC 3339
2790
+ * in the only place it shows a receipt; the schema follows the
2791
+ * specification. And RFC 3339 admits several spellings of one instant -- `Z`
2792
+ * against `+00:00`, fractional seconds present or absent -- so a
2793
+ * canonical-CBOR body containing one is not yet one encoding per instant.
2794
+ * For the receipt that costs a diagnostic mismatch; for the Attestation
2795
+ * Object, whose id is a digest of itself, it is Z4's two-ids consequence
2796
+ * reached through a type no clause pins, which is the WE-4 shape one layer
2797
+ * over.
2798
+ */
2799
+ readonly issuedAt: Rfc3339Instant;
2800
+ /**
2801
+ * RFC 3339 instant after which the receipt is void. 9.3 step 5 refuses an
2802
+ * expired receipt AND enforces `expires_at - issued_at <= 120 s`.
2803
+ *
2804
+ * THE WINDOW LENGTH IS EXECUTOR-ENFORCED, WHICH IS THE WHOLE POINT OF Y2.
2805
+ * Both endpoints are written by the KMS, so a verifier that only checks 'not
2806
+ * yet expired' has delegated the validity window to the party under
2807
+ * verification: a compromised signer issues a receipt valid for a week and
2808
+ * every subsequent check still passes. Violation fails closed with a
2809
+ * critical alert.
2810
+ *
2811
+ * The relation between the two fields cannot be expressed in this schema, so
2812
+ * shape validity says nothing about it (DR-6 depends on the same bound: a
2813
+ * hold window at or above the L-14 ceiling would outlive receipt validity
2814
+ * and fails closed).
2815
+ */
2816
+ readonly expiresAt: Rfc3339Instant;
2817
+ /**
2818
+ * The receipt nonce, claimed atomically against the Consumption Ledger at
2819
+ * 9.3 step 6 (CL-2 compare-and-swap; if the ledger is unavailable the
2820
+ * Executor fails closed; CL-4 keeps the claim for at least receipt validity
2821
+ * plus 24 h, because a registry that forgets a nonce before the receipt
2822
+ * expires is a replay window). Single use is enforced by that claim, not
2823
+ * asserted as a property.
2824
+ *
2825
+ * IT IS ALSO THE ORIGIN OF ACTION IDENTITY. DS-6a: the nonce of the FIRST
2826
+ * receipt issued for a Proposal becomes `origin_nonce`, and DS-6f has the
2827
+ * ledger claim the immutable `proposal_hash -> origin_nonce` binding
2828
+ * atomically with this claim. That binding is written once and read many
2829
+ * times, a rebind attempt is a critical alert, and its retention is
2830
+ * indefinite because an expiring origin binding reopens Z3 for any
2831
+ * long-lived re-drive.
2832
+ *
2833
+ * CHECKED AT 9.3 STEP 6, BEFORE THE CLAIM, IN BOTH LANGUAGES (ACP-89 -- this
2834
+ * paragraph recorded a gap until then, and the gap is why it is still here).
2835
+ * WE-4 governs this field exactly as it governs the attestation nonce -- the
2836
+ * `b64:` prefix is part of the value, carried and hashed as that string --
2837
+ * and for three releases nothing read the clause here: the value went
2838
+ * straight into the ledger claim in the reference and was unread in
2839
+ * `acp-decision`, so two spellings of one nonce claimed two ledger slots,
2840
+ * which is T-13 through the hole WE-4 exists to close. Step 6 now reads the
2841
+ * nonce BEFORE it claims: a value that is not `b64:` + RFC 4648 section 4
2842
+ * with padding refuses under WE-4, a well-formed value that is not 128-bit
2843
+ * then refuses under L-17, and only the validated string reaches the ledger
2844
+ * -- so a malformed nonce never consumes a slot, and it is rejected rather
2845
+ * than normalized for the reason the next paragraph gives.
2846
+ * `reference/src/acp_executor.py` and `crates/acp-decision/src/decide.rs`
2847
+ * carry the two checks at the same position in the same order, and the
2848
+ * cross-language differential compares both refusal NAMES rather than both
2849
+ * verdicts. WHAT THIS STILL DOES NOT REACH, because a control that
2850
+ * overstates itself here is the defect this paragraph was written about:
2851
+ * only the stateless half is checked -- the CL-2 claim and L-17's retention
2852
+ * half (CL-4) are the ledger's and are absent from `acp-decision` (ACP-46)
2853
+ * -- and nothing validates a receipt against this schema at any point
2854
+ * (ACP-52), so the pattern below is enforced by two implementations that
2855
+ * agree with it, never by a validator reading this file.
2856
+ *
2857
+ * THE TYPE IS DEFINED ONCE AND THE PATTERN IS TWO RULES THAT REFUSE UNDER
2858
+ * DIFFERENT NAMES. `b64:` followed by the RFC 4648 section 4 alphabet WITH
2859
+ * padding is WE-4, the wire type; the twenty-two characters plus `==` are
2860
+ * L-17's 128 bits. A verifier folding them into one check answers WE-4 for a
2861
+ * 64-bit nonce, which is a perfectly well-formed `b64:` value -- the wrong
2862
+ * clause name, and the cross-language differential compares names. The
2863
+ * prefix is PART OF THE VALUE: WE-4 requires it be carried, hashed and
2864
+ * signed as that string, and stripping it, dropping the padding or
2865
+ * substituting the URL-safe alphabet each produce a DIFFERENT value that
2866
+ * MUST be rejected rather than normalized -- normalizing makes one verifier
2867
+ * accept both spellings and hands the divergence to the next implementation,
2868
+ * where it is indistinguishable from a forgery. The type was pinned at the
2869
+ * root only in v1.3.18, after existing solely inside section 9.2's
2870
+ * diagnostic JSON with no clause saying whether the prefix counted; it was
2871
+ * found by writing a schema (ACP-87), which is the argument for writing
2872
+ * them. It is referenced from ledger_claim.schema.json rather than repeated
2873
+ * here, because the ledger is where a nonce is consumed and one wire type
2874
+ * must have one definition.
2875
+ */
2876
+ readonly nonce: Nonce128;
2877
+ /**
2878
+ * The identifier of the audit record for this Decision, carried so a receipt
2879
+ * can be tied to the hash-chained audit entry (AU-1) without a search.
2880
+ * Section 9.2 carries it; R-1 uses the same name for the identifier returned
2881
+ * with a rejection, so one value class spans both outcomes.
2882
+ *
2883
+ * NO CLAUSE PINS ITS SYNTAX, and none is invented here --
2884
+ * `minLength`/`maxLength` bound the shape and nothing more. Note also what
2885
+ * this field is NOT: a receipt naming an audit id is not evidence that the
2886
+ * record exists or that the chain contains it. AU-3a's detection rests on
2887
+ * multi-party anchoring of the chain, never on an identifier the same party
2888
+ * transmits.
2889
+ *
2890
+ * Schema constraints, carried as documentation and NOT enforced by this
2891
+ * type: minLength 1, maxLength 128.
2892
+ */
2893
+ readonly auditId: string;
2894
+ /**
2895
+ * The target idempotency key, `SHA-256(canonical(action_id))` where
2896
+ * `action_id` is the pair `(proposal_hash, origin_nonce)`. DS-6b requires it
2897
+ * be carried as a distinct receipt field; DS-1 requires the Executor use it
2898
+ * as the target's idempotency key, which is what makes exactly-once hold
2899
+ * ACROSS re-drives.
2900
+ *
2901
+ * ACTION IDENTITY IS NOT AUTHORIZATION IDENTITY, AND CONFLATING THEM WAS Y3.
2902
+ * Through v1.3.3 this key was `attestation_id`, which is fresh on every
2903
+ * re-drive -- so in exactly the lost-outcome case DS-3 exists to handle, the
2904
+ * target saw a new key for an action that may already have committed and a
2905
+ * floor-HIGH non-idempotent action doubled. DS-6c keeps both properties: the
2906
+ * key is stable, the authorization is fresh.
2907
+ *
2908
+ * IT IS TRANSMITTED AND IT IS NOT BELIEVED. The Executor derives the key
2909
+ * from the ledger-pinned origin binding and compares (DS-6f); a re-drive
2910
+ * receipt whose key differs from the ledger-derived value fails closed.
2911
+ * DS-6d forbids a coarser key such as `proposal_hash` alone, which would
2912
+ * suppress a legitimate second execution of an identical Proposal.
2913
+ *
2914
+ * Section 9.2's example does not show this field although DS-6b says it MUST
2915
+ * be carried, and the reference treats it as optional; both are recorded as
2916
+ * gaps.
2917
+ */
2918
+ readonly idempotencyKey: Sha256Hash;
2919
+ /**
2920
+ * THE ONE OPTIONAL FIELD IN THIS SCHEMA, and the justification is owed
2921
+ * rather than assumed.
2922
+ *
2923
+ * WHAT IT IS. The receipt nonce of the FIRST receipt issued for this
2924
+ * Proposal (DS-6a). DS-6b requires a re-drive receipt to carry it. DS-6f
2925
+ * then makes the carried value DIAGNOSTIC ONLY: the Executor reads
2926
+ * `origin_nonce` from the immutable ledger binding and derives
2927
+ * `idempotency_key` from the value it read, and if this field is present it
2928
+ * MUST equal the ledger value or the Executor fails closed with a critical
2929
+ * alert.
2930
+ *
2931
+ * WHY IT IS OPTIONAL RATHER THAN REQUIRED. DS-6f says 'if present', in those
2932
+ * words, so requiring it would contradict the text; and a first receipt has
2933
+ * no origin other than its own nonce, so there is nothing for it to carry.
2934
+ * The alternative -- omitting the property entirely -- is worse under
2935
+ * `additionalProperties: false`, because it would make every conformant
2936
+ * re-drive receipt malformed.
2937
+ *
2938
+ * WHY THAT IS NOT Z4 HERE, STATED SO THE EXEMPTION IS ARGUED RATHER THAN
2939
+ * CLAIMED. Z4's consequence is two canonical encodings of one object
2940
+ * yielding two ids and therefore two ledger slots. That reaches the
2941
+ * Attestation Object because its ledger identity IS a digest of itself. A
2942
+ * receipt's ledger identity is `nonce`, carried, so present-and-absent forms
2943
+ * of one receipt still claim one slot. And omission cannot move the
2944
+ * idempotency key, because DS-6f makes the ledger the source and this field
2945
+ * merely a cross-check against it -- which is exactly the fix for Z3, where
2946
+ * DS-6b as first drafted required only that the claimed origin be A consumed
2947
+ * nonce. That is a MEMBERSHIP test, not a PINNING test: a compromised KMS
2948
+ * named any other consumed nonce, the key moved, the target could not
2949
+ * deduplicate, and the floor-HIGH action doubled. Fourth recurrence of the
2950
+ * RES-8 class, in the machinery the previous fix introduced.
2951
+ *
2952
+ * WHAT OMITTING IT DOES COST: the diagnostic cross-check disappears, so a
2953
+ * signing substrate that has lost track of an origin binding produces a
2954
+ * receipt nobody can tell apart from a correct one until the ledger is
2955
+ * consulted. The field set is therefore NOT closed, and the specification
2956
+ * has no way to say 'this is a re-drive' that would let the obligation be
2957
+ * expressed conditionally. Recorded as a gap.
2958
+ *
2959
+ * Same 128-bit `b64:` wire type as `nonce`, referenced from its single
2960
+ * definition; the WE-4 and L-17 rules written out on that field apply here
2961
+ * unchanged.
2962
+ */
2963
+ readonly originNonce?: Nonce128;
2964
+ }
2965
+ /**
2966
+ * The lenient projection of DecisionReceipt: every field optional, every enum
2967
+ * degraded to `string`. What a verifier parses. Never make a decision on
2968
+ * one — the fail-safe lookups live on the strict type only.
2969
+ */
2970
+ export interface RawDecisionReceipt {
2971
+ readonly receiptVersion?: number;
2972
+ readonly alg?: string;
2973
+ readonly decision?: string;
2974
+ readonly proposalHash?: string;
2975
+ readonly schemaId?: string;
2976
+ readonly schemaVersion?: string;
2977
+ readonly schemaHash?: string;
2978
+ readonly fidelity?: string;
2979
+ readonly policyBundleHash?: string;
2980
+ readonly bundleEpoch?: number;
2981
+ readonly contextSnapshotHash?: string;
2982
+ readonly ruleId?: string;
2983
+ readonly riskLevelEffective?: string;
2984
+ readonly riskLevelFloorOnly?: string;
2985
+ readonly attestationDigests?: readonly string[];
2986
+ readonly tenantId?: string;
2987
+ readonly operator?: string;
2988
+ readonly issuedAt?: string;
2989
+ readonly expiresAt?: string;
2990
+ readonly nonce?: string;
2991
+ readonly auditId?: string;
2992
+ readonly idempotencyKey?: string;
2993
+ readonly originNonce?: string;
2994
+ }
2995
+ /**
2996
+ * The out-of-band summary of a held floor-HIGH action, as it travels: a
2997
+ * signed Rendered Summary (`$defs/rendered_summary`, role `notification`)
2998
+ * beside its signature, delivered to every attester recorded in the verified
2999
+ * Attestation Objects and to the operator (DR-2, DR-3). Carried on legs F5.3
3000
+ * and F8.2 of the leg register (ZIFFER-DEPLOY-001 Annex A); F8.2 is the same
3001
+ * notification reaching the operator, whom DR-3 makes a required recipient.
3002
+ *
3003
+ * THIS SCHEMA IS THE ONE PERMITTED COMMON DEPENDENCY BETWEEN THE NOTIFIER
3004
+ * AND THE APPROVAL SERVICE, AND NOTHING ELSE IS. DR-2 requires the summary
3005
+ * to be rendered by a service that shares NO rendering code path with the
3006
+ * Attestation Presentation Service, and states the consequence in as many
3007
+ * words: a shared formatting library used by both is a conformance failure
3008
+ * EVEN WHEN it renders honestly from canonical bytes, because independence
3009
+ * has to hold under compromise. So a shared renderer, formatter, sanitiser,
3010
+ * date helper or component library between the two services is the
3011
+ * vulnerability, not a refactor. Two channels sourced from one renderer are
3012
+ * one channel wearing two hats: a single compromise of that renderer lies to
3013
+ * both simultaneously, and the approver's veto -- the mechanism deferred
3014
+ * release exists to provide -- disappears with no signature failing
3015
+ * anywhere. The wire format is the permitted dependency precisely because it
3016
+ * carries no rendering behaviour. `sim/notify/render.py` and
3017
+ * `sim/approve/render.py` are two files that do the same job differently on
3018
+ * purpose, and say so at the top.
3019
+ *
3020
+ * NONE OF THAT IS CHECKABLE HERE. DR-2's structural half is a property of
3021
+ * two code trees; JSON Schema constrains one message. Nothing below detects
3022
+ * a shared renderer, and this file must not be read as though it did.
3023
+ *
3024
+ * T-32 CLOSED HERE FOR TWO OF ITS THREE ROWS, AND THIS FILE SAYS HOW
3025
+ * (v1.3.25). Through v1.3.24 this object carried `source_path` and
3026
+ * `from_canonical` -- the notification service describing itself to the
3027
+ * Executor, classified T (rows 24-25 of ACP-CLASS-001) because a description
3028
+ * a service gives of itself is not evidence -- and an earlier revision of
3029
+ * this description recorded why it could be nothing more: the notification
3030
+ * was not among CR-1's signed structures, so there were no bytes to verify.
3031
+ * There are now. `obj` is a Rendered Summary signed under the `notification`
3032
+ * key that door_identities.json (PB-11) names in the signed bundle; the
3033
+ * Executor verifies the signature under that key and compares
3034
+ * `obj.proposal_hash` against its own canonical hash of the held Proposal
3035
+ * (DR-14). Both self-descriptions are WITHDRAWN, and the rows move T -> B.
3036
+ *
3037
+ * WHY THE OBJECT IS A `$defs` ENTRY HERE AND NOT ITS OWN FILE. Two producers
3038
+ * sign one shape -- the presentation service signs the approval summary, the
3039
+ * notifier signs this -- and the generator resolves a `$ref` to a `$defs`
3040
+ * entry only, never to a whole document (it halted on the alternative, which
3041
+ * is the right answer: a document is a message and a message is not a
3042
+ * field). The Acknowledgement carries its signed object the same way. The
3043
+ * approval summary has no wire schema of its own because no clause puts it
3044
+ * on a leg between two trust domains: it reaches the Executor from the
3045
+ * presentation service on the leg the Attestation Objects already travel, in
3046
+ * this shape.
3047
+ *
3048
+ * WHAT IS DELIBERATELY NOT A FIELD.
3049
+ * (a) THE RECIPIENT SET. DR-3's audience is every attester recorded in the
3050
+ * VERIFIED Attestation Objects plus the operator, both available to the
3051
+ * Executor from the receipt it has already verified -- the operator inside
3052
+ * the attestation signature rather than the receipt body (AT-8, Y4). A
3053
+ * transmitted recipient set would be the notification service certifying its
3054
+ * own coverage: RES-8, the shape DR-13(2) and PB-10 refused by putting
3055
+ * audiences in the signed bundle. Disclosed asymmetry, unchanged: DR-13(2)
3056
+ * puts the fast-path audience under signed policy and DR-3 does not, and
3057
+ * `reference/src/acp_executor.py` asks the notifier for it on this path.
3058
+ * (b) `delivered`. The notifier's report of whom it actually reached,
3059
+ * consumed at DR-8. A return value on the way back to the Executor, not a
3060
+ * field of this object, and the ONE T-32 row that stays T: a signed delivery
3061
+ * report is still the party under verification describing its own coverage,
3062
+ * and DR-14 states the bound rather than inventing a field that would look
3063
+ * like closing it.
3064
+ * (c) A THIRD SHAPE FOR THE DR-13 NOTICE. The register's artifact vocabulary
3065
+ * holds `notification` (DR-3) and `notice` (DR-13), and they are two
3066
+ * artifacts: the notice is committed by the Executor to its OWN durable
3067
+ * audit state before an IRREVERSIBLE action executes below floor-HIGH,
3068
+ * involves no rendering and no service, and its delivery is explicitly not a
3069
+ * precondition of execution. ACP-86's register names this schema for
3070
+ * `notice`; that is still not true of this file -- no tag discriminates the
3071
+ * two, and DR-13(1)'s content appears in no field here -- and the
3072
+ * disagreement is recorded rather than resolved, because resolving it is a
3073
+ * decision about the register.
3074
+ *
3075
+ * ENCODING. AT-8a pins canonical CBOR for the Attestation Object and §8 pins
3076
+ * JCS for the Proposal; DR-14 pins the signature as taken over the canonical
3077
+ * encoding of `obj` alone. The closed field sets here are Z4's reason -- an
3078
+ * optional field gives one object two valid encodings -- and not a rule
3079
+ * quoted from a clause.
3080
+ * Generated from notification.schema.json.
3081
+ */
3082
+ export interface Notification {
3083
+ /**
3084
+ * The signed Rendered Summary, `role` = `notification`. Carried in its own
3085
+ * member rather than flattened beside the signature because the signature is
3086
+ * taken over THIS map alone -- the Acknowledgement's shape, for the
3087
+ * Acknowledgement's reason (ACK-2).
3088
+ */
3089
+ readonly obj: RenderedSummary;
3090
+ /**
3091
+ * CR-2: a signature under a suite is a MAP from primitive name to signature
3092
+ * value, one entry per primitive of the suite named in `obj.alg`. A bare
3093
+ * scalar MUST NOT be accepted under any suite, not even a single-primitive
3094
+ * one -- format leniency is a downgrade in disguise. OPEN MAP, deliberately:
3095
+ * the suite registry lives in the signed bundle (CR-1) and CR-7 makes
3096
+ * migration forward-only, so a closed `{classical, pq}` object here would
3097
+ * hard-code one suite into the wire format. CR-3 is conjunctive and not
3098
+ * expressible here: every primitive of the declared suite MUST verify and
3099
+ * the supplied set MUST exactly match it, checked in code against `obj.alg`
3100
+ * resolved through the bundle. Value encoding follows
3101
+ * acknowledgement.schema.json's `sig`, whose description records that the
3102
+ * two implementations of it disagree (WE-4 base64 vs the reference's hex) --
3103
+ * one report, not two.
3104
+ *
3105
+ * Schema constraints, carried as documentation and NOT enforced by this
3106
+ * type: values minLength 1.
3107
+ */
3108
+ readonly sig: Readonly<Record<string, string>>;
3109
+ }
3110
+ /**
3111
+ * The lenient projection of Notification: every field optional, every enum
3112
+ * degraded to `string`. What a verifier parses. Never make a decision on
3113
+ * one — the fail-safe lookups live on the strict type only.
3114
+ */
3115
+ export interface RawNotification {
3116
+ readonly obj?: RawRenderedSummary;
3117
+ readonly sig?: Readonly<Record<string, string>>;
3118
+ }
3119
+ /**
3120
+ * The immutable `proposal_hash` to `origin_nonce` binding (CL-3, DS-6f,
3121
+ * T-22), claimed at the FIRST receipt for a Proposal. Unlike the three
3122
+ * classes above this is a BINDING and not a consumption: it is written once
3123
+ * and read many times, its retention is indefinite (CL-3), and a rebind
3124
+ * attempt is a critical alert.
3125
+ *
3126
+ * WHY IT EXISTS. DS-6b as drafted in v1.3.4 required the Executor to verify
3127
+ * a receipt-claimed `origin_nonce` was "recorded as consumed in the
3128
+ * Consumption Ledger". That is a MEMBERSHIP test: it proves the value is *a*
3129
+ * consumed nonce, never that it is *the* origin of this Proposal. A
3130
+ * compromised KMS therefore names any other consumed nonce as the origin,
3131
+ * the idempotency key moves, the target cannot dedup, and the floor-HIGH
3132
+ * action doubles -- Z3, the fourth recurrence of the RES-8 class, found
3133
+ * inside the fix for Y3.
3134
+ *
3135
+ * WHAT THIS CLAIM IS AND IS NOT. It PINS a value; it is not evidence that a
3136
+ * value was pinned. On a re-drive the Executor MUST read the origin from the
3137
+ * ledger and derive `idempotency_key` from what it read; a receipt-carried
3138
+ * `origin_nonce` is diagnostic only and must match or the Executor fails
3139
+ * closed with a critical alert (DS-6f). That read is a different operation
3140
+ * and this schema does not model it.
3141
+ *
3142
+ * CL-7 GOVERNS THE WRITE AND CANNOT BE SEEN FROM HERE. The binding MUST (i)
3143
+ * read from a reachable majority, (ii) fail closed if reachable replicas
3144
+ * disagree, never resolving a security value by majority vote, and (iii)
3145
+ * adopt an existing binding where one is found, writing only when none
3146
+ * exists. The converse ordering leaves a partitioned write permanently split
3147
+ * across the replica set: every later read sees two values and fails closed
3148
+ * forever, a durable denial of service on that Proposal produced by an
3149
+ * ordinary network event with no attacker. That defect was real, was found
3150
+ * by `partition_suite.py`, and is invisible to this document -- a claim that
3151
+ * validates here says nothing about the order in which the ledger read and
3152
+ * wrote.
3153
+ * Generated from ledger_claim.schema.json#/$defs/origin_binding_claim.
3154
+ */
3155
+ export interface OriginBindingClaim {
3156
+ readonly tenantId: TenantId;
3157
+ readonly artifactClass: 'origin_binding';
3158
+ /**
3159
+ * The `proposal_hash` (DS-6f), which is the KEY half of the binding and
3160
+ * therefore the ledger key's `identifier` position under CL-5. It is the
3161
+ * canonical Proposal's hash under RFC 8785 (JCS), §9.2 -- the Proposal
3162
+ * canonicalizes differently from the receipt and the Attestation Object
3163
+ * (canonical CBOR, WE-1/WE-2/AT-8a), and mixing the two is the encoding
3164
+ * split at the point it does the most damage.
3165
+ */
3166
+ readonly identifier: Sha256Hash;
3167
+ /**
3168
+ * The BOUND half: the receipt nonce of the first receipt issued for this
3169
+ * Proposal (DS-6f). Carried in the claim because the ledger cannot derive it
3170
+ * -- it is the value being pinned. It is not a second spelling of the
3171
+ * receipt-nonce claim's `identifier` and does not consume a slot in that
3172
+ * class; the two claims are distinct rows even where the value is
3173
+ * byte-identical, which is what `artifact_class` in the key is for.
3174
+ *
3175
+ * DS-6f requires this binding to be claimed ATOMICALLY "alongside the nonce
3176
+ * claim itself", and a schema of one message per class cannot express an
3177
+ * atomic pair. Whether the two travel as one message or two is unanswered by
3178
+ * the specification and is recorded as an open question against this schema;
3179
+ * the Python reference issues them as two separate calls four checklist
3180
+ * steps apart (§9.3 step 6 and step 10).
3181
+ */
3182
+ readonly originNonce: Nonce128;
3183
+ }
3184
+ /**
3185
+ * The lenient projection of OriginBindingClaim: every field optional, every enum
3186
+ * degraded to `string`. What a verifier parses. Never make a decision on
3187
+ * one — the fail-safe lookups live on the strict type only.
3188
+ */
3189
+ export interface RawOriginBindingClaim {
3190
+ readonly tenantId?: string;
3191
+ readonly artifactClass?: string;
3192
+ readonly identifier?: string;
3193
+ readonly originNonce?: string;
3194
+ }
3195
+ /**
3196
+ * One floor-HIGH action, verified and HELD between §9.3 and execution.
3197
+ * Committed by the Executor as S1 of the leg register (ZIFFER-DEPLOY-001
3198
+ * Annex A), which carries it among the Executor-local durable commits and
3199
+ * not among the legs. F5.1 was its identifier while the register read it as
3200
+ * a leg; ACP-86 retired that id, because an Executor-internal hold crosses
3201
+ * no domain boundary and DP-83 makes a leg a crossing.
3202
+ *
3203
+ * S1 IS AN EXECUTOR-INTERNAL HOLD, AND THAT CHANGES WHAT THIS SCHEMA IS FOR.
3204
+ * Every other artifact in this directory crosses between two parties. This
3205
+ * one does not: the register records it as a commit whose component column
3206
+ * reads "Executor" and which names no crossing, so nothing here is received,
3207
+ * and no field is class T in the sense §14 suite 12 means it -- a value
3208
+ * transmitted by the party under verification. Every value below was
3209
+ * verified by the Executor at §9.3, recomputed by it (TR-8, RV-3), or drawn
3210
+ * by it (DR-11); the one exception is `notified`, and it is called out on
3211
+ * the field. What the schema constrains is therefore DURABLE STATE, and that
3212
+ * state is normative because DP-48 makes it so: an Executor MUST record
3213
+ * entry into `pending_release` before the hold begins, and on recovery MUST
3214
+ * enumerate every held action, void each with a critical alert and an audit
3215
+ * record, and MUST NOT resume, re-time, or release any of them. A hold that
3216
+ * lives only in process memory is a hold an ordinary restart erases -- along
3217
+ * with a repudiation (DR-4) that has already arrived.
3218
+ *
3219
+ * WHY THE HOLD EXISTS AT ALL. §9.3 establishes that a receipt is authentic,
3220
+ * bound, fresh and quorum-satisfying. It cannot establish that the approvers
3221
+ * SAW the action they approved. DR-1 therefore forbids a floor-HIGH action
3222
+ * from executing at verification time and holds it while a summary rendered
3223
+ * independently from the canonical bytes is delivered out of band (DR-2,
3224
+ * DR-3) and any recipient may veto (DR-4). That raises the bar from one
3225
+ * compromised component to two independent ones. A-8 is not closed and is
3226
+ * not claimed to be.
3227
+ *
3228
+ * WHAT IS BOUND TO WHAT, WHICH IS THE POINT OF THE RECORD. A release must
3229
+ * not be re-pointable at a different action, so the record pins three things
3230
+ * and not one. `proposal_hash` names the action -- and it is a NAME, not
3231
+ * evidence: at release the Executor re-hashes the bytes it is about to
3232
+ * execute and compares (9.3-3, "the Executor hashes what it will execute --
3233
+ * never whatever the receipt describes"). `receipt_nonce` pins the ONE
3234
+ * receipt whose quorum authorized this hold, because a Proposal can have
3235
+ * more than one live receipt (DS-3 re-drives through a NEW attestation;
3236
+ * DS-6a fixes the action identity across them), and a hold keyed only on the
3237
+ * Proposal would let a later receipt inherit a hold -- and a veto -- raised
3238
+ * against an earlier one. `policy_bundle_hash` and `bundle_epoch` name the
3239
+ * signed policy the recomputations were made against, so `reversibility`,
3240
+ * which decides whether silence releases, is something an auditor can
3241
+ * reproduce rather than an assertion they must accept.
3242
+ *
3243
+ * THE FIELD SET IS CLOSED, AND THE REASON IS NOT AT-8b's. `required` lists
3244
+ * every property and `additionalProperties` is false, but Z4's argument does
3245
+ * not carry over unchanged: nothing hashes this record, so two encodings of
3246
+ * it do not become two ids and two ledger slots. What does carry over is the
3247
+ * other half of it. An optional field is a field the release path sometimes
3248
+ * reads as absent, the absent branch is the one nothing exercises, and here
3249
+ * that branch decides whether an IRREVERSIBLE action releases with no
3250
+ * acknowledgement. `acknowledged_by` and `repudiated_by` are therefore
3251
+ * REQUIRED and MAY be empty arrays: an empty array is the honest encoding of
3252
+ * "nobody did", where an omitted field is the same state spelled a second
3253
+ * way. And DP-48's recovery path has to enumerate these records after a
3254
+ * restart, which it cannot do faithfully for a record carrying a field it
3255
+ * does not know about.
3256
+ *
3257
+ * `x-acp-absent` APPEARS NOWHERE BELOW, DELIBERATELY. An absent rule answers
3258
+ * "what does the SIGNED POLICY say when a lookup misses", and this record is
3259
+ * not policy -- `tools/codegen.py` states that as a rule for the whole wire
3260
+ * root, because a default supplied by the party under verification is not a
3261
+ * fail-safe. RV-1's absent-from-`reversibility.json` default (IRREVERSIBLE)
3262
+ * lives at the lookup, in `spec/schemas/bundle/reversibility.schema.json`,
3263
+ * which is where it has effect. Restating it here would give one fail-safe
3264
+ * two homes.
3265
+ *
3266
+ * WHAT IS NOT A FIELD. (a) `release_at`: it is `held_at` plus
3267
+ * `hold_window_seconds`, and a stored instant that can disagree with its own
3268
+ * inputs is a second definition of one moment; the disagreement that matters
3269
+ * is the early one, which is DR-1's refusal bypassed by a value rather than
3270
+ * by a clock. (b) `release_mode`: DR-1 says it is determined by the
3271
+ * recomputed reversibility class and the sampling draw, so it IS
3272
+ * `reversibility` and `sampled`, computed at release and never stored --
3273
+ * stored, one flipped field converts a DR-9 hold into a DR-7 silent release.
3274
+ * RV-4 does require the mode recorded, but in AUDIT, a different artifact on
3275
+ * a different leg (F6.1). (c) `idempotency_key`: DS-6f pins `proposal_hash`
3276
+ * to `origin_nonce` immutably in the Consumption Ledger and requires the
3277
+ * Executor to READ origin from that binding and derive the key from what it
3278
+ * read; a key copied in here would be read at the target call instead, which
3279
+ * is Z3 one layer in -- the membership-versus-pinning distinction that let a
3280
+ * compromised KMS move an idempotency key and double a floor-HIGH action.
3281
+ * (d) A Context snapshot or a stored capability decision: 9.3 step 9 is a
3282
+ * RE-QUERY written to close the issuance-to-execution window (T-10), a
3283
+ * capability read before the hold is stale by the whole window, and carrying
3284
+ * one would invite exactly the read that step forbids. (e) The Attestation
3285
+ * Objects, their signatures, and their `attestation_id`s: the quorum was
3286
+ * decided at 9.3 step 7b and the ids were consumed in the ledger (CL-3,
3287
+ * AT-5), and re-deciding it at release against a copy this record supplied
3288
+ * is a verification with no fresh binding. The attester IDENTITIES are kept,
3289
+ * because DR-3 and DR-5 need to know who must be notified and who may veto.
3290
+ * (f) The canonical Proposal bytes: where an Executor keeps what it is about
3291
+ * to execute is a storage question the specification does not answer; what
3292
+ * it does answer is that the hash of those bytes is recomputed and compared
3293
+ * (9.3-3).
3294
+ *
3295
+ * WHAT THIS SCHEMA DOES NOT CONSTRAIN. Almost everything that matters about
3296
+ * a hold is a relation -- between fields, between times, or between
3297
+ * components -- and JSON Schema expresses none of them: that the window has
3298
+ * elapsed (DR-1), that the receipt has not expired during it (DR-6), that
3299
+ * `notified` covers every attester and the operator (DR-3, DR-8), that an
3300
+ * acknowledgement came from a notified non-operator (DR-9, DR-5), that a
3301
+ * non-empty `repudiated_by` is terminal (DR-4), that `held_at` plus
3302
+ * `hold_window_seconds` still leaves a processing margin inside the
3303
+ * receipt's validity (DR-6). A record can be schema-valid and unreleasable,
3304
+ * and a record can be schema-valid and MUST NOT release. This document
3305
+ * constrains shape, and shape is not authority.
3306
+ *
3307
+ * Two limits stated rather than left to be assumed. DR-2's render-path
3308
+ * distinctness is a property of two CODE PATHS and cannot be a property of
3309
+ * any object; nothing here can carry it, and the notifier self-certifying
3310
+ * its own independence and coverage is the OPEN finding T-32 in
3311
+ * ACP-CLASS-001. DR-11's requirement that the sampling draw come from a
3312
+ * CSPRNG and be uninfluenceable by the requester is likewise
3313
+ * unrepresentable: `sampled` records the OUTCOME of a draw and can say
3314
+ * nothing about its source, so an implementation whose draw an attacker
3315
+ * grinds against produces records that validate perfectly.
3316
+ *
3317
+ * Finally, this record carries no signature and no integrity envelope,
3318
+ * because the specification gives it none: its integrity is the Executor's
3319
+ * own durable store. The closed field set above is a discipline about what
3320
+ * the release path may read. It is not a defence against an Executor that
3321
+ * writes whatever it likes.
3322
+ * Generated from pending_release.schema.json.
3323
+ */
3324
+ export interface PendingRelease {
3325
+ /**
3326
+ * The canonical Proposal (JCS, WE-1) this action was authorized for. It is
3327
+ * the name of the held action and not evidence of it: 9.3-3 requires the
3328
+ * Executor to hash what it will execute and compare, and the hold is exactly
3329
+ * the interval during which the thing to be executed and the thing that was
3330
+ * approved can drift apart. Under DS-6f this is also the key of the
3331
+ * immutable `proposal_hash` to `origin_nonce` ledger binding, which is where
3332
+ * the idempotency key comes from at the target call -- not from anything
3333
+ * recorded here.
3334
+ */
3335
+ readonly proposalHash: Sha256Hash;
3336
+ /**
3337
+ * The `nonce` of the ONE receipt whose quorum authorized this hold, claimed
3338
+ * in the Consumption Ledger at 9.3 step 6.
3339
+ *
3340
+ * WHY THE RECORD NEEDS IT AND `proposal_hash` IS NOT ENOUGH. DS-3 re-drives
3341
+ * an `indeterminate` outcome through a NEW attestation and therefore a new
3342
+ * receipt, and DS-6a fixes the action identity across re-drives, so one
3343
+ * Proposal can have several receipts alive inside a retention window. A hold
3344
+ * identified only by its Proposal is a hold any of them can be released
3345
+ * against -- including one whose attesters were never notified under DR-3,
3346
+ * which detaches the DR-4 veto from the authorization it was raised against.
3347
+ *
3348
+ * THE TYPE IS TWO RULES AND THEY REFUSE UNDER DIFFERENT NAMES. `b64:` plus
3349
+ * the RFC 4648 §4 alphabet WITH padding is WE-4, the wire type, and the
3350
+ * prefix is part of the value: it is carried, hashed and signed as that
3351
+ * string, and stripping it, dropping the padding, or substituting the
3352
+ * URL-safe alphabet each produce a different value and MUST be rejected
3353
+ * rather than normalized. The twenty-two characters are L-17's 128 bits. A
3354
+ * verifier folding the two into one check answers `WE-4` for a 64-bit nonce,
3355
+ * which is a perfectly well-formed `b64:` value -- the wrong clause name,
3356
+ * and the cross-language differential compares names.
3357
+ * `tools/nonce-type-vectors.json` pins that pair for the ATTESTATION nonce
3358
+ * across three implementations after they had already diverged for a release
3359
+ * with every gate green; the receipt nonce has the same two rules and no
3360
+ * such corpus.
3361
+ */
3362
+ readonly receiptNonce: Nonce128;
3363
+ /**
3364
+ * The verified receipt's `expires_at`, copied at 9.3 step 5 so DR-6 can be
3365
+ * evaluated at release without re-reading the receipt. DR-6: release MUST
3366
+ * fail closed if the receipt expired during the window, because a hold that
3367
+ * outlives its authorization executes on an expired receipt, and L-14's 120
3368
+ * s ceiling exists to bound exactly that. Note what this value's authority
3369
+ * rests on and what it does not: step 5 enforced the window LENGTH itself
3370
+ * rather than reading it from the KMS-written value (Y2), and nothing
3371
+ * re-derives it here -- this is a copy of a checked value, kept because the
3372
+ * checker and the release step are separated by the hold.
3373
+ */
3374
+ readonly receiptExpiresAt: Rfc3339Instant;
3375
+ /**
3376
+ * The signed bundle established at 9.3 step 4, under which the values below
3377
+ * were recomputed. It authorizes nothing at release -- no clause reads
3378
+ * policy through this hash -- and it is here because a recomputation with no
3379
+ * named input is not reproducible: RV-4 requires the recomputed
3380
+ * reversibility class recorded in audit, and an auditor who cannot name the
3381
+ * bundle cannot check the class or the release mode it selected.
3382
+ */
3383
+ readonly policyBundleHash: Sha256Hash;
3384
+ /**
3385
+ * Epoch of that bundle (9.3 step 4, which requires it be at least the
3386
+ * Executor's last-seen epoch). Monotonicity is enforced at the KMS against a
3387
+ * durable high-water mark (RAD-3) and, across a restart, against the
3388
+ * Executor's own recovered mark (DP-47) -- never from this value. It is
3389
+ * recorded so that a hold raised under epoch N is still visibly a hold
3390
+ * raised under epoch N after a rotation, which is the only way to read a
3391
+ * release decision made under a bundle that is no longer current.
3392
+ *
3393
+ * Schema constraints, carried as documentation and NOT enforced by this
3394
+ * type: minimum 0.
3395
+ */
3396
+ readonly bundleEpoch: number;
3397
+ /**
3398
+ * The tenant scope verified at 9.3 step 8, where the receipt's tenant must
3399
+ * match the tenant scope of the credentials about to be used. Three
3400
+ * obligations need it at release rather than at verification: DR-12 counts
3401
+ * silent releases in a PER-TENANT counter, DR-13 counts its below-HIGH
3402
+ * notices as a separate class in the same discipline, and AU-2 makes
3403
+ * `(tenant_id, seq)` unique and gapless in the audit chain. A release that
3404
+ * cannot name its tenant cannot be counted and cannot be anchored.
3405
+ */
3406
+ readonly tenantId: TenantId;
3407
+ /**
3408
+ * The operator identity taken from the VERIFIED Attestation Objects at 9.3
3409
+ * step 7b(iii-a) -- never from the receipt body's `operator`, which is
3410
+ * diagnostic only (Y4). Three rules read it during the hold. DR-3 makes the
3411
+ * operator a required notification recipient alongside every attester. DR-9
3412
+ * refuses an acknowledgement from the operator: the party who initiated the
3413
+ * action carries no independent information about whether the displayed
3414
+ * summary was honest, which is AT-2's approver-distinctness restated at the
3415
+ * release step. And 9.3 step 9 rechecks capability on this identity, which
3416
+ * is why it must be the signature-covered one.
3417
+ */
3418
+ readonly operator: OperatorId;
3419
+ /**
3420
+ * Every attester recorded in the verified Attestation Objects. DR-3 requires
3421
+ * the notification to reach each of them and the operator, over a channel
3422
+ * distinct from the one that obtained approval; DR-5 accepts a repudiation
3423
+ * or an acknowledgement only from a party recorded as notified.
3424
+ *
3425
+ * IT IS KEPT AS THE RECOMPUTED HALF OF THAT PAIR. The set of parties who
3426
+ * MUST be notified is derived from signatures the Executor itself verified,
3427
+ * so DR-8's completeness test can compare `notified` against this rather
3428
+ * than asking the notification service who its audience should have been.
3429
+ * Asking the notifier is the shape of T-32, still OPEN in ACP-CLASS-001: a
3430
+ * service that selects and then certifies its own coverage is verifying
3431
+ * itself.
3432
+ *
3433
+ * `minItems` is 2 because L-16 puts the floor-HIGH quorum at two distinct
3434
+ * attesters, each distinct from the operator, and a floor-HIGH receipt with
3435
+ * no attestations fails closed at INV-1-HIGH before any hold exists.
3436
+ * `uniqueItems` compares identity STRINGS, which is deliberately the weaker
3437
+ * check: AT-2 distinctness is resolved over registry KEYS and not over names
3438
+ * (PB-7, where changing a `role` string let one key holder satisfy a k=2
3439
+ * quorum alone), and that resolution happened at 9.3 step 7b(vi). This
3440
+ * constraint restates none of it.
3441
+ *
3442
+ * Schema constraints, carried as documentation and NOT enforced by this
3443
+ * type: minItems 2, uniqueItems; items minLength 1, maxLength 128.
3444
+ */
3445
+ readonly attesters: readonly string[];
3446
+ /**
3447
+ * The recomputed floor-only risk that put this action on the deferred path.
3448
+ * The `const` is DR-1's scope made checkable: DR-1 defers an action whose
3449
+ * RECOMPUTED floor-only risk is HIGH, and DR-13 states in as many words that
3450
+ * DR-1 through DR-12 govern that path only -- an irreversible action below
3451
+ * HIGH takes the notice path instead, which is a different artifact on a
3452
+ * different path -- the Executor-local commit S2, whose leg id F5.6 ACP-86
3453
+ * retired for the same reason as F5.1's. A `pending_release` record graded
3454
+ * anything else is a record that should not exist.
3455
+ *
3456
+ * IT IS HERE TO BE REFUSED ON, NOT RELIED ON. TR-8 admits one source for a
3457
+ * risk grade: recomputation from the signed bundle's floors and risk
3458
+ * functions applied to the independently received canonical Proposal,
3459
+ * ignoring Context-Store raises and ignoring any transmitted value. Nothing
3460
+ * at release may read this field as the grade. Reading a stored grade
3461
+ * instead of recomputing is X1 -- a forged `risk_level_floor_only`
3462
+ * suppressing attestation -- with the storage moved inside the verifier.
3463
+ */
3464
+ readonly floorOnlyRisk: Risk;
3465
+ /**
3466
+ * The class RECOMPUTED at verification from the trusted bundle and the
3467
+ * Proposal's `task_type` (RV-3), never read from the receipt: a
3468
+ * receipt-asserted `REVERSIBLE` would suppress the DR-9 acknowledgement
3469
+ * requirement exactly as v1.3.0's forged `risk_level_floor_only` suppressed
3470
+ * attestation, same defect class and a different field. RV-2 puts the class
3471
+ * in the signed bundle only -- no Context raise, no runtime override, no
3472
+ * receipt field of record -- and makes downgrading one as dangerous a policy
3473
+ * operation as lowering a tier floor (RK-5).
3474
+ *
3475
+ * With `sampled` it decides the release mode (DR-1), and the two outcomes
3476
+ * are not variations on one control. `IRREVERSIBLE` requires positive
3477
+ * acknowledgement from a notified non-operator and TIMES OUT CLOSED (DR-9).
3478
+ * `REVERSIBLE` releases on silence, and only there, because a control whose
3479
+ * default outcome equals its approved outcome teaches its own users to
3480
+ * ignore it and decays into a log entry while the dashboard still reports
3481
+ * that human oversight exists (DR-7).
3482
+ *
3483
+ * RV-1's absent-from-`reversibility.json` default is IRREVERSIBLE, and it
3484
+ * belongs to the LOOKUP, not to this field: it is carried by
3485
+ * `spec/schemas/bundle/reversibility.schema.json`'s `x-acp-absent` and
3486
+ * generated into the table's accessor. This field is required and has no
3487
+ * default.
3488
+ *
3489
+ * IT IS THE BUNDLE'S TYPE, NOT A SECOND ONE. This field `$ref`s
3490
+ * `spec/schemas/bundle/reversibility.schema.json#/$defs/reversibility`
3491
+ * across roots. It was declared twice until ACP-78 M1 --
3492
+ * `ReversibilityClass` here beside the bundle's `Reversibility` -- on the
3493
+ * argument that signed policy and a recorded value are never interchangeable
3494
+ * (RES-8) and the module a reader holds should say which they have. That
3495
+ * argument is right about the VALUE and wrong about the TYPE: what carries
3496
+ * the provenance is the object the value sits in, a signed
3497
+ * `reversibility.json` against a transmitted record, and a two-case
3498
+ * vocabulary duplicated is two definitions of one object. RV-3 also has to
3499
+ * COMPARE the recomputed class against the table it came from, and a
3500
+ * comparison between two enums that merely look alike is the CR-4 defect one
3501
+ * artifact down.
3502
+ */
3503
+ readonly reversibility: Reversibility;
3504
+ /**
3505
+ * The DR-10 confirmation-sampling draw for this action. A bundle-configured
3506
+ * fraction of REVERSIBLE floor-HIGH actions (L-29, default 10%) inherits
3507
+ * DR-9's acknowledgement duty, for a reason about people rather than
3508
+ * cryptography: if recipients learn that reversible actions never require a
3509
+ * response they stop reading those notifications too, and the channel decays
3510
+ * for the class that is most of the traffic.
3511
+ *
3512
+ * DR-11 governs the draw and none of it is visible here. It MUST come from a
3513
+ * cryptographically secure source at the Executor or an equivalently
3514
+ * isolated component; it MUST NOT be derived from the Proposal, the operator
3515
+ * identity, or any requester-supplied value, because a derived draw is one
3516
+ * an attacker grinds inputs against until the action falls outside the
3517
+ * sample; and it MUST be recorded so the realised rate is verifiable after
3518
+ * the fact. This field is that record, and it is the OUTCOME only.
3519
+ *
3520
+ * Because it is REQUIRED, the draw is made no later than the durable commit
3521
+ * -- earlier than DR-10 demands, and out of reach of anything that happens
3522
+ * during the hold. DR-10 scopes sampling to REVERSIBLE actions and the
3523
+ * specification does not say what the value means for an IRREVERSIBLE one,
3524
+ * where DR-9 already requires acknowledgement; there both `true` and `false`
3525
+ * describe the same release mode.
3526
+ */
3527
+ readonly sampled: boolean;
3528
+ /**
3529
+ * RFC 3339 instant at which the action entered `pending_release` and the
3530
+ * window started (DR-1). DP-48 makes it the moment the DURABLE record must
3531
+ * already exist by -- "before the hold begins" is the whole of that clause's
3532
+ * ordering, and it is AU-7's anchor-before-release rule applied to the
3533
+ * Executor's own state, for the same reason: a record written after the fact
3534
+ * can be suppressed by whatever the action enabled.
3535
+ *
3536
+ * Evaluated against NTS-disciplined wall time within the L-20 skew allowance
3537
+ * of five seconds, never against a monotonic clock. Leg F9.1 carries that
3538
+ * dependency and DP-52 states the distinction. It matters more here than on
3539
+ * a receipt: DR-6's margin between the end of the hold and the expiry of the
3540
+ * receipt is measured across two components' clocks.
3541
+ */
3542
+ readonly heldAt: Rfc3339Instant;
3543
+ /**
3544
+ * The window this action must survive before it may release, read from the
3545
+ * SIGNED BUNDLE in force at 9.3 step 4 (DR-1; L-28: default 60 s, minimum 30
3546
+ * s). Release before it has elapsed MUST fail closed.
3547
+ *
3548
+ * THE UPPER BOUND IS A REFUSAL, NOT A STYLE CHOICE. L-14 caps receipt
3549
+ * validity at 120 s, so DR-6 makes a window at or above that ceiling an
3550
+ * UNSATISFIABLE configuration -- no legal receipt can outlive it -- which
3551
+ * MUST be rejected rather than silently never releasing. Testing found the
3552
+ * interaction directly: a 60 s hold inside a 60 s receipt window expires
3553
+ * mid-hold and never releases the honest path. `maximum` is inclusive and
3554
+ * this repository's generator carries no exclusive bound, so 119 is
3555
+ * "strictly below the ceiling" written in the only keyword available. It is
3556
+ * the loosest value the specification permits and emphatically not a safe
3557
+ * one: DR-6's actual requirement is `hold_window + processing margin <
3558
+ * receipt validity`, the margin is a property of the deployment, and no
3559
+ * schema can check it.
3560
+ *
3561
+ * Two choices here are this schema's and not the specification's, and both
3562
+ * are recorded rather than hidden. L-28 states the window in seconds but
3563
+ * nowhere states its granularity; integer seconds is a decision. And pinning
3564
+ * the window IN the record rather than re-reading it from the bundle at
3565
+ * release is a decision too -- it stops a rotation during the hold from
3566
+ * retroactively lengthening or shortening a window the attesters were
3567
+ * notified about -- on a question §9.6 does not answer.
3568
+ *
3569
+ * Schema constraints, carried as documentation and NOT enforced by this
3570
+ * type: minimum 30, maximum 119.
3571
+ */
3572
+ readonly holdWindowSeconds: number;
3573
+ /**
3574
+ * The parties the notification service reported delivering to. DR-3 requires
3575
+ * that to be every attester in the verified Attestation Objects and the
3576
+ * operator, over a channel distinct from the one used to obtain approval.
3577
+ * Two clauses then read this set: DR-8, where an unreachable recipient or
3578
+ * any failed delivery means the action MUST NOT release, because executing
3579
+ * while the detection channel is absent is executing unwatched; and DR-5,
3580
+ * where an acknowledgement or a repudiation is accepted ONLY from a party
3581
+ * recorded here, or a compromised presentation service suppresses the veto
3582
+ * the mechanism exists to enable.
3583
+ *
3584
+ * THIS IS THE ONE VALUE IN THE RECORD THE EXECUTOR NEITHER VERIFIED NOR
3585
+ * RECOMPUTED. It is the notifier's claim about its own coverage, which is
3586
+ * RES-8's shape and the OPEN finding T-32 in ACP-CLASS-001, where
3587
+ * `delivered` is classified T against a disclosed residual. `attesters` sits
3588
+ * beside it so DR-8's completeness test compares this claim against a set
3589
+ * derived from verified signatures instead of against the notifier's own
3590
+ * answer about who its audience should have been.
3591
+ *
3592
+ * MAY BE EMPTY, and the empty case is not slack. DP-48 requires the durable
3593
+ * record to exist BEFORE the hold begins while DR-3's delivery happens
3594
+ * DURING it, so a committed, not-yet-delivered record is a state the
3595
+ * specification's own ordering produces. What may be done with such a record
3596
+ * -- how long it may sit undelivered, whether an undelivered hold expires or
3597
+ * is voided -- §9.6 does not say.
3598
+ *
3599
+ * Schema constraints, carried as documentation and NOT enforced by this
3600
+ * type: uniqueItems; items minLength 1, maxLength 128.
3601
+ */
3602
+ readonly notified: readonly string[];
3603
+ /**
3604
+ * Notified parties that positively acknowledged during the window. Where the
3605
+ * recomputed reversibility class is IRREVERSIBLE, DR-9 requires at least one
3606
+ * such acknowledgement before release and TIMES OUT CLOSED -- a timed-out
3607
+ * action needs a fresh quorum, never a replay. DR-10 gives sampled
3608
+ * REVERSIBLE actions the same duty. The acknowledgement MUST NOT come from
3609
+ * the operator (DR-9), which is why `operator` is carried in this record
3610
+ * rather than looked up later.
3611
+ *
3612
+ * Empty is the ordinary state on the DR-7 silent path, and it is not free:
3613
+ * DR-12 requires every release with an empty set here to increment a
3614
+ * per-tenant counter recorded in audit, so that silence is a MEASURABLE DEBT
3615
+ * rather than an invisible acknowledgement, and deployments SHOULD alert on
3616
+ * the rate rather than merely record it.
3617
+ *
3618
+ * WHAT AN IDENTITY IN THIS ARRAY IS NOT. The reference-level ACK-1..ACK-6
3619
+ * rules make each acknowledgement a signed, closed-schema, single-use object
3620
+ * bound to `proposal_hash` and verified against a registered key (leg F5.4).
3621
+ * This field records only the identities that survived those checks. An
3622
+ * identity is not evidence of the object it came from, and nothing
3623
+ * downstream may treat it as one.
3624
+ *
3625
+ * Schema constraints, carried as documentation and NOT enforced by this
3626
+ * type: uniqueItems; items minLength 1, maxLength 128.
3627
+ */
3628
+ readonly acknowledgedBy: readonly string[];
3629
+ /**
3630
+ * Notified parties that repudiated during the window (DR-4, DR-5). Any
3631
+ * notified party MAY repudiate; repudiation MUST fail closed with a critical
3632
+ * alert; consumed attestations remain consumed, so a repudiated action
3633
+ * requires a fresh quorum and never a replay. A non-empty array is therefore
3634
+ * TERMINAL for this record: no later acknowledgement and no elapsed window
3635
+ * releases it.
3636
+ *
3637
+ * An array rather than a nullable identity, because `null` and an omitted
3638
+ * field are one state spelled two ways -- Z4's argument at the record layer,
3639
+ * where the cost is an unexercised branch in the release path rather than a
3640
+ * second ledger slot, and where the branch decides whether a veto is seen.
3641
+ * The empty array means nobody repudiated, and it means it in exactly one
3642
+ * way.
3643
+ *
3644
+ * Schema constraints, carried as documentation and NOT enforced by this
3645
+ * type: uniqueItems; items minLength 1, maxLength 128.
3646
+ */
3647
+ readonly repudiatedBy: readonly string[];
3648
+ }
3649
+ /**
3650
+ * The lenient projection of PendingRelease: every field optional, every enum
3651
+ * degraded to `string`. What a verifier parses. Never make a decision on
3652
+ * one — the fail-safe lookups live on the strict type only.
3653
+ */
3654
+ export interface RawPendingRelease {
3655
+ readonly proposalHash?: string;
3656
+ readonly receiptNonce?: string;
3657
+ readonly receiptExpiresAt?: string;
3658
+ readonly policyBundleHash?: string;
3659
+ readonly bundleEpoch?: number;
3660
+ readonly tenantId?: string;
3661
+ readonly operator?: string;
3662
+ readonly attesters?: readonly string[];
3663
+ readonly floorOnlyRisk?: string;
3664
+ readonly reversibility?: string;
3665
+ readonly sampled?: boolean;
3666
+ readonly heldAt?: string;
3667
+ readonly holdWindowSeconds?: number;
3668
+ readonly notified?: readonly string[];
3669
+ readonly acknowledgedBy?: readonly string[];
3670
+ readonly repudiatedBy?: readonly string[];
3671
+ }
3672
+ /**
3673
+ * The typed candidate operation the Policy Engine decides over (§3), and the
3674
+ * hash preimage every other artifact in this system binds to. Carried on
3675
+ * legs F1.3, F4.1, F4.2 and F5.2 of the leg register (ZIFFER-DEPLOY-001
3676
+ * Annex A).
3677
+ *
3678
+ * ONE SCHEMA, NOT TWO, AND THE CANONICAL FLAG IS AN ENCODING RULE RATHER
3679
+ * THAN A PROPERTY. The closed artifact vocabulary carries two names for this
3680
+ * object -- `Proposal` (§3) and `canonical Proposal` (B-1a) -- and they are
3681
+ * one field set. `canonical` names an ENCODING of these bytes, not a second
3682
+ * artifact with a second shape: B-1a requires the Proposal reaching the
3683
+ * Executor to be `canonicalized identically before hashing`, and identical
3684
+ * canonicalization of two different field sets is not a thing that can be
3685
+ * asked for. A `canonical_proposal.schema.json` beside this file would put
3686
+ * two definitions on one object, which is the defect this repository has
3687
+ * published corrections for three times. The flag is therefore stated once,
3688
+ * here, in the ENCODING paragraph below -- and NOT as a property. A field
3689
+ * asserting `I am canonical` would be the party under verification
3690
+ * certifying its own encoding; B-1a's obligation is discharged by the
3691
+ * receiver hashing what it actually received (9.3-3), never by reading a
3692
+ * claim about it.
3693
+ *
3694
+ * ENCODING: JCS, AND IT MUST NOT BE MIXED WITH THE CBOR RULE. §9.2 states
3695
+ * the split in one sentence -- RFC 8785 (JCS) for the Proposal, canonical
3696
+ * CBOR (RFC 8949 §4.2) for the receipt and the Attestation Object
3697
+ * (WE-1/WE-2, AT-8a). AT-8a writes the second half as a prohibition (`JCS
3698
+ * MUST NOT be used for the Attestation Object`) and this schema writes the
3699
+ * first half the same way: canonical CBOR MUST NOT be used for the Proposal.
3700
+ * Mixing them is the encoding split at the point it does the most damage,
3701
+ * because `proposal_hash` is not one artifact's identifier -- it is the
3702
+ * binding EVERY other artifact carries. AT-1 puts it in the object the
3703
+ * humans sign; 9.3-3 makes the Executor recompute it over what it will
3704
+ * actually execute; DS-6a builds `action_id` from it and DS-6f pins an
3705
+ * IMMUTABLE `proposal_hash -> origin_nonce` binding in the Consumption
3706
+ * Ledger, retained INDEFINITELY because an expiring one would reopen Z3
3707
+ * (CL-3, CL-4); DR-13 writes it into the notice. Two encoders therefore do
3708
+ * not merely disagree about a digest. They give ONE action two identities.
3709
+ * The quorum the humans raised binds to a hash the Executor never computes,
3710
+ * so 9.3-3 fails closed and at the verifier that is indistinguishable from a
3711
+ * forgery -- the sentence WE-4's rationale already had to write once, for a
3712
+ * type nobody had written down. And a re-drive re-encoded by the second
3713
+ * encoder presents a `proposal_hash` DS-6f holds no binding for, so the
3714
+ * recovery path DS-3 mandates fails closed on an authorized action; an
3715
+ * implementation that instead BINDS the new hash has silently given one
3716
+ * action two action identities and two DS-6b idempotency keys, which is the
3717
+ * doubled trade DS-4 names as the worst failure available. Y5.2/AT-8a is
3718
+ * that hazard one artifact over, against a binding kept for 24 hours; this
3719
+ * one is kept forever.
3720
+ *
3721
+ * THE FIELD SET IS PR-1's (v1.3.27), AND THIS SCHEMA IS ITS RENDERING.
3722
+ * Through v1.3.26 ZIFFER-SPEC-001 STATED NO FIELD SET FOR THE PROPOSAL: §3
3723
+ * defined it in one sentence, SR-5 required the `(schema_id, schema_version,
3724
+ * schema_hash)` triple and the fidelity class, B-6 forbade the client to set
3725
+ * the stamp, V-11 spoke of an `envelope` and its `payload` and put
3726
+ * `task_type` inside the payload -- and `reference/src/acp_executor.py` read
3727
+ * a THIRD shape, flat, with `tenant_id`, `targets`, `params` and `cidrs` at
3728
+ * top level and no `schema_version`, `schema_hash` or `fidelity` at all.
3729
+ * Three shapes, one artifact, and `proposal_hash` was a hash over whichever
3730
+ * one an implementer chose. This file reported that choosing between them
3731
+ * was a normative act and refused to make it. v1.3.27 made it, after the
3732
+ * first end-to-end build (M7) put the two shapes on one wire: the door
3733
+ * emitted this envelope, the verifier graded the flat object, §9.3 step 8
3734
+ * looked for a `tenant_id` the envelope could not carry, and neither side
3735
+ * could be called wrong. PR-1 fixes the envelope -- the triple, the fidelity
3736
+ * class, the tenant the admitting adapter's VERIFIED bundle names, and a
3737
+ * CLOSED payload of `task_type`, `operator`, `targets`, `params` and `cidrs`
3738
+ * -- as the one Proposal, the one hash preimage, and the one object §8.3.1's
3739
+ * typing environment is derived from. The flat shape is gone: the reference
3740
+ * reads the envelope through one accessor, `acp_executor.payload()`, and the
3741
+ * Rust verifier through `acp_decision::Proposal::from_wire`, so a second
3742
+ * reading has nowhere to live.
3743
+ *
3744
+ * WHY BOTH LEVELS ARE CLOSED, AND WHAT THAT REFUSES. `additionalProperties`
3745
+ * is false at the root AND at the payload, and every property at both levels
3746
+ * is REQUIRED -- `params` and `cidrs` may be EMPTY objects, never absent. An
3747
+ * optional field means one object has two valid encodings and therefore two
3748
+ * `proposal_hash` values -- Z4, which AT-8b closes for the Attestation
3749
+ * Object by making its field set closed and every field required. Until
3750
+ * v1.3.27 no clause did the same for the Proposal, although it is hashed,
3751
+ * bound and ledger-keyed by the same machinery and its DS-6f binding is the
3752
+ * one retained indefinitely; PR-1 now states AT-8b's rule for it in as many
3753
+ * words, and this schema is where the rule is machine-readable. One
3754
+ * consequence is disclosed rather than resolved: §7.4 permits a deployment
3755
+ * to insert a domain-screening stage whose verdict `enters the Proposal as a
3756
+ * field policy can rule on`. V-1 forbids unknown fields on the registered
3757
+ * Input Schema that governs `params`, and PR-1 closes the payload around it,
3758
+ * so that verdict has no key to live under. The specification names the
3759
+ * field, does not name its key, does not type it, and does not say whether
3760
+ * it is inside the hash preimage. A deployment that inserts a screen is
3761
+ * outside this schema and needs a version increment, on AT-8b's reasoning
3762
+ * applied here.
3763
+ *
3764
+ * WHAT IS NOT A FIELD. `proposal_hash` is DERIVED, not carried, and has no
3765
+ * property below. It is SHA-256 over the canonical encoding of these bytes
3766
+ * (§9.2), and §9.3 step 3 makes the Executor hash `the Proposal the Executor
3767
+ * independently received and canonicalized` -- `never whatever the receipt
3768
+ * describes`. A Proposal carrying its own hash invites a reader to compare a
3769
+ * transmitted value against a transmitted value, which is TR-10 exactly: a
3770
+ * transmitted identifier is a NAME for a binding, not evidence of one. Also
3771
+ * absent, each deliberately: `risk_level` and `reversibility`, recomputed by
3772
+ * the Executor from the signed bundle and these bytes (TR-8, RV-3) because a
3773
+ * receipt-asserted risk is X1 and a receipt-asserted reversibility is the
3774
+ * same defect with a new field; and `origin_nonce` and `idempotency_key`,
3775
+ * pinned by the ledger with any receipt-carried claim diagnostic only
3776
+ * (DS-6f, Z3). `operator` IS a field since PR-1 -- it is what §8.4 step 4
3777
+ * checks capability for -- and it is still not authority at floor-HIGH,
3778
+ * where the value the Executor acts on is `attestations[].obj.operator`
3779
+ * inside the attester signature (Y4, §9.3 step 7b(iii-a)).
3780
+ *
3781
+ * SUITE 12 CLASSIFICATION. Every property below is class T at the schema
3782
+ * layer: this document constrains SHAPE, and shape is not authority. What
3783
+ * discharges each one lives elsewhere and is cited on the property rather
3784
+ * than restated (DP-84). The triple is a NAME for a binding until §8.4 step
3785
+ * 1 verifies it against the SIGNED registry (TR-10, SR-2). `fidelity` is
3786
+ * RECOMPUTED from the bundle-registered adapter binding for `schema_id`
3787
+ * (TR-8, §9.3 step 7) and is never read as authority from here,
3788
+ * notwithstanding that B-6 requires it to be carried. `tenant_id` is stamped
3789
+ * by the adapter from the bundle it VERIFIED (PR-2) -- B at the door -- and
3790
+ * is T on the wire until §9.3 step 8 compares it against the receipt's and
3791
+ * against the bundle the Executor trusts, at which point a receipt, a
3792
+ * Proposal and a manifest that all name one tenant is what the step
3793
+ * establishes. `operator` below floor-HIGH is the disclosed residual the
3794
+ * receipt's `operator` already was (ACP-CLASS-001); at floor-HIGH it is B
3795
+ * through Y4.
3796
+ * Generated from proposal.schema.json.
3797
+ */
3798
+ export interface Proposal {
3799
+ /**
3800
+ * Names the registered schema the payload was validated against. Schemas are
3801
+ * content-addressed by `schema_id` + `schema_version` + `schema_hash` (SR-1)
3802
+ * and SR-5 requires that triple on every Proposal. It is also the key the
3803
+ * Executor uses to look up the bundle-registered ADAPTER binding when it
3804
+ * recomputes `fidelity` (§9.3 step 7), so a Proposal without it leaves TR-8
3805
+ * with nothing to recompute against and the FC-2 confirmation requirement
3806
+ * resting on a transmitted stamp.
3807
+ *
3808
+ * THE PATTERN IS §6.3's, CHARACTER FOR CHARACTER, AND THE REFERENCE DOES NOT
3809
+ * SATISFY IT. `reference/suites/conformance.py` builds `schema_id: "fw.v1"`,
3810
+ * and `.` is outside that class. The pattern is kept as written: widening a
3811
+ * schema's own pattern to admit the fixture that violates it proves only
3812
+ * that the instrument is adjustable. Whether a pattern stated inside a
3813
+ * NORMATIVE EXAMPLE binds an implementation is itself unresolved and is
3814
+ * reported, not assumed.
3815
+ */
3816
+ readonly schemaId: SchemaId;
3817
+ /**
3818
+ * The semantic version of that registered schema (SR-1), carried on the
3819
+ * Proposal by SR-5 so the artifact says which admissible SET it was checked
3820
+ * against and not merely which schema name. This is the axis SR-3 decides
3821
+ * change direction over: a change is NARROWING only where the new admissible
3822
+ * set is a provable subset of the old in every dimension of the bounded
3823
+ * lattice -- character class, length bound, enum set, numeric range -- and
3824
+ * anything not provably narrowing in every dimension is widening and takes
3825
+ * the distinct-reviewer path. v1.2.0 left `narrowing` undefined, which let a
3826
+ * net-widening change self-classify as narrowing and bypass review.
3827
+ */
3828
+ readonly schemaVersion: SchemaVersion;
3829
+ /**
3830
+ * The content address of that schema version (SR-1), and the reason the
3831
+ * triple is a triple rather than a pair: `schema_id` and `schema_version`
3832
+ * are names a registry could remap, a hash is not. Class T here and a NAME
3833
+ * for a binding, not evidence of one (TR-10). The binding is discharged at
3834
+ * §8.4 step 1 -- schema attestation, valid `(schema_id, version, hash)` in
3835
+ * bundle, else DENY -- against the SIGNED registry, and the validator MUST
3836
+ * refuse to start on a signature or hash mismatch (SR-2). SR-6 supplies the
3837
+ * other direction: a Proposal naming a removed schema receives DENY with
3838
+ * `schema_retired`.
3839
+ */
3840
+ readonly schemaHash: Sha256Hash;
3841
+ /**
3842
+ * The fidelity class of the adapter that produced this Proposal. Stamped by
3843
+ * the ADAPTER, bound into the Decision and the receipt, and MUST NOT be
3844
+ * settable by the client (B-6); carried here because B-6 and SR-5 both say
3845
+ * the Proposal carries it.
3846
+ *
3847
+ * CARRIED IS NOT TRUSTED. The Executor MUST verify fidelity against the
3848
+ * bundle-registered adapter binding for this `schema_id` and MUST NOT accept
3849
+ * it as transmitted (TR-8, §9.3 step 7), because a forged `F-HIGH` stamp
3850
+ * suppresses the FC-2 confirmation requirement exactly as v1.3.0's forged
3851
+ * `risk_level_floor_only` suppressed attestation (X1). The value is inside
3852
+ * the hash preimage, so it is bound to everything `proposal_hash` binds --
3853
+ * including the object the attesters signed and the
3854
+ * `resource.effective_tier` comparisons that read `fidelity` in §8.3's own
3855
+ * reference bundle -- and it is still not authority for any control
3856
+ * decision.
3857
+ *
3858
+ * AND NO CLAUSE SAYS WHAT A DISAGREEMENT HERE MEANS. §9.3 step 7a makes a
3859
+ * disagreement between the recomputed value and the RECEIPT's value a
3860
+ * critical alert. Nothing states the outcome when the recomputed value
3861
+ * disagrees with the value carried in the PROPOSAL, which is the value AT-3
3862
+ * showed the attesters and the one the quorum is bound to. The Python
3863
+ * reference never reaches the question: its Proposal carries no `fidelity`
3864
+ * field at all, which is B-6 and SR-5 unimplemented rather than a reading of
3865
+ * them.
3866
+ */
3867
+ readonly fidelity: FidelityClass;
3868
+ /**
3869
+ * The tenant the admitting Ingress Adapter's VERIFIED bundle governs
3870
+ * (RAD-5's value, the manifest's `tenant_id`), stamped by the adapter under
3871
+ * PR-2 and never settable by the client -- B-6's rule for `fidelity`,
3872
+ * applied to the second value the door supplies. It exists so that §9.3 step
3873
+ * 8 has a byte to compare: the step requires the receipt's tenant to equal
3874
+ * the Proposal's AND the trusted bundle's, both held independently of the
3875
+ * receipt, and until v1.3.27 the schema-shaped Proposal carried no tenant at
3876
+ * all, so a conformant door's output could not pass a conformant Executor's
3877
+ * step 8. Class T on the wire, discharged at step 8 -- and NOT a lookup key:
3878
+ * the Executor selects its bundle from its own configuration and compares
3879
+ * this value against it, never the other way round, because a transmitted
3880
+ * tenant that selected the policy would be the KMS's pre-RAD-5 defect one
3881
+ * component over.
3882
+ */
3883
+ readonly tenantId: TenantId;
3884
+ /**
3885
+ * The validated object itself: PR-1's closed payload of `task_type`,
3886
+ * `operator`, `targets`, `params` and `cidrs`, all five REQUIRED. V-11 is
3887
+ * the clause that names both halves -- it requires the validator to reject
3888
+ * any envelope whose payload `task_type` disagrees with its `schema_id`.
3889
+ * §8.3.1 says the typing environment risk expressions resolve against is
3890
+ * `derived from the Proposal schema`: since PR-1 that environment is
3891
+ * `params` (each entry an EL-2 value, bound under its own name) together
3892
+ * with each target's tier from `floors.json` (`<target>.effective_tier`,
3893
+ * RK-2), and nothing else in the envelope is visible to a rule.
3894
+ */
3895
+ readonly payload: ProposalPayload;
3896
+ }
3897
+ /**
3898
+ * The lenient projection of Proposal: every field optional, every enum
3899
+ * degraded to `string`. What a verifier parses. Never make a decision on
3900
+ * one — the fail-safe lookups live on the strict type only.
3901
+ */
3902
+ export interface RawProposal {
3903
+ readonly schemaId?: string;
3904
+ readonly schemaVersion?: string;
3905
+ readonly schemaHash?: string;
3906
+ readonly fidelity?: string;
3907
+ readonly tenantId?: string;
3908
+ readonly payload?: RawProposalPayload;
3909
+ }
3910
+ /**
3911
+ * `task_type` and the four fields PR-1 closes the payload around. CLOSED at
3912
+ * this level since v1.3.27, and the registered Input Schema's fields live
3913
+ * INSIDE `params`, where V-1 closes them.
3914
+ *
3915
+ * WHAT WAS OPEN HERE, AND WHY IT IS NOT ANY MORE. Through v1.3.26 this
3916
+ * object declared `task_type` and left everything else undeclared, on the
3917
+ * argument that the payload's field set is closed SOMEWHERE ELSE -- the
3918
+ * per-tenant, content-addressed registry SR-1 and L-06 describe, a
3919
+ * DEPLOYMENT fact this file must not restate. The argument stands, and PR-1
3920
+ * relocates it one level down: the registered schema governs `params`, an
3921
+ * object whose keys are the deployment's, while the four members every
3922
+ * Proposal has in common -- the action class, the principal, the resources,
3923
+ * the CIDR widths -- are named once, here, and REQUIRED. An open payload had
3924
+ * left the verifier no way to tell a registered field from a smuggled one,
3925
+ * and the generated types carried `task_type` and nothing else, which a
3926
+ * reader could take for the whole payload (ACP-153, item 14).
3927
+ *
3928
+ * Z4, SAID PLAINLY. Every member REQUIRED and both objects closed is AT-8b's
3929
+ * rule -- no optional fields, no defaultable fields, no extension points --
3930
+ * applied to the Proposal, which through v1.3.26 had no counterpart of it.
3931
+ * `params` and `cidrs` are EMPTY objects when an action has none, never
3932
+ * absent, so one action has one encoding. What this cannot close: a
3933
+ * registered Input Schema that declares an optional or defaulted field
3934
+ * inside `params` reopens it there, and no clause forbids one from doing so;
3935
+ * B-1a is what stops that becoming two hashes -- the CANONICAL Proposal
3936
+ * travels to the Executor and §9.3 step 3 hashes what arrived -- and the
3937
+ * absence of an AT-8b-style rule for registered schemas is reported, not
3938
+ * assumed harmless.
3939
+ *
3940
+ * WHAT THIS TYPE ENFORCES, AND WHAT IT DOES NOT. EL-2: every `params` value
3941
+ * is an integer or a string, and a value of any other JSON type -- a
3942
+ * non-integer number, a boolean, null, an array, an object, or an integer
3943
+ * outside the width the deployment declares under AC-1a -- MUST cause the
3944
+ * Proposal to be REFUSED, never re-typed and never coerced. Since v1.3.27
3945
+ * the generated types carry that domain (`param_value`), so an off-domain
3946
+ * value fails to parse rather than being read as a string; the clause exists
3947
+ * because a JSON `22.0` bound as a STRING in the Python reference graded
3948
+ * MEDIUM where `port: 22` graded HIGH, against this document's own reference
3949
+ * bundle. Not enforced here and enforced at the validator instead: V-7 (NFC
3950
+ * before pattern matching, bidi and zero-width code points refused), V-6
3951
+ * (duplicate keys refused at every depth), L-01 to L-05 (32 KiB before
3952
+ * parsing, depth 8, 24 fields per schema, 2,048 characters per string, 100
3953
+ * items per list), and V-1's closure of `params` against the registered
3954
+ * schema -- for which no signed registry exists yet in the bundle (ACP-153,
3955
+ * item 12).
3956
+ * Generated from proposal.schema.json#/$defs/proposal_payload.
3957
+ */
3958
+ export interface ProposalPayload {
3959
+ /**
3960
+ * The action class: a `Literal` over the registered set, closed world, no
3961
+ * dynamic dispatch and no plugins (V-8). §8.4 step 3 looks up a risk
3962
+ * function by it and DENIES where none exists -- an action with no risk
3963
+ * function is refused at `8.4-3` rather than graded HIGH, because grading an
3964
+ * unknown action is P-4's rule broken. RV-3 recomputes the reversibility
3965
+ * class from the trusted bundle and THIS value, never from the receipt, and
3966
+ * RV-1's absent-implies-IRREVERSIBLE default keys on it; DR-13 then reads
3967
+ * that default on the fast path, which is what turned it from documentation
3968
+ * into a control.
3969
+ *
3970
+ * V-11 REQUIRES IT TO AGREE WITH `schema_id` AND DOES NOT SAY WHAT AGREEMENT
3971
+ * IS. The clause admits two readings -- string identity, or a lookup
3972
+ * resolving `schema_id` to the task type it registers -- and the two
3973
+ * disagree on this repository's own fixtures, which pair `schema_id:
3974
+ * "fw.v1"` with `task_type: "modify_firewall_rule"`. §6.3's normative
3975
+ * example implements identity (`v.task_type != info.data["schema_id"]`),
3976
+ * under which these are two spellings of one value and this property is
3977
+ * redundant with `schema_id` -- and V-11 exists precisely because two
3978
+ * spellings can disagree. The pattern below is `schema_id`'s, for that
3979
+ * reason and no stronger one: no clause states a pattern for `task_type`
3980
+ * directly, and this is the strongest thing the text supports.
3981
+ *
3982
+ * Schema constraints, carried as documentation and NOT enforced by this
3983
+ * type: pattern `^[a-z0-9_-]{1,32}$`.
3984
+ */
3985
+ readonly taskType: string;
3986
+ /**
3987
+ * The operating principal the action is proposed for: the identity §8.4 step
3988
+ * 4 checks every required capability against, step 6 tests against the
3989
+ * excluded roles, and §8.5 groups accumulators by. The Attestation Object's
3990
+ * `operator_id`, referenced rather than re-declared: one definition of a
3991
+ * principal identifier. CARRIED IS NOT TRUSTED. Below floor-HIGH this is the
3992
+ * disclosed residual the receipt's `operator` already was -- a
3993
+ * client-asserted principal, bounded by the capability check that binds it
3994
+ * to signed entitlements (§8.8). At floor-HIGH the authoritative value is
3995
+ * `attestations[].obj.operator` inside every attester signature (Y4, §9.3
3996
+ * step 7b(iii-a)), and the Policy Engine copies THIS value into the object
3997
+ * the attesters sign, so a Proposal whose `operator` differs from what the
3998
+ * quorum attested cannot execute.
3999
+ */
4000
+ readonly operator: OperatorId;
4001
+ /**
4002
+ * The resources the action names, by the identifiers `floors.json` is keyed
4003
+ * by (RK-1; the item bounds are that table's `propertyNames`). At least one:
4004
+ * an action that names no resource has no floor to recompute a risk from,
4005
+ * and TR-8 has nothing to recompute against -- refused at the door under
4006
+ * this schema rather than graded against nothing. At most 100 (L-05's
4007
+ * per-list bound). Each target's tier enters the typing environment as
4008
+ * `<target>.effective_tier` (RK-2), and a target absent from `floors.json`
4009
+ * resolves to `T3` (RK-1) -- unknown is never LOW.
4010
+ *
4011
+ * Schema constraints, carried as documentation and NOT enforced by this
4012
+ * type: minItems 1, maxItems 100; items minLength 1, maxLength 256.
4013
+ */
4014
+ readonly targets: readonly string[];
4015
+ /**
4016
+ * The registered Input Schema's object -- the fields `(schema_id,
4017
+ * schema_version, schema_hash)` resolves to and V-1 closes -- carried under
4018
+ * one key so the four common members beside it can be named without
4019
+ * restating a per-tenant registry here. Each value is an EL-2 value
4020
+ * (`param_value`): an integer or a string, bound into the typing environment
4021
+ * under its own name. EMPTY, never absent, for an action with no parameters
4022
+ * (Z4). Nothing in this repository validates a payload against a registered
4023
+ * Input Schema, because no signed registry exists in the bundle yet
4024
+ * (ACP-153, item 12): until it does, `params` is closed by name at the
4025
+ * registry a deployment holds and open to this file, which is disclosed
4026
+ * rather than papered over.
4027
+ *
4028
+ * Map keys: A parameter name as the registered Input Schema declares it and
4029
+ * as a rule names it in an expression (§8.3.1): lower-case, underscores, at
4030
+ * most 64 characters. The set of names is the REGISTERED schema's (SR-1,
4031
+ * V-1), closed there and not here.
4032
+ */
4033
+ readonly params: Readonly<Record<string, ParamValue>>;
4034
+ /**
4035
+ * Prefix widths of the CIDR-typed parameters, by parameter name:
4036
+ * `{"source_cidr": 24}` for a `/24`. §8.3.1's `prefix_len(...)` reads them,
4037
+ * and the environment builder binds each as `<name>.prefix_len` (AC-1a's
4038
+ * integer width). A width is 0..128; an IPv4 width above 32 is the
4039
+ * registered schema's to refuse. EMPTY, never absent, for an action with no
4040
+ * CIDR parameter. Carried apart from `params` rather than parsed out of a
4041
+ * string there, because the width is what a rule compares and a rule that
4042
+ * had to parse a CIDR would be a rule that could be made to parse it
4043
+ * wrongly.
4044
+ *
4045
+ * Map keys: The name of the CIDR-typed parameter the width belongs to, in
4046
+ * the same class as a `params` name.
4047
+ *
4048
+ * Schema constraints, carried as documentation and NOT enforced by this
4049
+ * type: values minimum 0, maximum 128.
4050
+ */
4051
+ readonly cidrs: Readonly<Record<string, number>>;
4052
+ }
4053
+ /**
4054
+ * The lenient projection of ProposalPayload: every field optional, every enum
4055
+ * degraded to `string`. What a verifier parses. Never make a decision on
4056
+ * one — the fail-safe lookups live on the strict type only.
4057
+ */
4058
+ export interface RawProposalPayload {
4059
+ readonly taskType?: string;
4060
+ readonly operator?: string;
4061
+ readonly targets?: readonly string[];
4062
+ readonly params?: Readonly<Record<string, unknown>>;
4063
+ readonly cidrs?: Readonly<Record<string, number>>;
4064
+ }
4065
+ /**
4066
+ * One execution per receipt (CL-3, T-09). The Executor claims the receipt
4067
+ * nonce at §9.3 step 6, before any effect leaves the control plane, and a
4068
+ * second receipt bearing the same nonce is refused because the ledger
4069
+ * already holds it. AT-5's shape applies here too: single use is enforced by
4070
+ * consumption, never by assertion -- v1.2.0 stated the property and shipped
4071
+ * no mechanism.
4072
+ * Generated from ledger_claim.schema.json#/$defs/receipt_nonce_claim.
4073
+ */
4074
+ export interface ReceiptNonceClaim {
4075
+ readonly tenantId: TenantId;
4076
+ /**
4077
+ * CL-3's first identifier class. See the note on `artifact_class` naming in
4078
+ * `$defs/tenant_id` and in the schema description: the four tokens are this
4079
+ * schema's rendering of class names the specification states only in prose.
4080
+ */
4081
+ readonly artifactClass: 'receipt_nonce';
4082
+ /**
4083
+ * The receipt's `nonce` (§9.2), sized 128 bits by L-17 and typed by WE-4.
4084
+ * Named `identifier` rather than `nonce` because CL-5 writes the ledger key
4085
+ * as `(tenant_id, artifact_class, identifier)` and a second spelling of one
4086
+ * column is the defect this repository is built around.
4087
+ */
4088
+ readonly identifier: Nonce128;
4089
+ }
4090
+ /**
4091
+ * The lenient projection of ReceiptNonceClaim: every field optional, every enum
4092
+ * degraded to `string`. What a verifier parses. Never make a decision on
4093
+ * one — the fail-safe lookups live on the strict type only.
4094
+ */
4095
+ export interface RawReceiptNonceClaim {
4096
+ readonly tenantId?: string;
4097
+ readonly artifactClass?: string;
4098
+ readonly identifier?: string;
4099
+ }
4100
+ /**
4101
+ * The SIGNED OBJECT a door produces during a deferred-release hold (DR-14):
4102
+ * the human-readable summary of one action, bound to the canonical Proposal
4103
+ * it was rendered from and to the role that rendered it. One shape, two
4104
+ * producers -- the Attestation Presentation Service signs the approval
4105
+ * summary under the `presentation` key and the notification service signs
4106
+ * the Notification under the `notification` key, both named in the bundle's
4107
+ * door_identities.json (PB-11). The Executor verifies each under the key for
4108
+ * its role and compares each `proposal_hash` against its own canonical hash
4109
+ * of the held Proposal.
4110
+ *
4111
+ * WHAT THE SIGNATURE DOES NOT PROVE. That `summary` is faithful to the bytes
4112
+ * `proposal_hash` names. A compromised door signs whatever it renders; what
4113
+ * it can no longer do is sign for the OTHER door, because it does not hold
4114
+ * that key. Rendering honesty remains DR-2's structural requirement -- no
4115
+ * shared rendering path, demonstrated at build time under conformance suite
4116
+ * 3 -- and this object must not be read as though a signature supplied it.
4117
+ *
4118
+ * CLOSED FIELD SET, FOR Z4'S REASON: an optional field gives one summary two
4119
+ * valid encodings. No nonce, on purpose: the object binds `proposal_hash`, a
4120
+ * replay of it is the same summary for the same action, and DR-4/DR-5 key
4121
+ * repudiation on the proposal, not on the summary.
4122
+ * Generated from notification.schema.json#/$defs/rendered_summary.
4123
+ */
4124
+ export interface RenderedSummary {
4125
+ /**
4126
+ * CR-1: the signature suite this object is signed under, inside the signed
4127
+ * bytes. An unregistered suite fails closed.
4128
+ */
4129
+ readonly alg: SuiteId;
4130
+ /**
4131
+ * Which door rendered this. Inside the signed bytes so the object says what
4132
+ * it is: the Executor still resolves the key by the slot it expects
4133
+ * (notification under `notification`, approval summary under
4134
+ * `presentation`), and PB-11's distinctness means a summary signed for one
4135
+ * slot cannot verify in the other -- but a signed object that does not name
4136
+ * its own role is one whose meaning depends on where it was found, which is
4137
+ * the two-encodings defect one step removed.
4138
+ */
4139
+ readonly role: DoorRole;
4140
+ /**
4141
+ * The canonical Proposal this summary was rendered from. The binding the
4142
+ * object exists to carry: the Executor compares it against its OWN canonical
4143
+ * hash of the Proposal being held, never against a transmitted value
4144
+ * (TR-10's shape). This is also what DR-4's repudiation cites, which the
4145
+ * unsigned notification of v1.3.24 could not name -- an earlier revision of
4146
+ * this file reported that gap and did not patch it; DR-14 closes it.
4147
+ */
4148
+ readonly proposalHash: Sha256Hash;
4149
+ /**
4150
+ * The human-readable summary DR-2 requires, rendered from the canonical
4151
+ * Proposal bytes by the door named in `role`. §7.3 governs its content and
4152
+ * this is the field most exposed to the model: the canonical Proposal
4153
+ * carries parameters that passed validation and are still
4154
+ * attacker-influenced, so whatever displays this string MUST treat it as
4155
+ * DATA and never as markup -- an approver reading attacker-influenced prose
4156
+ * is an injection target, and T-02 includes humans. Empty is refused
4157
+ * (`minLength: 1`, this schema's reading): a summary with no content is a
4158
+ * detection channel that is present and blank. No maximum, deliberately: an
4159
+ * invented bound silently truncates a legitimate summary, which is a display
4160
+ * lie manufactured by the schema. Named for the specification's word;
4161
+ * `reference/src/acp_executor.py` called it `text` through v1.3.24.
4162
+ *
4163
+ * Schema constraints, carried as documentation and NOT enforced by this
4164
+ * type: minLength 1.
4165
+ */
4166
+ readonly summary: string;
4167
+ }
4168
+ /**
4169
+ * The lenient projection of RenderedSummary: every field optional, every enum
4170
+ * degraded to `string`. What a verifier parses. Never make a decision on
4171
+ * one — the fail-safe lookups live on the strict type only.
4172
+ */
4173
+ export interface RawRenderedSummary {
4174
+ readonly alg?: string;
4175
+ readonly role?: string;
4176
+ readonly proposalHash?: string;
4177
+ readonly summary?: string;
4178
+ }
4179
+ /**
4180
+ * What the KMS receives from the Policy Engine. It is the only artifact that
4181
+ * crosses leg F3.2 of the leg register (ZIFFER-DEPLOY-001 Annex A), and it
4182
+ * carries one thing: the canonical bytes the KMS is being asked to sign with
4183
+ * the `receipt_signing_key` (§9.1.1 step 6).
4184
+ *
4185
+ * WHY THE FIELD SET IS CLOSED, AND WHY IT HAS EXACTLY ONE MEMBER. §9.1.1
4186
+ * step 2 has the KMS extract 'the claimed `policy_bundle_hash` and
4187
+ * `bundle_epoch`' from every signing request. Both are already inside the
4188
+ * bytes below, because both are receipt fields (§9.2). Carrying them AGAIN
4189
+ * beside those bytes would give one value two spellings, and the two
4190
+ * spellings are separately chosen by the party under verification. A
4191
+ * compromised Policy Engine puts the current epoch and the current bundle
4192
+ * hash in the request fields -- which is what the KMS retrieves, verifies
4193
+ * and checks against its high-water mark (steps 3-5) -- and a superseded,
4194
+ * genuinely signed, more permissive bundle's hash and epoch in the body it
4195
+ * hands over. Every step of §9.1.1 passes, and the KMS then signs, under the
4196
+ * one key the Executor trusts, a receipt naming a bundle it never verified.
4197
+ * That is not a subtle failure of the protocol: it is RAD-4 violated in the
4198
+ * exact words RAD-4 uses, reached without the KMS breaking any rule it was
4199
+ * given, because the rule was given about a different field. Downstream it
4200
+ * is T-15 bundle rollback, and §9.3 step 4 is the only thing left in its way
4201
+ * -- a comparison against the Executor's last-seen epoch, which
4202
+ * ZIFFER-SPEC-001 never makes durable or shared (ZIFFER-DEPLOY-001 DP-40
4203
+ * supplies that obligation and records the gap as a live one).
4204
+ * `additionalProperties: false` with one required member is what forbids the
4205
+ * second spelling, and it is the reason this artifact needs a schema at all
4206
+ * rather than being left to the transport.
4207
+ *
4208
+ * WHAT THE KMS DOES WITH THIS, SO THE SCHEMA IS NOT MISREAD AS AUTHORITY.
4209
+ * Every value reachable through this field is a CLAIM by the Policy Engine,
4210
+ * and RAD-4 is explicit that the KMS MUST NOT accept bundle metadata, epochs
4211
+ * or policy hashes from the Policy Engine runtime without cryptographic
4212
+ * verification. The `policy_bundle_hash` in the body is a NAME for a bundle,
4213
+ * never evidence of one: the KMS retrieves that bundle from the read-only
4214
+ * repository the Policy Engine runtime cannot write to (§9.1.1 step 3,
4215
+ * isolation stronger than filesystem permissions) and verifies its Ed25519
4216
+ * signature against its own copy of the `policy_signing_key` public key
4217
+ * (step 4). On failure it rejects all signing requests until a valid bundle
4218
+ * is presented (RAD-4). The `bundle_epoch` in the body is not authority
4219
+ * either: RAD-3 compares it against the highest epoch the KMS has ever
4220
+ * validated for that tenant in durable storage, rejects anything lower, and
4221
+ * updates the mark (step 5). The mark is state at the KMS and appears in no
4222
+ * message here. TR-10 states the general form -- a transmitted identifier is
4223
+ * a name for a binding, not evidence of one.
4224
+ *
4225
+ * WHAT IS NOT A FIELD, each absence with its reason. (a) No
4226
+ * `policy_bundle_hash`, `bundle_epoch` or `tenant_id` beside the body: one
4227
+ * value, one spelling, per the paragraph above; the tenant RAD-1, RAD-2 and
4228
+ * RAD-3 are all scoped to is the body's `tenant_id` (§9.2). (b) No key
4229
+ * identifier and no algorithm or suite selector: the Executor verifies under
4230
+ * the `receipt_signing_key` the signed bundle registers (§9.3 step 1), so
4231
+ * the key is settled by the bundle the KMS has just verified, not by the
4232
+ * requester. Offering the party under verification a negotiable parameter is
4233
+ * the downgrade shape AB-0 refuses one layer up, where accepting receipt
4234
+ * versions 2 and 3 concurrently would let an attacker present the weaker
4235
+ * one. (c) No canonical Proposal: §9.1.1 gives the KMS steps 2-5 and no
4236
+ * policy evaluation. The KMS is independent about BUNDLE AUTHENTICITY, not
4237
+ * about whether the Decision is correct, and §10's compromise table already
4238
+ * grants that a compromised Policy Engine emits arbitrary Decision content,
4239
+ * answering it with RAD-1/RAD-2 rate limits, the Executor's recomputation
4240
+ * (TR-8) and audit replay. A Proposal on this leg would read as a second
4241
+ * evaluation that no clause defines. (d) No attestation entries: under
4242
+ * receipt version 3 they are excluded from the signed body by construction
4243
+ * (AB-5), they reach the KMS from the approver devices on leg F4.4, and they
4244
+ * travel beside the receipt on F3.3. (e) No `x-acp-absent` anywhere: no
4245
+ * clause declares a fail-safe default for any part of this artifact, and
4246
+ * inventing one would give an absent field a meaning the specification does
4247
+ * not give it.
4248
+ *
4249
+ * WHAT THIS SCHEMA DOES NOT CONSTRAIN, stated so the claim is not read wider
4250
+ * than it is. It constrains SHAPE, and shape is not authority: under the §14
4251
+ * suite-12 classification every value reachable through this field is class
4252
+ * T at the schema layer, and the receiving obligations are RAD-3 and RAD-4
4253
+ * exactly as the leg register cites them, not restated here
4254
+ * (ZIFFER-DEPLOY-001 DP-84). It says nothing about ORDER, and §9.1.1 is an
4255
+ * ordered checklist: a KMS that signs first and verifies afterwards
4256
+ * satisfies every line below. It says nothing about the durable high-water
4257
+ * mark, which is per-tenant state and crosses no leg. It says nothing about
4258
+ * freshness of the request itself -- no clause gives this leg an anti-replay
4259
+ * value, and a replayed request re-signs the same body, whose duplicate is
4260
+ * caught downstream only by CL-3's consumption of the receipt nonce. And it
4261
+ * says nothing about the response: §9.1.1 step 7's `SIGNING_DENIED` is a
4262
+ * normative reply with no schema here and no row in the leg register.
4263
+ *
4264
+ * WHAT THE SPECIFICATION DOES NOT ANSWER. Recorded rather than filled in,
4265
+ * because a schema that invents an answer publishes it as though the
4266
+ * specification had one. (1) Who assembles `attestation_digests` (AB-1) is
4267
+ * unstated, and the register gives the Policy Engine no leg on which
4268
+ * attestation entries or their digests reach it -- F4.4 routes them to the
4269
+ * KMS. If the KMS assembles them, this leg carries a PARTIAL body and this
4270
+ * schema is wrong in shape rather than in detail. (2) §9.1.1 step 2 does not
4271
+ * say whether the claimed values are separate request fields or are read out
4272
+ * of the body; this schema takes the second reading and gives its reason
4273
+ * above, but the text settles neither. (3) No clause names the party that
4274
+ * canonicalizes the receipt body, nor forbids the KMS re-encoding a
4275
+ * structure it decoded before signing; AT-8a states that rule for the
4276
+ * Attestation Object only. (4) AB-6's 4,096-byte bound is written about the
4277
+ * Executor, and no clause states it at this leg even though the bytes are
4278
+ * the same bytes. (5) Nothing in this repository builds or consumes a
4279
+ * signing request: `sim/receipts.py` plays the KMS and signs directly, there
4280
+ * is no KMS-side epoch mark anywhere, and `tools/check-flow-legs.py`
4281
+ * resolves this leg's RAD-3 citation against a raise site in the Executor
4282
+ * and exempts RAD-4 outright. So the two obligations this leg cites have no
4283
+ * executable consumer AT THIS BOUNDARY, which is the
4284
+ * normative-text-with-no-executable-consumer shape ACP-50, ACP-56 and ACP-57
4285
+ * each landed a check for.
4286
+ * Generated from signing_request.schema.json.
4287
+ */
4288
+ export interface SigningRequest {
4289
+ /**
4290
+ * The bytes to be signed: the canonical CBOR encoding (RFC 8949 §4.2) of the
4291
+ * receipt body WITHOUT the attestation entries (WE-1, WE-2, AB-5), carried
4292
+ * as a WE-4 value.
4293
+ *
4294
+ * THE NAME IS THE SPECIFICATION'S. AB-5, AB-6 and §9.3 step 7b all call
4295
+ * these bytes 'the signed body', so calling them anything else here would be
4296
+ * a second vocabulary for one object -- the defect ZIFFER-DEPLOY-001 DP-16
4297
+ * forbids one layer down. At this leg the signature does not exist yet; the
4298
+ * name is for the bytes the signature will cover, and no field here carries
4299
+ * a signature.
4300
+ *
4301
+ * THE KMS SIGNS THE BYTES IT RECEIVED. It parses them to extract the claimed
4302
+ * `policy_bundle_hash` and `bundle_epoch` of §9.1.1 step 2, and signs the
4303
+ * received encoding rather than a re-encoding of the structure it decoded.
4304
+ * Two encoders on one signing path is the defect AT-8a names for the
4305
+ * Attestation Object -- issuer and verifier disagree and one object acquires
4306
+ * two identities -- and nothing about the receipt makes it immune, since the
4307
+ * Executor recomputes nothing here and verifies the signature over the bytes
4308
+ * it is handed (§9.3 step 1). WE-3 refuses JSON as receipt input for the
4309
+ * same reason; it is written about a receipt consumer rather than about this
4310
+ * leg, so it supports the rule here without stating it. Carrying bytes
4311
+ * rather than a decoded object is also what makes 'the bytes signed are the
4312
+ * bytes the Policy Engine composed' a checkable claim instead of an
4313
+ * assumption.
4314
+ *
4315
+ * THE LENGTH BOUND IS DERIVED, NOT CITED, AND IT IS NOT EXACT. AB-6 caps the
4316
+ * signed body at 4,096 bytes and requires the check on MEASURED BYTES rather
4317
+ * than on any proxy, having been written after a draft capped the digest
4318
+ * count at 50 and measured 4,107 bytes at that cap. These are the bytes AB-6
4319
+ * bounds (AB-5), so a request over the cap asks for a receipt no conformant
4320
+ * Executor may accept and no custody tier T2 key can produce -- the
4321
+ * 4,096-byte limit is AWS KMS's `MessageType: RAW` cap, executed rather than
4322
+ * read in `tools/kms-compat-2026-08-22.json` (5/5 PASS, 2026-08-22).
4323
+ * `maxLength` here is on the base64 TEXT, and base64 quantizes at three
4324
+ * bytes: 5,468 characters admits 4,098 bytes, two more than AB-6 allows. It
4325
+ * is therefore a floor on refusal and never the check. An implementation
4326
+ * MUST measure the decoded bytes, which is AB-6's own warning about proxies
4327
+ * applied to AB-6's own transport encoding. No clause places the bound at
4328
+ * this leg at all; that gap is recorded on the schema above rather than
4329
+ * closed here.
4330
+ *
4331
+ * EVERYTHING IN THESE BYTES IS A CLAIM. The KMS verifies the bundle they
4332
+ * name (RAD-4) and the epoch they reference (RAD-3) against artifacts and
4333
+ * state it holds itself; it does not verify the Decision they assert, and no
4334
+ * clause asks it to.
4335
+ */
4336
+ readonly signedBody: Base64Bytes;
4337
+ }
4338
+ /**
4339
+ * The lenient projection of SigningRequest: every field optional, every enum
4340
+ * degraded to `string`. What a verifier parses. Never make a decision on
4341
+ * one — the fail-safe lookups live on the strict type only.
4342
+ */
4343
+ export interface RawSigningRequest {
4344
+ readonly signedBody?: string;
4345
+ }
4346
+ /**
4347
+ * One claim made against the Consumption Ledger. Carried on leg F3.4 of the
4348
+ * leg register (ZIFFER-DEPLOY-001 Annex A), Executor -> Consumption Ledger,
4349
+ * whose cited receiving obligations are CL-2, CL-3, CL-6 and CL-7. The
4350
+ * Consumption Ledger is TCB (§4.3): a ledger that falsely reports an
4351
+ * identifier absent reopens T-09 (receipt replay) and T-14 (attestation
4352
+ * amplification). This document is the smallest thing that can be wrong
4353
+ * before any of those guarantees is reachable at all.
4354
+ *
4355
+ * WHY THIS IS FOUR CLOSED OBJECTS AND NOT ONE OBJECT WITH OPTIONAL FIELDS.
4356
+ * CL-3 enumerates four identifier classes and they do not carry the same
4357
+ * value: a receipt nonce is 128 bits rendered `b64:` (WE-4, L-17), an
4358
+ * attestation id is a `sha256:` digest over the canonical CBOR of the whole
4359
+ * Attestation Object (AT-1, AT-8a), a bundle epoch is an integer high-water
4360
+ * mark (CL-3, RAD-3), and an origin binding is a PAIR, `proposal_hash` to
4361
+ * `origin_nonce` (DS-6f). Folding those into one object requires optional
4362
+ * fields, and an optional field is Z4 exactly as AT-8b describes it one
4363
+ * layer up: the same claim present-as-null and the same claim with the field
4364
+ * omitted are each well formed, encode differently, and any ledger that keys
4365
+ * or audits on the encoded claim then holds two rows for one claim. One
4366
+ * nonce, two slots, which is the hole CL-3 exists to close. So each class is
4367
+ * its own object, every field is REQUIRED, `additionalProperties` is false,
4368
+ * and the composition is `oneOf` rather than `anyOf` so that a document
4369
+ * matching two branches is REFUSED rather than resolved to the first match.
4370
+ *
4371
+ * `x-acp-absent` deliberately appears nowhere below. §9.3.1 declares no
4372
+ * absent-value default for a claim, and inventing one here would let a
4373
+ * malformed claim be normalized into a well-formed one, which is the precise
4374
+ * opposite of what CL-2 requires when the ledger cannot proceed. A default
4375
+ * that turns a refusal into an acceptance is not a fail-safe default.
4376
+ *
4377
+ * WHY `artifact_class` IS CARRIED AND NOT INFERRED FROM THE VALUE. CL-5's
4378
+ * key is the triple `(tenant_id, artifact_class, identifier)`, and the class
4379
+ * is what keeps the four namespaces disjoint. Without it, one execution per
4380
+ * receipt (T-09) and one receipt per attestation (T-14) stop being
4381
+ * independent guarantees: an identifier claimed in one class is observed as
4382
+ * claimed in the other, so consuming an attestation can silently consume a
4383
+ * nonce that was never issued, or fail to consume one that was. Inferring
4384
+ * the class from the value's syntax is strictly worse than carrying it,
4385
+ * because two of the four classes already share the `sha256:` form -- an
4386
+ * attestation id and the key half of an origin binding are both digests, and
4387
+ * nothing in the string distinguishes them.
4388
+ *
4389
+ * WHAT IS NOT A FIELD, AND WHY. (a) No timestamp. CL-8 requires identifier,
4390
+ * class, timestamp and Executor identity in the audit row of every claim
4391
+ * operation, hit and miss; it does not say the claim carries them, and F3.4
4392
+ * does not cite CL-8 at all. A claim-carried timestamp lets the party the
4393
+ * audit row is ABOUT choose the time that row is dated, which is the T-32
4394
+ * shape -- a component certifying a fact about itself -- while the ledger
4395
+ * has its own clock and is the party doing the recording. (b) No Executor
4396
+ * identity, for the same reason: an identity taken from the authenticated
4397
+ * connection (mTLS is REQUIRED between components, §4.3) is evidence, an
4398
+ * identity typed into the message by the sender is a name for one. (c) No
4399
+ * compare-and-swap comparand. CL-2 requires CAS or an equivalent atomic
4400
+ * primitive, but the expected value is fixed by the semantics rather than
4401
+ * chosen per call -- absence, for the three consumption classes, and
4402
+ * absence-or-the-already-adopted-value for the binding (CL-7) -- so a
4403
+ * transmitted expectation would let the claimant choose the bar it is
4404
+ * measured against. For the epoch class that is not a subtlety: a
4405
+ * transmitted expected high-water mark is T-15 bundle rollback with extra
4406
+ * steps. (d) No response. The ledger's answer travels the other way and the
4407
+ * register has no return row for it; that omission is recorded against this
4408
+ * schema rather than silently repaired by modelling a message the register
4409
+ * does not carry.
4410
+ *
4411
+ * WHAT THIS SCHEMA CANNOT CONSTRAIN, STATED SO THE CLAIM IS NOT READ WIDER
4412
+ * THAN IT IS. Every clause F3.4 cites is about BEHAVIOUR or ORDERING, and
4413
+ * none of them is expressible in a message shape. CL-1 linearizability; CL-2
4414
+ * atomicity and fail-closed when the ledger is unavailable; CL-6 fail-closed
4415
+ * when absence cannot be confirmed; CL-7 completing the read phase across
4416
+ * the quorum before mutating any replica, and never resolving a disagreement
4417
+ * by vote; CL-4 retention; DS-6f immutability of the binding and the
4418
+ * critical alert on a rebind attempt. A schema-valid claim says nothing
4419
+ * about any of them. They are per-implementation obligations
4420
+ * (`spec/vectors/OBLIGATIONS.md`), exercised by
4421
+ * `reference/suites/partition_suite.py` and `partition_integration.py`. An
4422
+ * implementation reporting "CL-7 satisfied" because a claim validated
4423
+ * against this file has answered a different question from the one CL-7
4424
+ * asks.
4425
+ *
4426
+ * THIS SCHEMA HALTED `tools/codegen.sh`, AND THE HALT IS WHAT GOT THE RULE
4427
+ * WRITTEN. `oneOf` was on the generator's refused list, because it emitted
4428
+ * no rule for a tagged union and a generated type that looked complete while
4429
+ * expressing none of the union would be worse than no type at all. Neither
4430
+ * way of making the halt go away was taken, because both damage something
4431
+ * real: collapsing the four classes into one object needs optional fields,
4432
+ * which is the Z4 defect this schema is shaped to refuse, and dropping a
4433
+ * class contradicts CL-3, which enumerates four. What the halt actually said
4434
+ * is that the ACP-78 wire surface contains a tagged union -- so in M1 the
4435
+ * generator was taught one. It accepts a `oneOf` whose branches are closed
4436
+ * objects made disjoint by a single required `const` field, PROVES that
4437
+ * discriminator from the schema rather than being told it, and still halts
4438
+ * on any `oneOf` that is not tagged. The tag is `artifact_class`, which CL-5
4439
+ * already makes a column of the ledger key, so nothing here exists for the
4440
+ * generator's benefit. What the generated union deliberately does NOT carry
4441
+ * is a decoder that tries branches until one fits: `first shape that fits`
4442
+ * is how a document meant as one artifact class is accepted as another,
4443
+ * which is the namespace collapse `artifact_class` exists to prevent.
4444
+ *
4445
+ * SUITE 12 CLASSIFICATION. Every field below is class T at the schema layer,
4446
+ * because shape is not authority and this document constrains shape. Two of
4447
+ * them need their residual said out loud. `tenant_id` NAMES a namespace and
4448
+ * does not authorise one: where a deployment runs one ledger per tenant
4449
+ * (CL-5) the tenant is fixed by the instance and the authenticated
4450
+ * connection, and a claim MUST NOT reach another tenant's namespace by
4451
+ * naming it. `origin_nonce` is a value being pinned for the first time,
4452
+ * never evidence that a binding exists -- on a re-drive the Executor MUST
4453
+ * READ the origin from the ledger and derive `idempotency_key` from what it
4454
+ * read (DS-6f, closing Z3), and that read is a different operation from this
4455
+ * message.
4456
+ * Generated from ledger_claim.schema.json.
4457
+ *
4458
+ * A DISCRIMINATED union over `artifactClass`, which every branch pins to a
4459
+ * literal. Narrow it by switching on that field; a `default` arm that
4460
+ * accepts an unrecognised value is a branch this union does not have, and
4461
+ * the fail-safe answer there is to refuse. There is no `Raw` union: with
4462
+ * every field optional each branch would accept every other document and the
4463
+ * tag would decide nothing.
4464
+ */
4465
+ export type LedgerClaim = ReceiptNonceClaim | AttestationIdClaim | BundleEpochClaim | OriginBindingClaim;
4466
+ //# sourceMappingURL=generated_wire.d.ts.map