Skip to content

fix(release): publish the key nodes enforce, and verify every release signature against it - #276

Merged
Nic-dorman merged 1 commit into
mainfrom
nic/v2-1802-release-key-pub-matches-embedded
Nov 5, 2026
Merged

Nic-dorman merged 1 commit into
mainfrom
nic/v2-1802-release-key-pub-matches-embedded

Conversation

@Nic-dorman

Copy link
Copy Markdown
Member

Linear issue

Closes V2-1802

The release notes tell users to verify downloads with ant-keygen verify --key release-signing-key.pub …. But sign/release-signing-key.pub hasn't been the release key since 2026-03-24:

  • cc34270 (03-23) rotated both the file and the key embedded in src/upgrade/signature.rs.
  • fda30ed (03-24) rotated the embedded key again, and left the file alone.

Nodes verify upgrades against the embedded key, so auto-upgrade was unaffected. Anyone verifying by hand would have seen every release since 03-24 fail.

Confirmed against v0.23.1 on dev2, using ant-keygen 0.1.0 (the latest release, which release.yml downloads) and ant-node's own verify_binary_signature*:

Archive Embedded key sign/release-signing-key.pub on main .pub from this PR
ant-node-cli-linux-x64.tar.gz valid INVALID (ant-keygen exit 1) valid (exit 0)
ant-node-cli-windows-x64.zip valid INVALID valid

The changes:

  • sign/release-signing-key.pub is regenerated byte for byte from RELEASE_SIGNING_KEY: it starts 55 ed f3 e1, sha256 163f903d…6643. The embedded key, which is what nodes trust, is not changed.
  • New unit test test_published_key_file_matches_embedded_key: include_bytes! of the .pub must equal RELEASE_SIGNING_KEY. A rotation now has to change both copies. I kept the Rust array as the source rather than embedding the file with include_bytes!, so that a change to the key nodes trust stays a readable source diff, not "Binary file not shown".
  • release.yml sign job: after signing, it verifies every archive's .sig with ant-keygen verify --key sign/release-signing-key.pub. The release fails on a mismatch, or if there are no archives. With the test above, this also shows nodes will accept the archives. Until now nothing checked the signatures the job produced. The job gets a sparse checkout of sign/ for this.
  • The .pub is attached to each GitHub release, and the notes say it's attached and is the key nodes verify upgrades against. Before, users had to find it in the repo.

Not in scope: V2-1749 (release.yml downloads ant-keygen unpinned; verify-key doesn't verify).

Risk tier

  • T0 — docs / tooling / CI / pure UX-output. Repo CI only.
  • T1 — client-only, no network-facing behavior change. CI + prod compat smoke.
  • T2 — node/client logic with behavioral surface, no protocol/format/economics change. Dev testnet + ADR.
  • T3 — protocol / storage format / payments / routing. T2 evidence + adversarial testing.

T0 because the node's code and its embedded key are unchanged. This changes the published key file, a unit test and the release workflow. The reviewer should confirm, since it sits next to the upgrade mechanism.

Compatibility

  • Wire: none
  • Storage: none
  • API: none

Semver impact

  • breaking
  • feature
  • fix

Test evidence

On dev2 (Linux, stable 1.99.1):

  • Against the real v0.23.1 archives, the table above: the new .pub verifies with both ant-keygen verify (the release.yml command) and ant-node's verify_binary_signature_with_key, and the old one is rejected.
  • cargo fmt --all -- --check, cargo clippy --all-targets --all-features -- -D warnings, RUSTDOCFLAGS=-D warnings cargo doc --all-features --no-deps and cargo check --lib --no-default-features --locked are all clean.
  • cargo test --lib --features test-utils upgrade::signature: 13 passed.
  • Mutation: with main's old .pub restored, test_published_key_file_matches_embedded_key fails with "sign/release-signing-key.pub is not RELEASE_SIGNING_KEY; regenerate the file from the embedded key (or rotate both)".
  • actionlint .github/workflows/release.yml: clean. The new verify step first runs on the next release, or on a dry_run dispatch, which runs sign too.

New dependency

none

ADR

n/a

Mitigation / rollback

Revert the PR. Nodes are unaffected either way, because the embedded key doesn't change. If the new verify step fails a release, it means a signature doesn't match the key nodes enforce. That release shouldn't ship.

🤖 Generated with Claude Code

… signature against it

fda30ed rotated the embedded release key on 2026-03-24 but not
sign/release-signing-key.pub, so every release since has failed to
verify against the key the release notes point users to (confirmed on
v0.23.1). Regenerate the file from RELEASE_SIGNING_KEY, which is
unchanged; add a unit test holding the two equal; have the sign job
verify each .sig against the file and fail the release on a mismatch;
attach the key to each release.

Closes V2-1802

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@MaidSafe-Hermes MaidSafe-Hermes left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE — reviewed head a8321ff (base 5413030). No blocking defects found in the three changed paths. The signing job uses the same archive patterns as its signing loop, fails on an empty archive set or any failed/missing signature, then includes the verified public key in the signed artifact and final release upload. The unit test binds the checked-in key bytes to the key embedded in node verification.

Verification: 2/2 independent seats reviewed (sol-1 gpt-6.1-sol xhigh; glm-1 z-ai/glm-5.3); both seats agree after checking their initial workflow-context questions against the full PR workflow. GitHub CI Gate and test/build jobs succeeded. Security Audit fails on RUSTSEC-2026-0387 (crypto-bigint 0.5.5); Cargo.lock is unchanged by this PR, so this is not introduced here and remains a separate merge gate. The focused local cargo test attempt timed out; no local test pass is claimed.

Non-blocking security note: a public key downloaded only from the same release as the artifact does not independently authenticate the release. Users still need a trusted way to establish that key (for example, compare it with the key in a trusted source checkout or an independently published fingerprint).

@Nic-dorman
Nic-dorman merged commit 261eb32 into main Nov 5, 2026
32 of 39 checks passed
@Nic-dorman
Nic-dorman deleted the nic/v2-1802-release-key-pub-matches-embedded branch November 5, 2026 13:56
grumbach added a commit to grumbach/ant-node that referenced this pull request Nov 6, 2026
Brings in main up to 360d472: OS-assigned ports for e2e test nodes (WithAutonomi#281)
and devnet nodes (WithAutonomi#282), release-key and signature checks (WithAutonomi#276), the
third-party notices and audit-regression CI gates (WithAutonomi#274, WithAutonomi#275), the 10 s
DHT request timeout for test nodes (WithAutonomi#277), and the 0.24.0 release (WithAutonomi#283).

One conflict, in tests/e2e/harness.rs: main removed the network bring-up
retry on a fresh port range together with NETWORK_START_ATTEMPTS and its
`warn` import, and this branch had added two helpers next to them
(POSSESSION_PENALTY_TRUST_RATIO and parseable_single_node_proof). The
resolution keeps both helpers and takes main's removal.
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.

2 participants