feat(tee)!: pin the summit genesis via config_digest; deliver it through tdx-init - #87
Merged
Conversation
…ugh tdx-init Deploy-side half of the lockstep wire break landed in SeismicSystems/enclave@2b96dc9b60 (#230), which edited the v1 manifest schema in place and made tdx-init's config POST require the summit genesis. This turns the cross-repo fixture test (test_render_matches_enclave_fixture_bytes) green again. ## Manifest schema + emitter - `summit.genesis_template_hash` -> `summit.genesis_config_digest`: summit's own config digest (SHA-256 over its domain-prefixed SSZ of the complete genesis), computed by shelling out to `summit genesis digest` — the one implementation of the SSZ layout, and the same value summit derives its P2P/signing domains from. Injectable `digest_fn` for tests, mirroring `genesis_hash_fn`/`compile_fn`; new `--summit-bin` on assemble/validate. - `genesis_nonce` is gone. Its clone-uniqueness job is covered by the digest once it pins per-founding TEE-born validator keys; until the harvest flow lands, assemble ships `validators = []`, so distinct test cohorts must vary their inputs (chain_id, namespace) — READMEs say so. - Pinned network_id vector re-derived: 0x8ef142e3f2bf15f8b201c4d8cda7848a9e846222c62b5615d4d36c7fccd98a24 (same vector the enclave crate asserts). ## Artifact set The shipped summit artifact is now the *complete* genesis a node boots from, not a parameters template: assemble fills `eth_genesis_hash` (derived from the injected reth genesis) and a `validators` set into the authored input and ships it as `summit-genesis.toml`. The authored input is renamed to `inputs/summit-genesis.toml` — same-basename convention as reth-genesis.json, with `inputs/` alone marking provenance ("-template" also collided with summit's different notion of a template, which must fully parse as `Genesis`). ## Config delivery (node + cohort configure) `[network].summit_genesis_base64` joins the POST (required by tdx-init; old deploy CLI -> new image 400s "missing field", new CLI -> old image 400s "unknown field" — intentional hard break, same precedent as `[root_key]`). New `--summit-genesis` flag defaulting to `summit-genesis.toml` beside the manifest; client-side structural pre-check (TOML + namespace) mirrors tdx-init's POST-time validation so bad input fails fast. ## Genesis ceremony Follows summit's CLI move (SeismicSystems/summit@633fc2e, #449): the standalone `genesis` binary is retired, so the ceremony uses `summit genesis set-validators` and verifies its input against the manifest's `summit.genesis_config_digest` via `summit genesis digest` (replacing the SHA-256 template-hash check). `--summit-template` -> `--summit-genesis`, defaulting to the shipped artifact copy. ## networks/example-devnet Regenerated end-to-end with the real toolchain (summit digest, reth genesis-hash, admission compile); new network_id 0xb0951428... — it's a template identity, no cohort runs under it. The network-dir.png diagram still shows the old artifact names and needs an excalidraw re-render. The founding pipeline inversion (harvest TEE-born keys -> fill validators *before* assemble, retiring the post-boot ceremony POST) comes with the harvest work; this commit keeps the transitional ceremony flow working.
samlaf
added a commit
to SeismicSystems/seismic-images
that referenced
this pull request
Aug 5, 2026
Images half of the founding reorder: summit's validator keys are now born before the network manifest exists, so deploy's harvest can pin them into network_id before any node has an identity, and the summit genesis arrives through the config POST instead of a ceremony. Bump enclave c9e9543 -> e5e2a66, picking up SeismicSystems/enclave#229 through SeismicSystems/enclave#233: the founding-keys binding, tdx-init's summit-genesis delivery + config_digest manifest pin, the verify-quote CLI, and the summit-key-holder binary (built on the batched enclave cargo line). Note the bumped tdx-init 400s any config POST without summit_genesis_base64; deploy already sends it (SeismicSystems/deploy#87). - New summit-key-holder.service: starts at network-online parallel to tdx-init's config wait, as the summit user. Holds the summit keys in RAM and serves {pubkeys, quote} on :7879 for the founding harvest — the quote window closes when the network manifest lands and re-opens every boot (tmpfs conf dir), so :7879 stays operator-CIDR-only in the NSG permanently. Its persist control socket lives in a RuntimeDirectory=: private to the summit user, deliberately not one of seismic-runtime.conf's cross-user grants. - summit.service: `summit keys generate -n` is replaced by `summit-key-holder persist-wait`, which blocks until the holder has written the keystore (first boot) or confirmed it decodable (reboot) and exits nonzero otherwise, so summit never starts on keys the manifest never pinned. The old line must not survive as a fallback: `-n` exits 0 on existing keys, so a race would silently mint unpinned keys. --genesis-path now reads /run/seismic/conf/summit-genesis.toml, written by tdx-init each boot from the POST's summit_genesis_base64. - TPM access moves from the `attestation` group to a dedicated `tpm` group on /dev/tpm*; attestation and summit (harvest-quote minting) join it in mkosi.postinst. - seismic-runtime.conf's header now states the file's contract — every line is a cross-user access grant, service-private runtime dirs belong to their units — and the conf-dir comment lists everything tdx-init fans out. readme.md documents the new service, the boot-chain shape, and the widened TPM-consumer list; CI's expected-initrd-files check covers the new unit and binary.
samlaf
added a commit
that referenced
this pull request
Aug 7, 2026
…91) The founding reorder's assemble half: `manifest assemble` now derives the founding validator set from the harvested cohort instead of shipping the transitional empty set #87 left behind. With this the founding order is whole — init → up → harvest → assemble → configure — and `network_id` pins the founders' keys by construction. ## What assemble does now - Pairs each harvested box (`inputs/harvest/<node>.json`) with an authored withdrawal-credentials address and the box's current IP from `nodes/<node>.json`, and emits the completed `[[validators]]` through `summit genesis set-validators`. The empty-set prepend is gone. - Re-verifies every archived quote against the policy compiled from `inputs/measurements.json` before pinning anything. This is the second of the two gates: root_key admission gates privacy, assemble-time DCAP gates consensus membership — summit never talks to the custodian, so an unverified founding pubkey could otherwise vote from outside a TEE. - Refuses a harvest carrying a repeated node or consensus pubkey: summit keys validator accounts by node pubkey, so a duplicate silently collapses the set, and a shared consensus key is accidental-equivocation material. ## Withdrawal credentials are a list, not a node-name map `inputs/founder-withdrawal-credentials.json` is now a JSON array of addresses, one per founding node, paired to the boxes in node-name order at assemble time. Keying it by cohort node name forced the file to be authored after `up`, which is backwards — the founders' addresses are a fact about the founders, not about the infrastructure. `manifest init` scaffolds it as the fourth authored input, and `--founders N` fills in `0x00…0<i>` placeholders, which is all a throwaway founding needs. The count check that replaces the exact name match runs twice: at harvest before any quote is fetched, and again at assemble. The pairing is positional and nothing downstream can tell a swapped pair from an intended one, so assemble logs each pairing as it builds the set. ## up - `--count` is optional with `--network`: the authored credentials are the founding set, so they size the cohort. A contradicting `--count` is an error rather than a silent override. - The preflight image-pin check reads the authored `inputs/measurements.json` rather than the assembled manifest and policy. Provisioning precedes assembly now, so at `up` time the inputs are all a network directory holds. The success banner walks harvest → assemble → configure instead of pointing at the ceremony. ## Renames, docs, and the throwaway path - `measurement-policy.json` → `measurement-policy-bootstrap.json`: the file is only the *founding* allowlist — what `bootstrap_policy_hash` pins and registry genesis storage compiles from. The live policy is the registry contract's state, which the authority can mutate after genesis. - `manifest init --force` re-authors inputs for a re-found, matching the `assemble --force` that already existed. - `tee/docs/runbook-devnet.md` is the copy-pasteable throwaway recipe (init → provision → harvest → assemble → configure → smoke-test → down), and `networks/.gitignore` ignores `tmp-*/` wholesale so a scratch founding never dirties git. - `example-devnet` is documented as a schema example, not a runnable founding: no artifact set can be assembled without a live harvest. - Every path the CLIs print is absolute, so it resolves from any cwd. The seismic_node Pulumi program path and its default stack config move to `tee/cli/common/repo.py`, letting `manifest init` point the founder at the stack config to review (vhd_blob_url, operator_ip_cidr) without common importing the network CLI. - The dev stack's `vhd_blob_url` moves to a 2026-08-07 image, built after the summit-key-holder unit landed (seismic-images #48). Breaking for any existing network directory: the policy filename and the credentials file shape both changed, and `up --network` now expects `inputs/`. Nothing is founded on the old layout. Tests stay offline throughout (verifier subprocess and summit emitter mocked). Next: configure's per-boot IP splice, the launch-time pubkey and block-0 assertions, and deleting the genesis ceremony.
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.
Deploy-side half of the lockstep wire break landed in SeismicSystems/enclave@2b96dc9b60 (#230), which edited the v1 manifest schema in place and made tdx-init's config POST require the summit genesis. This turns the cross-repo fixture test (test_render_matches_enclave_fixture_bytes) green again.
Manifest schema + emitter
summit.genesis_template_hash->summit.genesis_config_digest: summit's own config digest (SHA-256 over its domain-prefixed SSZ of the complete genesis), computed by shelling out tosummit genesis digest— the one implementation of the SSZ layout, and the same value summit derives its P2P/signing domains from. Injectabledigest_fnfor tests, mirroringgenesis_hash_fn/compile_fn; new--summit-binon assemble/validate.genesis_nonceis gone. Its clone-uniqueness job is covered by the digest once it pins per-founding TEE-born validator keys; until the harvest flow lands, assemble shipsvalidators = [], so distinct test cohorts must vary their inputs (chain_id, namespace) — READMEs say so.Artifact set
The shipped summit artifact is now the complete genesis a node boots from, not a parameters template: assemble fills
eth_genesis_hash(derived from the injected reth genesis) and avalidatorsset into the authored input and ships it assummit-genesis.toml. The authored input is renamed toinputs/summit-genesis.toml— same-basename convention as reth-genesis.json, withinputs/alone marking provenance ("-template" also collided with summit's different notion of a template, which must fully parse asGenesis).Config delivery (node + cohort configure)
[network].summit_genesis_base64joins the POST (required by tdx-init; old deploy CLI -> new image 400s "missing field", new CLI -> old image 400s "unknown field" — intentional hard break, same precedent as[root_key]). New--summit-genesisflag defaulting tosummit-genesis.tomlbeside the manifest; client-side structural pre-check (TOML + namespace) mirrors tdx-init's POST-time validation so bad input fails fast.Genesis ceremony
Follows summit's CLI move
(SeismicSystems/summit@633fc2e, #449): the standalone
genesisbinary is retired, so the ceremony usessummit genesis set-validatorsand verifies its input against the manifest'ssummit.genesis_config_digestviasummit genesis digest(replacing the SHA-256 template-hash check).--summit-template->--summit-genesis, defaulting to the shipped artifact copy.networks/example-devnet
Regenerated end-to-end with the real toolchain (summit digest, reth genesis-hash, admission compile); new network_id 0xb0951428... — it's a template identity, no cohort runs under it. The network-dir.png diagram still shows the old artifact names and needs an excalidraw re-render.
The founding pipeline inversion (harvest TEE-born keys -> fill validators before assemble, retiring the post-boot ceremony POST) comes with the harvest work; this commit keeps the transitional ceremony flow working.