Skip to content

feat(tee)!: pin the summit genesis via config_digest; deliver it through tdx-init - #87

Merged
samlaf merged 1 commit into
mainfrom
manifest-config-digest
Aug 5, 2026
Merged

feat(tee)!: pin the summit genesis via config_digest; deliver it through tdx-init#87
samlaf merged 1 commit into
mainfrom
manifest-config-digest

Conversation

@samlaf

@samlaf samlaf commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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.

…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
samlaf merged commit 79dd4f1 into main Aug 5, 2026
1 check passed
@samlaf
samlaf deleted the manifest-config-digest branch August 5, 2026 18:24
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant