feat(15.0.0)!: FedCode is non_exhaustive + admission is unconstructible unless verified (#274); CC decimals assigned (#275) - #276
Merged
emooreatx merged 1 commit intoSep 6, 2026
Conversation
…le unless verified (#274); CC decimals assigned (#275) CIRISEdge reviewed v14.2.0 during its v20.2.0 adopt, found the design sound, and raised two structural items. Both are right. 1. FedCode grew a public field in a MINOR -- twice. v14.1.0 added owned_nodes; v14.2.0 added ml_dsa_65_pubkey_sha256. Both to a public non-#[non_exhaustive] struct, which is a compile break for every struct-literal constructor: 12 call sites in edge, and the version number said it was safe to take blind. That is exactly the hazard #257 raised and v14.0.0 was cut to fix. But that sweep annotated 31 error ENUMS and ZERO structs -- so the class survived in the half it never looked at, and I shipped it twice through that half. FedCode and OwnedNode are now #[non_exhaustive] with FedCode::new + with_* builders and OwnedNode::new, so a future field is an added method. Scope decided by evidence, not taste: 236 public structs here carry public fields, and blanket-annotating would break destructuring for readers to buy nothing. A downstream sweep asked which are actually CONSTRUCTED by consumers -- FedCode (12) and OwnedNode (5) are the only two. VerifyConfig, FullAttestationRequest, RegisterArgs, AttestationProof: all zero. Result types are read, not built. 2. The commitment check had no enforcing call site. verify_pulled_ml_dsa_65_pubkey is the right primitive in the wrong shape: a free function returning Result<(), _>, so nothing structurally stopped a host from pulling an ML-DSA body and registering it unchecked -- and that failure is SILENT, a registered hybrid whose PQC half came from whoever answered the Pull. New AdmittedHybridKey fixes the class rather than trusting each call site: private fields, one fallible constructor taking the code and the pulled bytes together, so a value exists only on match. A host taking it as its registration input cannot express the unchecked path. Same discipline as Validity::checked. Edge's framing is adopted into FSD-003 because a reader will ask: the commitment proves the ML-DSA half is the one the minter committed to, NOT that one party holds both halves. Nobody cross-signs them at registration and need not -- persist admits hybrid only and every row must verify under both signatures, so joint control is proven at FIRST USE, not registration. Also: bump-version.sh now rewrites intra-workspace sibling pins, which a major bump had left stale by hand at 13.0.0, 14.0.0 and 15.0.0 before automating it. #275: all 7 UNASSIGNED evidence rows carry CC decimals (CIRISConstitution#101); 53 citations resolve, none unassigned. 1491 workspace green, clippy clean, doc clean, guards pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U4djyL5Vx6whagKB7J4586
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
emooreatx
deleted the
fix/274-non-exhaustive-fedcode-and-enforcing-admission
branch
September 6, 2026 16:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #274 and #275.
Thanks for the review — the verdict framing and the DoS analysis were both useful, and both structural items are right.
1.
FedCodegrew a public field in a MINOR — twicev14.1.0 added
owned_nodes; v14.2.0 addedml_dsa_65_pubkey_sha256. Both to a public non-#[non_exhaustive]struct — a compile break for every struct-literal constructor, 12 call sites on your side, and, as you put it, the version number said it was safe to take blind.That is exactly the hazard #257 raised and v14.0.0 was cut to fix. But that sweep annotated 31 error enums and zero structs — so the class survived in the half it never looked at, and I shipped it twice through that half. You caught the follow-through, not a new problem.
FedCodeandOwnedNodeare now#[non_exhaustive], withFedCode::new+with_*builders andOwnedNode::new.Scope decided by evidence, not taste
236 public structs here carry public fields. Blanket-annotating would break destructuring for readers to buy nothing, so I asked which are actually constructed by consumers:
FedCodeOwnedNodeVerifyConfigFullAttestationRequestRegisterArgsAttestationProofThose two are the entire demonstrated hazard. Result types are read, not built.
2. The check had no enforcing call site
You're right that this is the wrong shape, not the wrong primitive. A free function returning
Result<(), _>leaves nothing structurally stopping a host from registering an unchecked pull, and the failure is silent.New
AdmittedHybridKey— private fields, one fallible constructor taking the code and the pulled bytes together, so a value exists only on match:Same discipline as
Validity::checked. Exactly your suggested shape.Your framing, adopted into the FSD
The subtlety you said a reader will ask about is now normative text in §3A.5, close to verbatim — the commitment proves the ML-DSA half is the one the minter committed to, not that one party holds both halves; nobody cross-signs them at registration and need not, since persist admits
hybridonly and every row must verify under both signatures. Joint control is proven at first use, not registration. That's a better articulation than what shipped and it deserved to be in the spec rather than an issue thread.Your DoS read matches mine: a wrong body is detectable locally, immediately, and attributably, and the fedcode door is human-initiated — the spam surface is the unsolicited announce path, which this doesn't create.
Also in this cut
#275 — all 7
UNASSIGNEDevidence rows now carry their CC decimals (CIRISConstitution#101). 53 citations resolve, none unassigned. One note back on the homes, below.bump-version.shnow rewrites intra-workspace sibling pins — a major bump had leftciris-crypto = { version = "N" }stale, fixed by hand at 13.0.0, 14.0.0 and 15.0.0 before I automated it rather than doing it a fourth time.1491 workspace green, clippy clean, doc clean, guards + self-test pass.
🤖 Generated with Claude Code
https://claude.ai/code/session_01U4djyL5Vx6whagKB7J4586