Skip to content

feat(attestation-service): serve evidence for the operator's deploy verification - #259

Merged
samlaf merged 1 commit into
seismicfrom
sl/sei-181-feat-deploy-preflight-attestation-evidence-rpc
Aug 19, 2026
Merged

feat(attestation-service): serve evidence for the operator's deploy verification#259
samlaf merged 1 commit into
seismicfrom
sl/sei-181-feat-deploy-preflight-attestation-evidence-rpc

Conversation

@samlaf

@samlaf samlaf commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Fixes SEI-181

An operator who has just provisioned a node holds only a liveness signal: :7878 opened. Everything they do next — publishing the node's address, handing it to later nodes as a bootnode — trusts that the endpoint is their node, and nothing yet proves it. A recycled IP, a clone, or a node booted with the wrong manifest all pass a port check, and the one evidence RPC already served is no help here: tx-io evidence is a deliberately timeless, cacheable document, so a replayed copy of it verifies perfectly at an impostor endpoint.

getDeployVerificationEvidence upgrades the signal to an attested one. The caller sends a fresh deployment_nonce; the service derives deploy_verification_binding(network_id, deployment_nonce) from its own manifest-derived identity and quotes over it. The verifier recomputes the binding from its own manifest copy and the nonce it sent, then verifies the envelope against its measurement policy. A passing quote proves a measured node holding the expected manifest minted evidence for this exact request. The caller contributes freshness and nothing else, so the method cannot be used as an oracle to quote transcripts of the caller's choosing.

The binding helper existed, tested but unused, as
deploy_preflight_binding(network_id, nonce, context). Both halves of that name change here. The context tail is dropped: this RPC is its only caller, the question it answers is fixed, and a future flow that needs to bind something else gets its own domain string rather than a context value — separate domains are what keep an answer to one question from passing as an answer to another. The digest is unchanged by the removal (SHA256(x || "") is SHA256(x)); the pinned vector moves only because the old one exercised a non-empty context. "Verification" replaces "preflight" because the check can only succeed after the node is fully up — a joiner binds :7878 only once the root-key handshake has completed — so what it precedes is the operator's reliance, not the node's flight. Membership itself is granted by that handshake and its admission policy, never by this check; the RPC guards the operator's own decisions.

The live-TEE suite gains test_deploy_verification_relying_party: the verifier's full path against a genesis pair, and rejection of the same evidence against another request's nonce and against another network's identity.

The deploy-side verifier that consumes this is SEI-183.

…erification

Fixes SEI-181

An operator who has just provisioned a node holds only a liveness
signal: :7878 opened. Everything they do next — publishing the node's
address, handing it to later nodes as a bootnode — trusts that the
endpoint is their node, and nothing yet proves it. A recycled IP, a
clone, or a node booted with the wrong manifest all pass a port check,
and the one evidence RPC already served is no help here: tx-io evidence
is a deliberately timeless, cacheable document, so a replayed copy of
it verifies perfectly at an impostor endpoint.

getDeployVerificationEvidence upgrades the signal to an attested one.
The caller sends a fresh deployment_nonce; the service derives
deploy_verification_binding(network_id, deployment_nonce) from its own
manifest-derived identity and quotes over it. The verifier recomputes
the binding from its own manifest copy and the nonce it sent, then
verifies the envelope against its measurement policy. A passing quote
proves a measured node holding the expected manifest minted evidence
for this exact request. The caller contributes freshness and nothing
else, so the method cannot be used as an oracle to quote transcripts
of the caller's choosing.

The binding helper existed, tested but unused, as
deploy_preflight_binding(network_id, nonce, context). Both halves of
that name change here. The context tail is dropped: this RPC is its
only caller, the question it answers is fixed, and a future flow that
needs to bind something else gets its own domain string rather than a
context value — separate domains are what keep an answer to one
question from passing as an answer to another. The digest is unchanged
by the removal (SHA256(x || "") is SHA256(x)); the pinned vector moves
only because the old one exercised a non-empty context. "Verification"
replaces "preflight" because the check can only succeed after the node
is fully up — a joiner binds :7878 only once the root-key handshake has
completed — so what it precedes is the operator's reliance, not the
node's flight. Membership itself is granted by that handshake and its
admission policy, never by this check; the RPC guards the operator's
own decisions.

The live-TEE suite gains test_deploy_verification_relying_party: the
verifier's full path against a genesis pair, and rejection of the same
evidence against another request's nonce and against another network's
identity.

The deploy-side verifier that consumes this is SEI-183.
@samlaf
samlaf requested a review from cdrappi as a code owner August 19, 2026 17:25
@linear-code

linear-code Bot commented Aug 19, 2026

Copy link
Copy Markdown

SEI-181

@samlaf
samlaf merged commit 23d53ca into seismic Aug 19, 2026
5 checks passed
@samlaf
samlaf deleted the sl/sei-181-feat-deploy-preflight-attestation-evidence-rpc branch August 19, 2026 17:34
samlaf added a commit to SeismicSystems/seismic that referenced this pull request Aug 19, 2026
Part of SEI-181.
Related to SeismicSystems/enclave#259

The attestation service's binding list gains
getDeployVerificationEvidence(deployment_nonce), answering with
deploy_verification_binding(network_id, deployment_nonce): the caller
contributes only freshness. The deploy-verification paragraph now also
states the check's boundary — it guards the operator's own decisions
(publishing the node's address, handing it to later nodes as a
bootnode); membership is granted by the attested root-key handshake and
its admission policy, never by this check.
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