Documentation · Releases
Release process: reproducible builds, signing, verification
network/docs/release-process.md · commit e561109 · 2026-10-23
Reproducible node builds
scripts/release/build-node.sh [out-dir] builds minerva-geth so that two builds of the same
commit, with the same Go toolchain (go1.22.12) and C compiler, are byte-identical:
GOFLAGS=-trimpath -buildvcs=false,GOTOOLCHAIN=local,-ldflags "-buildid= ...";- the commit hash and commit date are the only version inputs (no clock);
- cgo objects (blst, secp256k1) are compiled without
-gand with-ffile-prefix-map/-fdebug-prefix-map, so no local path reaches the binary; - the tarball is built with sorted names, numeric uid/gid 0,
SOURCE_DATE_EPOCHfrom the commit andgzip -n.
scripts/release/check-reproducible.sh builds twice into separate directories and fails unless
the SHA-256 of binary and archive match. Evidence (2026-10-20, commit 73a7404, linux/amd64,
go1.22.12, GCC 15):
[minerva-release] reproducible: 0250c223c8b4ffacdda57567913250af7e6c4f2ebe7b22c3e512ac7787700f09 minerva-geth-1.12.22-73a74044-linux-amd64
A release note lists: source commit, Go version, C compiler version, SHA256SUMS, the genesis
hashes per profile (minerva doctor --genesis verifies them), the system-contract code hashes
(contracts/deployments/<profile>/deployments.json), and the test commands that ran.
Signing
SHA256SUMS is signed with an SSH key (ssh-keygen -Y sign, namespace minerva-release) when
MINERVA_RELEASE_KEY is set. Operators verify with
scripts/release/verify-release.sh <dir> allowed_signers, where allowed_signers lists the
maintainers' public keys:
release@minerva namespaces="minerva-release" ssh-ed25519 AAAA...
Why SSH signatures: every operator machine already has ssh-keygen; no GPG keyring, no web of
trust. This is classical signing.
Alongside it, SHA256SUMS.mldsa65.json carries an ML-DSA-65 (FIPS 204) signature over the
same file, context MINERVA_RELEASE_V1, produced with the same library the accounts use:
pnpm --filter @minerva/pq-native-tx release:pq keygen release.seed release.pub # once, offline
MINERVA_RELEASE_PQ_SEED_FILE=release.seed pnpm release:build # signs
MINERVA_RELEASE_PQ_PUBLIC_KEY_FILE=release.pub pnpm release:verify build/release # verifies
The signature file records the algorithm, context, the file's SHA-256 and the signer's public
key; verification trusts only the public key given on the command line and fails on any byte
change of SHA256SUMS (tamper test in the release tooling). Publish release.pub next to the
SSH allowed_signers; a release is valid only when both signatures verify.
Container images, when published, must be referenced by digest (image@sha256:...), never by a
mutable tag.
Networking plan (Phase 4, design only)
What a public Minerva network needs beyond the devnet, in order:
- Bootnodes and ports. Each profile publishes its bootnode enodes in
clients/minerva-node/config/<profile>/bootnodes.txt; nodes open TCP/UDP 30303, keep RPC on127.0.0.1unless fronted by a reverse proxy, andminerva doctorwarns on zero peers outside the devnet. - Node identity. The devp2p node key is the operator's identity today (secp256k1). An
authenticated identity scheme that does not depend on ECDSA is a separate reviewed design: the
candidate is an ML-DSA-65 node certificate bound to the enode id, exchanged in the RLPx
helloas a capability, with peers refusing unauthenticated connections once a fork block passes. - Transport. RLPx key agreement is ECDH over secp256k1. A hybrid ML-KEM-768 + ECDH handshake is the planned upgrade; it is a protocol change for every client and is not scheduled before the account layer ships. Until then, the security claim stays: PQ signatures protect authorization, PoW protects ordering; transport confidentiality is classical.
- Operator communications. Release announcements carry the signed
SHA256SUMS, genesis hashes and the fork schedule;minerva doctor --genesisis the operator's check. Emergency changes are announced with the same signature. - Update verification. Operators verify a release before installing it
(
verify-release.sh), and the node refuses to start on a genesis whose hash differs from the profile's published hash once the profile table ships in the binary.
