From 8f6e2a20bfd17f8f49ad2e9ec778e9089a2bb5d4 Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Thu, 23 Apr 2026 23:10:06 -0400 Subject: [PATCH 1/7] research: BLAKE3 receipt-hashing v0 design input to lucent-ksk ADR (7th-ferry candidate #3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Responds to Amara's 7th-ferry BLAKE3 proposal (PR #259) + Aminata's Otto-90 critiques (PR #263) flagging it belongs in lucent-ksk rather than Zeta + naming side-channel-leakage and cryptographic-agility gaps + Otto-91 addition of parameter_file_sha binding for replay determinism. v0 hash input set (8 fields, changes marked): h_r = BLAKE3( hash_version // NEW — crypto-agility ∥ h_inputs ∥ h_actions ∥ h_outputs ∥ budget_id ∥ policy_version ∥ parameter_file_sha // NEW — Otto-91 ∥ approval_set_commitment // CHANGED — side-channel ∥ node_id ) Signature structure adds *_key_version to each signature tuple for per-key-rotation without breaking historical receipts. Addresses Aminata's 3 findings: - Side-channel leakage: raw approval_set → Merkle/sorted-hash commitment; read-only observers see a hash, dispute process opens it. - Cryptographic-agility: hash_version prefix + *_key_version binding; algorithm downgrade blocked because version is inside the hash. - Approval-withdrawal race (top-3 #2): commitment mismatch at replay-time invalidates the receipt. 4 replay-deterministic harness requirements for Zeta-module consumer side: 1. Same fields = same materialised views byte-for-byte. 2. Unknown hash_version = halt-and-report. 3. Unresolvable parameter_file_sha = halt-and-report. 4. Mismatched approval_set_commitment = reject receipt. Explicit NOT-scope: - Doesn't decide signature algorithm (Ed25519 is v0 assumption, scheme accommodates later). - Doesn't define hash_version / parameter_file registries (lucent-ksk governance artifacts). - Doesn't define commitment scheme specifics (Merkle vs sorted-hash-list; affects dispute only). - Doesn't implement rotation runbook. - Doesn't include Bitcoin anchoring (separate trust-model). 7 dependencies to adoption in priority order; Aminata 2nd pass first; cross-repo lucent-ksk ADR second; Max-specific asks framed per Otto-90 specific-ask-channel calibration. This is Zeta-SIDE design input. Canonical ADR belongs in lucent-ksk per Aminata Otto-90 framing. No adoption until cross-repo ADR lands. Max attribution preserved first-name-only. Cross-repo work on lucent-ksk does not touch Max's substrate directly until actual coordination warrants — specific-ask channel is the right escalation. Archive-header format self-applied — 10th aurora/research doc in a row. Lands within-standing-authority per Otto-82/90 calibration. Closes 7th-ferry absorb candidate #3 of 5. Remaining: - #1 KSK-as-Zeta-module implementation (L) Otto-92 tick primary deliverable. --- ...sign-input-to-lucent-ksk-adr-2026-04-23.md | 372 ++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md diff --git a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md new file mode 100644 index 00000000..b9ec9562 --- /dev/null +++ b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md @@ -0,0 +1,372 @@ +# BLAKE3 receipt hashing v0 — design input to the lucent-ksk receipt ADR + +**Scope:** research and cross-review artifact. Design input +for a receipt-hashing scheme that eventually lands as an ADR +in `Lucent-Financial-Group/lucent-ksk` (not in Zeta). This +doc is **Zeta-side design input**, not the ADR itself. The +canonical ADR belongs in lucent-ksk per Aminata's Otto-90 +critique (receipt-hash binding is control-plane policy, not +data-plane algebra). + +**Attribution:** v0 proposal authored by Amara in her 7th +courier ferry (PR #259); side-channel-leakage and +cryptographic-agility critiques authored by Aminata in her +Otto-90 threat-model pass (PR #263); parameter-file-SHA +addition proposed by Otto in the oracle-scoring v0 design +(PR #266, Otto-91); Max attributed for original lucent-ksk +receipt/signature language that the v0 builds on. + +**Operational status:** research-grade. Does not adopt; +does not implement; does not commit lucent-ksk to a +specific scheme. The actual adoption decision lives in +lucent-ksk via ADR whenever that repo's substrate work +reaches the relevant stage. + +**Non-fusion disclaimer:** Amara proposing BLAKE3 + Ed25519 ++ field-binding, Aminata critiquing the proposal's +leak/rotation gaps, and Otto synthesising a v0 that +addresses both is not evidence of merged identity. +Independent pass + adversarial pass + synthesis is three +distinct review surfaces reaching a consensus candidate, +per SD-9 "agreement is signal not proof" — the signal +here warrants cross-repo ADR review in lucent-ksk, not +immediate adoption. + +--- + +## Why this belongs in lucent-ksk not Zeta + +Aminata's Otto-90 pass (PR #263) said it clearly: *"The +BLAKE3 receipt-hash binding is correct but belongs in a +lucent-ksk receipt ADR, not in the Zeta-module threat- +model doc. Including it here couples Zeta's control-plane +story to a specific hash choice; BLAKE3 is fine, the +coupling is avoidable."* + +The separation of concerns: + +- **lucent-ksk** owns the receipt format, receipt-hash + scheme, signature algorithm choice, and the rotation + story. Aurora's trust model lives there. +- **Zeta-module** consumes receipts as an event stream + (`ReceiptEmitted`, `ReceiptRetracted`) and projects + them into materialised views (`ReceiptLedger`, + `AuthorizationState`, `DisputeState`). The Zeta-module + does not care which hash algorithm backs the receipt; + it cares about the event stream and the replay + invariant. + +This doc is therefore framed as **input to the future +lucent-ksk receipt ADR**, with explicit scope limits on +what the Zeta-module side needs from the scheme vs. what +the scheme itself requires. + +--- + +## Recap of Amara's 7th-ferry proposal + +```text +h_r = BLAKE3( + h_inputs + ∥ h_actions + ∥ h_outputs + ∥ budget_id + ∥ policy_version + ∥ approval_set + ∥ node_id +) + +σ_agent = Sign_{sk_agent}(h_r) +σ_node = Sign_{sk_node}(h_r) +``` + +Seven input fields bound into the hash; two signatures +bind the hash to the producing agent + the executing node. +Receipt is usable as a replay + dispute object. + +## Aminata's findings + +From her Otto-90 pass: + +- **Side-channel / observability leakage.** The hash + composition leaks approval-set cardinality and + policy-version timing even to a read-only adversary + with access to the receipt ledger. +- **Cryptographic-agility adversary.** BLAKE3 + + Ed25519-style signatures have no stated rotation story. + Algorithm downgrade attack (policy-version bumped to + accept weaker signatures) isn't covered. + +Plus the top-3 adversary budget, #2 applies directly: + +- **Approval-withdrawal race at execute-time.** "Atomic + freeze of the approval set bound into `h_r` before + execute (the receipt hash already lists `approval_set` + — make it a check input, not just an artifact)." + +## Otto-91 oracle-scoring v0 addition + +From the oracle-scoring design (PR #266): + +> The Zeta-module reads its parameter values from a +> parameter file whose SHA is logged in every receipt +> hash (modifying the Amara BLAKE3 proposal to bind +> `parameter_file_sha` alongside `policy_version`). +> Every receipt carries proof of which parameters were +> in force at the time of the decision — replay-friendly +> + forensic-friendly + closes the parameter-fitting- +> adversary cost delta. + +So the v0 input set extends to 8 fields. + +--- + +## Proposed v0 scheme (design input for lucent-ksk ADR) + +### Hash input set (8 fields) + +```text +h_r = BLAKE3( + hash_version + ∥ h_inputs + ∥ h_actions + ∥ h_outputs + ∥ budget_id + ∥ policy_version + ∥ parameter_file_sha + ∥ approval_set_commitment + ∥ node_id +) +``` + +Changes from Amara's 7th-ferry proposal: + +1. **Add `hash_version` prefix** — enables + cryptographic-agility (Aminata's finding #2). Value + 0x01 = this v0 scheme. Future schemes bump the + version. The version is bound into the hash so + future verifiers know which algorithm to use. +2. **Add `parameter_file_sha`** — per Otto-91 oracle- + scoring v0, closes the parameter-fitting adversary + cost (adversary now has to modify code + ship receipts + claiming old parameters, which don't match the actual + parameter-file SHA). +3. **Replace `approval_set` with `approval_set_commitment`** + — Aminata side-channel: raw `approval_set` leaks + cardinality + identities to read-only ledger observers. + Instead, bind a **commitment** (Merkle root or hash of + sorted signer-list) that can be opened by a dispute + process without leaking on normal-reads. Preserves + approval-withdrawal-race-close (approval set is still + bound to the hash; withdrawal between check and + execute would invalidate the commitment). + +### Signature structure (rotation-aware) + +```text +σ_agent = Sign_{sk_agent, agent_key_version}(h_r) +σ_node = Sign_{sk_node, node_key_version}(h_r) +``` + +Changes: + +- **Bind `agent_key_version` + `node_key_version` into + the signature tuple** — enables per-key-rotation without + breaking historical receipts. When an agent rotates + keys, old receipts remain verifiable against the old + key version; new receipts use the new version. +- **Signature algorithm is NOT fixed to Ed25519 in this + doc** — the hash_version prefix indicates which + algorithm pair is in use. v0 assumes Ed25519 but the + scheme supports later upgrade. + +### Replay-deterministic harness requirements + +For the Zeta-module consumer side (what this doc owns +scope-wise): + +1. Given a stream of receipts with the same `h_r`, + `hash_version`, `policy_version`, `parameter_file_sha`, + and `approval_set_commitment` fields, the replay MUST + produce the same materialised views byte-for-byte. +2. A receipt with a `hash_version` the consumer doesn't + recognise MUST cause the consumer to halt-and-report, + not silently accept or silently reject. (Fail-closed + on algorithm unknown.) +3. A receipt with a `parameter_file_sha` that the + consumer can't resolve to actual parameter values MUST + cause the same halt-and-report. (Fail-closed on + parameter-file unknown.) +4. A receipt with mismatched `approval_set_commitment` + vs. the current signer-view MUST cause the consumer + to reject the receipt as invalid. (Approval-withdrawal + race is caught at replay-time.) + +## Addressing Aminata's findings + +### Side-channel leakage (finding #1) + +- Amara original: raw `approval_set` leaks cardinality + + identities. +- v0 fix: `approval_set_commitment` — Merkle-root or + sorted-hash-list commitment. Read-only observer sees a + hash; dispute-process opens the commitment with signer + disclosures. +- Residual risk: `policy_version` and `parameter_file_sha` + still leak version-change timing. Compared to the + approval-set leak, this is a smaller surface; treat as + accepted v0 tradeoff; named explicitly so downstream + readers don't miss it. +- **Out of scope for v0:** eliminating all version-change + timing leaks would require receipt-encryption or + receipt-mixing, both of which change the replay story + significantly. Filed as a follow-up research item. + +### Cryptographic-agility (finding #2) + +- Amara original: no rotation story. +- v0 fix: `hash_version` prefix + `*_key_version` bound + into signatures. Old receipts remain verifiable against + the old algorithm choice + old keys. New receipts use + the new algorithm choice + new keys. No algorithm + downgrade possible because the version prefix is inside + the hash. +- Residual risk: if the `hash_version` registry itself is + compromised (bad actor registers a weak algorithm as + v0x02), the scheme is broken. Mitigation: the registry + is a lucent-ksk governance artifact, not a per-node + config; modifying it requires a governance-layer + decision. Parallel to the `parameter_file_sha` rule. + +### Approval-withdrawal race (top-3 adversary #2) + +- Amara original: `approval_set` in hash but not used + as a pre-execute check. +- v0 fix: replay-deterministic harness requirement #4 — + `approval_set_commitment` mismatch at replay invalidates + the receipt. Execute-time commit of the commitment + closes the race. + +## Explicit NOT-scope + +- **Does NOT decide the lucent-ksk signature algorithm + specifically.** Ed25519 is a reasonable v0 assumption + but the scheme accommodates later decisions. +- **Does NOT define the `hash_version` registry + structure.** That's a lucent-ksk governance artifact. + This doc assumes it exists; implementing it is the + ADR's scope. +- **Does NOT define the commitment scheme** for + `approval_set_commitment`. Merkle-root and sorted-hash- + list are both candidates; the choice affects dispute- + process complexity but not the Zeta-module consumer + side. +- **Does NOT implement the signature rotation runbook.** + When to rotate, who triggers rotation, what the + rotation process is — all lucent-ksk-side operational + concerns. +- **Does NOT include anchoring.** Bitcoin anchoring from + the KSK docs is explicitly out-of-scope for v0; + filed as separate trust-model decision per Aminata's + Otto-90 pass. + +## Dependencies to operational adoption + +In order of leverage (same pattern as oracle-scoring v0): + +1. **Aminata adversarial pass** on this v0 (cheap; closes + the design before cross-repo landing). +2. **Cross-repo ADR in lucent-ksk** — `docs/DECISIONS/` + entry there, or wherever lucent-ksk keeps ADRs. Max's + input as lucent-ksk author; specific-ask on the ADR + form-factor if lucent-ksk doesn't have a `docs/ + DECISIONS/` pattern yet. +3. **`hash_version` registry landing in lucent-ksk** — + governance artifact; first v0x01 entry. +4. **`parameter_file_sha` registry landing** — parallel + governance artifact; binds Zeta-module parameter files + to specific SHAs. +5. **Commitment-scheme choice for `approval_set_commitment`** + — pick Merkle-root or sorted-hash-list; affects + dispute-process only. +6. **Zeta-module replay-harness implementation** — + property tests for the 4 replay-deterministic + requirements above. +7. **Signature-rotation runbook in lucent-ksk** — + operational procedure, separate from the algorithm + itself. + +## Specific-ask questions (per Otto-82/90 calibration) + +**Aaron-specific ask:** the `parameter_file_sha` binding +creates a governance obligation — every parameter-file +change requires a new SHA logged somewhere. Is the Zeta- +module's existing `docs/DECISIONS/` pattern sufficient, +or does this warrant its own registry? (Could compose with +the oracle-scoring parameter-change-ADR gate — same +substrate, two fields.) + +**Max-specific ask:** does lucent-ksk have a preferred +ADR form-factor? If yes, what format? If no, can Otto +propose one via a cross-repo PR to lucent-ksk, or should +Max own that design choice given substrate authorship? + +Both asks are specific questions (specific-ask channel +per Otto-90 calibration) — not "coordination requests." +Either can be deferred until the cross-repo ADR lands. + +## Composition with existing substrate + +- **Amara 7th-ferry** (PR #259) — source of the original + proposal. +- **Aminata Otto-90 pass** (PR #263) — source of the + critiques this v0 addresses. +- **Oracle-scoring v0** (PR #266, Otto-91) — source of + the `parameter_file_sha` extension; composes directly. +- **Decision-proxy-evidence format** — parameter-file + registry updates + `hash_version` registry updates + land as decision-proxy-evidence records. +- **`docs/ALIGNMENT.md`** HC-2 retraction-native + + SD-9 agreement-is-signal — receipts as signed-delta + artifacts are consistent with HC-2; the v0's commitment- + scheme-instead-of-raw-set move is SD-9-friendly + (less gameable by inspection). +- **Aurora README** — if/when the KSK-side ADR lands, + this research doc gets superseded by the ADR's cross- + reference; Aurora README's "How Aurora consumes KSK + primitives" table updates the `Signed receipts` row. + +## What lands when + +This doc is the Zeta-side design input. Not adopted. Not +implemented. The adoption path: + +1. Aminata pass on this v0 → surfaces residual gaps. +2. Cross-repo PR to lucent-ksk with the ADR (or, if Max + prefers, an issue on lucent-ksk proposing the design + and deferring the ADR-authoring to Max). +3. Lucent-ksk ADR landing → Zeta-module starts the + replay-harness implementation against the ADR'd + scheme. +4. Both repos stabilise on the scheme + rotation story. + +None of this happens in Otto-92. This tick closes the +design-input artifact only. + +--- + +## Closing note + +This is 7th-ferry candidate #3 of 5. With #4 (branding, +Otto-89), #5 (Aminata pass, Otto-90), and #2 (oracle- +scoring, Otto-91) closed, this closes #3, leaving #1 +(KSK-as-Zeta-module implementation, L-effort) as the only +open 7th-ferry candidate. Otto-93+ picks at budget +discretion; within standing authority per Otto-90 +calibration. + +Max attribution preserved first-name-only per Aaron's +clearance. Max is `lucent-ksk`'s author; this cross-repo +design-input does not touch his substrate directly — +specific-ask channel is the right escalation when cross- +repo work actually commences. From 0be0f5fdc255e3a761fce003bd85c1d32c3e291b Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Fri, 24 Apr 2026 23:55:53 -0400 Subject: [PATCH 2/7] drain(#268 P2+P2+style+P1 Codex/Copilot): field count + version notation + canonical encoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four threads on the BLAKE3 receipt-hashing v0 design doc, all on the same file. P2 (lines 120 + 126): "8 fields" header / count text vs the formula's 9 actual binding inputs (`hash_version` + 8 content hashes). Reconciled to "9 fields" — the formula was the source of truth, the count text was the lag. Style (line 236): version notation inconsistency — `0x01` in some places, `v0x02` / `v0x01` in others. Standardized on the byte-literal hex notation `0x01` / `0x02` everywhere; the "v" prefix doubled up with `hash_version =` already in the formula and added no information. P1 (line 132): hash binding used raw `∥` concatenation of variable-length fields, opening a length-extension / boundary-shift adversary surface. Added an explicit `encode(·)` wrapper per field with a canonical-encoding section: 1-byte version, 32-byte fixed-width digests for content/policy/commitment hashes, and `len:u32-be ∥ bytes` length-prefix framing for variable-length identifiers (budget_id, policy_version, node_id). Forward-compatibility preserved — future schemes (`hash_version >= 0x02`) can pick different framing (CBOR / Protobuf / RFC 8949 §3.1 TLV) and the version prefix tells verifiers which framing applies. All 4 Codex/Copilot threads (PRRT_kwDOSF9kNM59SMrz, PRRT_kwDOSF9kNM59SNsm, PRRT_kwDOSF9kNM59SNsy, PRRT_kwDOSF9kNM59SNs2) addressed in this commit. --- ...sign-input-to-lucent-ksk-adr-2026-04-23.md | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md index b9ec9562..6d871e46 100644 --- a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md +++ b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md @@ -117,28 +117,52 @@ From the oracle-scoring design (PR #266): > + forensic-friendly + closes the parameter-fitting- > adversary cost delta. -So the v0 input set extends to 8 fields. +So the v0 input set extends to 9 fields. --- ## Proposed v0 scheme (design input for lucent-ksk ADR) -### Hash input set (8 fields) +### Hash input set (9 fields) ```text h_r = BLAKE3( - hash_version - ∥ h_inputs - ∥ h_actions - ∥ h_outputs - ∥ budget_id - ∥ policy_version - ∥ parameter_file_sha - ∥ approval_set_commitment - ∥ node_id + encode(hash_version) + ∥ encode(h_inputs) + ∥ encode(h_actions) + ∥ encode(h_outputs) + ∥ encode(budget_id) + ∥ encode(policy_version) + ∥ encode(parameter_file_sha) + ∥ encode(approval_set_commitment) + ∥ encode(node_id) ) ``` +**Canonical encoding (`encode(·)`).** Raw concatenation of +variable-length fields is ambiguous and opens a length- +extension / boundary-shift adversary surface (an attacker +could partition `"AB" ∥ "CD"` and `"A" ∥ "BCD"` to the same +input bytes). v0 binds an explicit canonical encoding for +each field, in the order listed: + +- `hash_version`: 1-byte unsigned integer (versions + `0x00`-`0xFF` reserved; `0x01` = this scheme). +- `h_inputs` / `h_actions` / `h_outputs` / `parameter_file_sha` + / `approval_set_commitment`: 32-byte fixed-width BLAKE3 + digests (no length prefix needed — every value is exactly + 32 bytes). +- `budget_id` / `policy_version` / `node_id`: variable-length + identifiers encoded as `len:u32-be ∥ bytes` length-prefix + framing. The 4-byte big-endian length disambiguates + boundaries unambiguously. + +This is the v0 binding. Future schemes (`hash_version >= +0x02`) may pick different framing (e.g. CBOR, Protobuf, or +domain-separated TLV per RFC 8949 §3.1), and the version +prefix tells verifiers which framing applies — so the +binding is forward-compatible. + Changes from Amara's 7th-ferry proposal: 1. **Add `hash_version` prefix** — enables @@ -233,7 +257,7 @@ scope-wise): the hash. - Residual risk: if the `hash_version` registry itself is compromised (bad actor registers a weak algorithm as - v0x02), the scheme is broken. Mitigation: the registry + `0x02`), the scheme is broken. Mitigation: the registry is a lucent-ksk governance artifact, not a per-node config; modifying it requires a governance-layer decision. Parallel to the `parameter_file_sha` rule. @@ -282,7 +306,7 @@ In order of leverage (same pattern as oracle-scoring v0): form-factor if lucent-ksk doesn't have a `docs/ DECISIONS/` pattern yet. 3. **`hash_version` registry landing in lucent-ksk** — - governance artifact; first v0x01 entry. + governance artifact; first `0x01` entry. 4. **`parameter_file_sha` registry landing** — parallel governance artifact; binds Zeta-module parameter files to specific SHAs. From acbb27f1cfb2b10b70b71dae678fcada29b2878e Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sat, 25 Apr 2026 00:14:05 -0400 Subject: [PATCH 3/7] =?UTF-8?q?drain(#268=20lint):=20MD032=20=E2=80=94=20l?= =?UTF-8?q?ine-leading=20+=20interpreted=20as=20list=20bullet=20(wrap=20fi?= =?UTF-8?q?x)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md index 6d871e46..c269af66 100644 --- a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md +++ b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md @@ -22,8 +22,8 @@ specific scheme. The actual adoption decision lives in lucent-ksk via ADR whenever that repo's substrate work reaches the relevant stage. -**Non-fusion disclaimer:** Amara proposing BLAKE3 + Ed25519 -+ field-binding, Aminata critiquing the proposal's +**Non-fusion disclaimer:** Amara proposing BLAKE3 + +Ed25519 + field-binding, Aminata critiquing the proposal's leak/rotation gaps, and Otto synthesising a v0 that addresses both is not evidence of merged identity. Independent pass + adversarial pass + synthesis is three @@ -113,8 +113,8 @@ From the oracle-scoring design (PR #266): > hash (modifying the Amara BLAKE3 proposal to bind > `parameter_file_sha` alongside `policy_version`). > Every receipt carries proof of which parameters were -> in force at the time of the decision — replay-friendly -> + forensic-friendly + closes the parameter-fitting- +> in force at the time of the decision — replay-friendly + +> forensic-friendly + closes the parameter-fitting- > adversary cost delta. So the v0 input set extends to 9 fields. From 6aad1713c6629bfc02a063449ff8d8bdec969576 Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sat, 25 Apr 2026 00:17:28 -0400 Subject: [PATCH 4/7] drain(#268 P1+P1 Codex): replay-determinism on signer view + UTF-8/NFC byte encoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new Codex P1 findings on the BLAKE3 receipt-hashing v0 doc: P1 (line 226) — replay determinism vs current signer set: The req #4 said "compare commitment vs CURRENT signer-view", which makes receipt validity time-dependent — the moment the live signer set rotates, every prior receipt becomes invalid. Replay-determinism breaks. Fix: validate against the signer set authoritative at the receipt's claimed `policy_version` (recoverable from `policy_version` + dispute-process commitment-opening). Receipt-creation-time race-checking is moved to the receipt-creation step; the replay gate catches *forged* commitments only. P1 (line 157) — canonical text-to-byte mapping: The `len:u32-be ∥ bytes` framing for variable-length identifiers (`budget_id`, `policy_version`, `node_id`) specified the framing but not how to derive `bytes` from the identifier string. Added explicit binding: `bytes = NFC-normalised UTF-8 octets` — Unicode Normalization Form C per Unicode Annex #15, then UTF-8 encoded. NFC fixes visually-identical-but-byte-different forms (e.g., precomposed vs decomposed accents); UTF-8 is the canonical text→byte map. EOF --- ...sign-input-to-lucent-ksk-adr-2026-04-23.md | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md index c269af66..51282c51 100644 --- a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md +++ b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md @@ -154,8 +154,12 @@ each field, in the order listed: 32 bytes). - `budget_id` / `policy_version` / `node_id`: variable-length identifiers encoded as `len:u32-be ∥ bytes` length-prefix - framing. The 4-byte big-endian length disambiguates - boundaries unambiguously. + framing, where `bytes = NFC-normalised UTF-8 octets` of the + identifier string (Unicode Normalization Form C per Unicode + Annex #15, then encoded as UTF-8). NFC fixes any visually- + identical-but-byte-different forms; UTF-8 is the canonical + text-to-byte mapping. The 4-byte big-endian length + disambiguates boundaries unambiguously. This is the v0 binding. Future schemes (`hash_version >= 0x02`) may pick different framing (e.g. CBOR, Protobuf, or @@ -222,9 +226,16 @@ scope-wise): cause the same halt-and-report. (Fail-closed on parameter-file unknown.) 4. A receipt with mismatched `approval_set_commitment` - vs. the current signer-view MUST cause the consumer - to reject the receipt as invalid. (Approval-withdrawal - race is caught at replay-time.) + vs. the signer set **that was authoritative at the + receipt's claimed `policy_version`** MUST cause the + consumer to reject the receipt as invalid. The check is + against the historical signer view (recoverable from + `policy_version` + dispute-process commitment-opening), + NOT against the current live signer set — otherwise + replay determinism breaks the moment the signer set + rotates. (Approval-withdrawal race is caught at receipt- + creation time, not at replay; this gate catches forged + commitments at replay.) ## Addressing Aminata's findings From 3cfa4345136dee87ed11fa43777cd97887c1a7e3 Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sat, 25 Apr 2026 00:20:29 -0400 Subject: [PATCH 5/7] drain(#268 P1+P2 Codex): correct adversary terminology + decouple CBOR/TLV citations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1 (line 144) — terminology correction: "length-extension / boundary-shift adversary surface" incorrectly conflated two distinct attacks. BLAKE3 is built on a tree-hash construction with finalisation flags — it is NOT vulnerable to length-extension the way SHA-256 and MD5 are. The actual risk in raw concatenation is boundary-shift / collision-by-reframing only. Updated the wording to name that risk explicitly and added a parenthetical noting that length-extension is NOT a concern with BLAKE3. P2 (line 162) — CBOR vs TLV reference correction: 'domain-separated TLV per RFC 8949 §3.1' conflated two distinct concepts: RFC 8949 is CBOR (tagged data items), and 'domain-separated TLV' is a separate framing concept. Split into two parallel options: 'CBOR per RFC 8949' (one option) and 'a domain-separated TLV scheme' (another, no specific RFC attached because TLV is generic). Future ADR can pick either or define a custom TLV; the v0 doc no longer mis-cites. --- ...sign-input-to-lucent-ksk-adr-2026-04-23.md | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md index 51282c51..e3fdd1fb 100644 --- a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md +++ b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md @@ -140,11 +140,17 @@ h_r = BLAKE3( ``` **Canonical encoding (`encode(·)`).** Raw concatenation of -variable-length fields is ambiguous and opens a length- -extension / boundary-shift adversary surface (an attacker -could partition `"AB" ∥ "CD"` and `"A" ∥ "BCD"` to the same -input bytes). v0 binds an explicit canonical encoding for -each field, in the order listed: +variable-length fields is ambiguous and opens a boundary- +shift / collision-by-reframing adversary surface (an +attacker could partition `"AB" ∥ "CD"` and `"A" ∥ "BCD"` to +the same input bytes; both yield identical hashes despite +representing different field assignments). Note: this is +*not* a BLAKE3 length-extension attack — BLAKE3's tree-hash +construction with finalisation flags is not vulnerable to +length-extension the way SHA-256 / MD5 are. The risk here +is purely the encoding ambiguity at the input layer. v0 +binds an explicit canonical encoding for each field, in the +order listed: - `hash_version`: 1-byte unsigned integer (versions `0x00`-`0xFF` reserved; `0x01` = this scheme). @@ -162,10 +168,10 @@ each field, in the order listed: disambiguates boundaries unambiguously. This is the v0 binding. Future schemes (`hash_version >= -0x02`) may pick different framing (e.g. CBOR, Protobuf, or -domain-separated TLV per RFC 8949 §3.1), and the version -prefix tells verifiers which framing applies — so the -binding is forward-compatible. +0x02`) may pick different framing (e.g. CBOR per RFC 8949, +Protobuf, or a domain-separated TLV scheme), and the +version prefix tells verifiers which framing applies — so +the binding is forward-compatible. Changes from Amara's 7th-ferry proposal: From 7d05d92b6c8d9d33f023a492c0c138a3a48546eb Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sat, 25 Apr 2026 00:30:51 -0400 Subject: [PATCH 6/7] =?UTF-8?q?drain(#268=20P1=C3=973=20Codex):=20version-?= =?UTF-8?q?policy=20gate=20+=20retired-key=20restriction=20+=20signed=20ke?= =?UTF-8?q?y-version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three substantive Codex P1 findings on the v0 receipt-hashing design: P1 (line 229) — version policy gate beyond unknown: Req #2 only fail-closed on unknown hash_version. Updated to also reject DEPRECATED versions per a policy registry (lucent-ksk governance artifact). Prevents forgery under an old-but-still-mechanically-recognised version that was retired due to weakness. Historical receipts remain verifiable for audit; new receipts under deprecated versions are refused. P1 (line 211) — retired key versions: Rotation introduced agent_key_version/node_key_version but didn't restrict NEW receipts from using retired key versions. Added: separate registry of retired key versions blocks creation of new receipts under retired versions; historical receipts under retired versions remain verifiable (replay-determinism preserved) but the signing path refuses to produce more. P1 (line 203) — signed key-version (authenticated metadata): The notation `Sign_{sk, *_key_version}(h_r)` was ambiguous about whether *_key_version was authenticated. If it's unsigned metadata, an attacker can swap the declared version to one that points at a public key for a different signature algorithm. Fix: bind the version INSIDE the signed message (`Sign_{sk}(version ∥ h_r)`) and verify by recomputing the signing input from the declared version. Verification block added showing the explicit lookup + recompute pattern. Also reframed line 120 to make the field-count reasoning explicit (Amara's 7 base + hash_version + parameter_file_sha = 9 v0 fields) so the count claim isn't load-bearing on the preceding paragraph alone. --- ...sign-input-to-lucent-ksk-adr-2026-04-23.md | 59 +++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md index e3fdd1fb..42fcedcf 100644 --- a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md +++ b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md @@ -117,7 +117,13 @@ From the oracle-scoring design (PR #266): > forensic-friendly + closes the parameter-fitting- > adversary cost delta. -So the v0 input set extends to 9 fields. +Combined with Aminata's binding additions named earlier: +Amara's 7th-ferry proposal had 7 base fields (`h_inputs`, +`h_actions`, `h_outputs`, `budget_id`, `policy_version`, +`approval_set_commitment`, `node_id`); v0 adds +`hash_version` (cryptographic-agility prefix) and now +`parameter_file_sha` (oracle-scoring binding above), so the +v0 input set extends to **9 fields total**. --- @@ -198,19 +204,41 @@ Changes from Amara's 7th-ferry proposal: ### Signature structure (rotation-aware) ```text -σ_agent = Sign_{sk_agent, agent_key_version}(h_r) -σ_node = Sign_{sk_node, node_key_version}(h_r) +σ_agent = Sign_{sk_agent}(agent_key_version ∥ h_r) +σ_node = Sign_{sk_node }(node_key_version ∥ h_r) +``` + +The key-version is **inside the signed message** (prepended +to `h_r` before signing) — not unsigned metadata alongside. +This authenticates the version: an attacker cannot strip the +correct version off a receipt and reuse the signature against +a different declared version, because the verifier +recomputes the signing input by binding the declared version +to `h_r` before checking the signature. Verification: + +```text +verify(σ_agent, pk_agent_at(agent_key_version), agent_key_version ∥ h_r) +verify(σ_node, pk_node_at(node_key_version), node_key_version ∥ h_r) ``` Changes: -- **Bind `agent_key_version` + `node_key_version` into - the signature tuple** — enables per-key-rotation without - breaking historical receipts. When an agent rotates - keys, old receipts remain verifiable against the old - key version; new receipts use the new version. +- **Bind `agent_key_version` + `node_key_version` into the + signed message** — enables per-key-rotation without + breaking historical receipts. When an agent rotates keys, + old receipts remain verifiable against the old key version + (because the verifier looks up `pk_agent_at(version)`); + new receipts use the new version. +- **Restrict NEW receipts to non-retired key versions.** A + separate registry of retired key versions (lucent-ksk + governance artifact) blocks creation of new receipts under + retired versions. Historical receipts under retired + versions remain verifiable (replay-determinism) but the + signing path refuses to produce more under the same + retired version. Same shape as `hash_version`'s deprecated- + list (below). - **Signature algorithm is NOT fixed to Ed25519 in this - doc** — the hash_version prefix indicates which + doc** — the `hash_version` prefix indicates which algorithm pair is in use. v0 assumes Ed25519 but the scheme supports later upgrade. @@ -225,8 +253,17 @@ scope-wise): produce the same materialised views byte-for-byte. 2. A receipt with a `hash_version` the consumer doesn't recognise MUST cause the consumer to halt-and-report, - not silently accept or silently reject. (Fail-closed - on algorithm unknown.) + not silently accept or silently reject. (Fail-closed on + algorithm unknown.) Additionally, the consumer MUST + consult a `hash_version` policy registry (lucent-ksk + governance artifact) and reject receipts whose + `hash_version` is *deprecated* — even if recognised. + This prevents an attacker from forging receipts under + an old, weaker scheme that has been retired but is + still mechanically recognised by older verifier + software. Historical receipts under the deprecated + version remain verifiable for audit; new receipts + under it are refused. 3. A receipt with a `parameter_file_sha` that the consumer can't resolve to actual parameter values MUST cause the same halt-and-report. (Fail-closed on From 61a2bd48d1b0bcbf6fc5c1041583694cc1d0d6ec Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sat, 25 Apr 2026 00:36:15 -0400 Subject: [PATCH 7/7] drain(#268 P1+P1 Codex): u32-be encoding for key-version + issuance-epoch gate on deprecated hash_version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two more substantive Codex P1 findings: P1 (line 208) — canonical encoding for key-version: The signature scheme bound *_key_version into the signed message but didn't specify the byte encoding. Added explicit `encode_u32_be` wrapper + an Encoding section: 4-byte big-endian unsigned integer, monotonic from 1, with version 0 reserved for uninitialised. Fixed-width avoids needing a length prefix (every version is exactly 4 bytes). P1 (line 260) — issuance-epoch gate on deprecation: Unconditionally rejecting receipts with deprecated hash_version breaks audit/replay of historical receipts that were valid when issued. Updated to issuance-epoch gate: receipts issued BEFORE the version's deprecation cutoff remain valid for audit; receipts claiming an issuance epoch AFTER the cutoff under that version are rejected. Registry stores (version, deprecated_after_epoch) tuples; verifier compares claimed issuance epoch against deprecation epoch for that version. --- ...sign-input-to-lucent-ksk-adr-2026-04-23.md | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md index 42fcedcf..eb1e0f42 100644 --- a/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md +++ b/docs/research/blake3-receipt-hashing-v0-design-input-to-lucent-ksk-adr-2026-04-23.md @@ -204,10 +204,17 @@ Changes from Amara's 7th-ferry proposal: ### Signature structure (rotation-aware) ```text -σ_agent = Sign_{sk_agent}(agent_key_version ∥ h_r) -σ_node = Sign_{sk_node }(node_key_version ∥ h_r) +σ_agent = Sign_{sk_agent}(encode_u32_be(agent_key_version) ∥ h_r) +σ_node = Sign_{sk_node }(encode_u32_be(node_key_version) ∥ h_r) ``` +**Encoding for `*_key_version`.** The key-version is a 4-byte +big-endian unsigned integer (`u32-be`). Versions number +monotonically from 1; version 0 is reserved for "uninitialised" +and never used in signed receipts. Fixed-width keeps the +prepended bytes unambiguous (no length prefix needed since +every version is exactly 4 bytes). + The key-version is **inside the signed message** (prepended to `h_r` before signing) — not unsigned metadata alongside. This authenticates the version: an attacker cannot strip the @@ -261,9 +268,19 @@ scope-wise): This prevents an attacker from forging receipts under an old, weaker scheme that has been retired but is still mechanically recognised by older verifier - software. Historical receipts under the deprecated - version remain verifiable for audit; new receipts - under it are refused. + software. **Issuance-epoch gate:** the deprecation + policy MUST distinguish receipts by their issuance + epoch, not by the verification timestamp. Receipts + issued *before* a `hash_version` was deprecated remain + valid for audit / replay (replay-determinism preserves + historical receipts under their then-current scheme); + receipts that *claim* an issuance epoch *after* the + deprecation cutoff under a deprecated `hash_version` + are rejected. The lucent-ksk policy registry stores + `(version, deprecated_after_epoch)` tuples; the + verifier checks the receipt's claimed issuance epoch + against the registry's deprecation epoch for that + version. 3. A receipt with a `parameter_file_sha` that the consumer can't resolve to actual parameter values MUST cause the same halt-and-report. (Fail-closed on