feat(seismic-manifest): render and schema-check the network manifest - #269
Merged
samlaf merged 1 commit intoAug 27, 2026
Merged
Conversation
Closes SEI-211. The deploy CLI re-implemented the manifest's canonical rendering and its `network_id` in Python, kept byte-for-byte in step with the Rust parser by a shared fixture and golden vectors on both sides. Two implementations of one security identifier is a standing split-brain risk, and the lockstep was held by hand. `seismic-manifest` is the one implementation: a library with a thin CLI over the `seismic-network-manifest` schema crate. `render` takes a manifest document in any JSON formatting and writes the canonical `network-manifest.json` bytes; `parse` puts an existing manifest through the same strict v1 parser every node reads it with and answers with its exit code, writing nothing. Non-Rust callers shell out to the binary; Rust deploy tooling links the library. The emitter lives apart from the schema crate on purpose. That crate implements `Deserialize` only, so no node build has a code path that re-serializes the file, and any re-rendering risks changing the bytes and therefore the `network_id`. A feature gate would not hold that line, since cargo unifies features across a `--workspace` build. Two things the renderer does deliberately: - It sorts keys itself rather than leaning on `serde_json::Map`. Which order that map yields is set by `preserve_order`, a non-additive feature any crate in the graph can turn on — `dcap-qvl` does, so a `--workspace` build gets insertion order while a standalone `cargo install` gets sorted. Sorting explicitly keeps the bytes, and the `network_id`, independent of how the tool was built. - It destructures `NetworkManifestV1` field by field, so a later schema change fails to compile until the rendering is updated with it. `network_id` is not part of the tool's output. It is `sha256` of the file bytes with no canonicalization step, so unlike the rendering and the schema it has nothing that could drift between implementations.
samlaf
deleted the
sl/sei-211-feat-rust-manifest-tool-reusing-the-schema-crate
branch
August 27, 2026 17:02
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 SEI-211.
The deploy CLI re-implemented the manifest's canonical rendering and its
network_idin Python, kept byte-for-byte in step with the Rust parser by a shared fixture and golden vectors on both sides. Two implementations of one security identifier is a standing split-brain risk, and the lockstep was held by hand.seismic-manifestis the one implementation: a library with a thin CLI over theseismic-network-manifestschema crate.rendertakes a manifest document in any JSON formatting and writes the canonicalnetwork-manifest.jsonbytes;parseputs an existing manifest through the same strict v1 parser every node reads it with and answers with its exit code, writing nothing. Non-Rust callers shell out to the binary; Rust deploy tooling links the library.The emitter lives apart from the schema crate on purpose. That crate implements
Deserializeonly, so no node build has a code path that re-serializes the file, and any re-rendering risks changing the bytes and therefore thenetwork_id. A feature gate would not hold that line, since cargo unifies features across a--workspacebuild.Two things the renderer does deliberately:
serde_json::Map. Which order that map yields is set bypreserve_order, a non-additive feature any crate in the graph can turn on —dcap-qvldoes, so a--workspacebuild gets insertion order while a standalonecargo installgets sorted. Sorting explicitly keeps the bytes, and thenetwork_id, independent of how the tool was built.NetworkManifestV1field by field, so a later schema change fails to compile until the rendering is updated with it.network_idis not part of the tool's output. It issha256of the file bytes with no canonicalization step, so unlike the rendering and the schema it has nothing that could drift between implementations.