git / moonbite-network / deploy/RELEASE-SIGNING.md · raw
# Release signing & checksum integrityFixes audit findings **B2** (stale/mismatched published checksums) and **B3/B5**(installers run unverified binaries as root). The download channel is threat-modelasset #3 — a compromise here is everyone's keys at once — so binary trust must be**authenticity** (a signature you pin), not just a checksum sitting next to the file.This repo now ships the *machinery*. Two things only a human can do remain, and thechannel is not trustworthy until both are done:1. **Generate and hold the signing key** (offline, passphrase-protected).2. **Decide the canonical binary set** and stamp its real hashes.---## What is already wired| Piece | File | Behaviour ||---|---|---|| Checksum manifest is generated from real artifacts | `deploy/miner/build-windows-bundle.py` | writes `SHA256SUMS.txt` (zip + members); prints the `minisign -Sm` next step; `--stage` copies zip **+ sums + .minisig** together and **refuses to stage unsigned** once a key is configured || Drift guard | `deploy/verify-release-checksums.py` | re-hashes everything referenced by `release/SHA256SUMS.txt` and `website/downloads/README.txt`; fails on mismatch or two same-named builds that disagree || CI gate | `.github/workflows/ci.yml` | runs the drift guard on every push/PR (**green** as of 2026-10-03: the stale beta `.exe` rows were removed and the committed manifest is Linux-only) || Publish signs the manifest | `deploy/publish-node-binaries.sh` | step [6/6] runs `minisign -Sm` when `moonbite-release.key` is present, emits `SHA256SUMS.txt.minisig`, prints the `gh release`/`setup-node.sh` commands with the signature asset and `MOONBITE_SUMS_URL` wired in; fails loud (never silently unsigned) when no key || Node installer verification | `deploy/setup-node.sh` | fetches to a staging dir, `mb_verify`s against the signed manifest, installs only on pass; refuses if a key is set but `MOONBITE_SUMS_URL` is unset || Seed installer verification | `deploy/setup-seed2.sh` | verifies the tarball **before** extracting/running as root; key pinned inline (`RELEASE_PUBKEY`) because it is `curl\|bash`'d || Pinned public key | `deploy/moonbite-release.pub` | placeholder; installers run sha256-only + loud warning until replaced |Fail-closed rule: once a real key is present, a **missing or bad signature aborts theinstall**. It never silently downgrades.**Fail-closed by DEFAULT (2026-10-03).** Even with no key yet, the networkinstallers (`setup-node.sh`, `setup-seed2.sh`) now **refuse** to install afetched binary that has no verification at all, rather than warning andinstalling it. The only way to install a fully-unverified network binary is anexplicit, loud operator override, `MOONBITE_ALLOW_UNVERIFIED=1`, intended solelyfor a build you produced yourself; local-path installs (you hand the script thebinary) stay trusted. So the silent "curl a binary, run it as root" backdoor isshut for everyone today; completing the key ceremony upgrades the remainingsha256-only path (manifest reachable, no key) to full signature verification.---## 1. Key ceremony (do once, on an offline machine)```bash# Offline box. minisign: https://jedisct1.github.io/minisign/minisign -G -p moonbite-release.pub -s moonbite-release.key```- `moonbite-release.key` — **secret**. Keep it offline, passphrase-protected. **Never commit it.** (`*.key` is git-ignored; confirm.)- `moonbite-release.pub` — the public key line (starts `RW…`). Publish it widely so users pin it independently of any one origin: 1. paste it into `deploy/moonbite-release.pub`, replacing `REPLACE_ME…`; 2. paste the **same** line into `deploy/setup-seed2.sh` at the `RELEASE_PUBKEY=` marker (grep `RELEASE_PUBKEY`); 3. add it to the site (`website/`, the download page) and the top-level `README`.Rotated/compromised key? Publish a new pubkey in all the above and re-sign; revoke by announcement (minisign has no revocation).---## 2. Decide the canonical binaries (audit B2 — partly done 2026-10-03)Already done (2026-10-03 hardening):- The **stale pre-relaunch (Sep 3) Windows `.exe`** were removed from `release/bin` and dropped from `release/SHA256SUMS.txt`, so the drift guard passes (incl. `--strict`) and no `.exe` is committed with a hash that no longer matches.- `website/downloads/README.txt` no longer pastes a drift-prone hash — it tells users to verify against the `SHA256SUMS.txt` shipped with each download / on the GitHub release (and its `.minisig`, once signing is live).- `release/dist/` and `release/miner/*.exe` are git-ignored so a second divergent binary can't be committed next to `release/bin` again.Still your call (needs the build machine; cannot be done from this checkout):- The **Linux** binaries still in `release/bin` (`moonbited`, `moonbite-cli`, …) are also **Sep 3 beta**. Rebuild the relaunch binaries from a tagged `moonbite-core` commit (reproducible, not the dev machine), refresh `release/bin` + its manifest rows, or stop committing binaries entirely and rely on the publish-time manifests.- Produce the **canonical Windows build** (`release/miner/*.exe` is the current dev-built, unsigned staging set that `build-windows-bundle.py` packages). Confirm it is the real relaunch build, then sign and ship it via the runbook below.---## 3. Build, sign, stamp, stage (every release)```bash# 1. Put the canonical binaries in release/miner/ (windows) and the linux build dir.# 2. Build the bundle + machine-readable manifest:python deploy/miner/build-windows-bundle.py # writes release/dist/{zip,SHA256SUMS.txt}# 3. Sign the manifest with the offline key:minisign -Sm release/dist/SHA256SUMS.txt # -> SHA256SUMS.txt.minisig# (do the same for the linux SHA256SUMS-linux.txt)# 4. Update the human-facing references to the REAL hashes:# - release/SHA256SUMS.txt (canonical set only; drop the stale beta rows)# - website/downloads/README.txt (the "verify what you downloaded" block)# - release/README.md (fix the genesis line if it still says 3d053c59)# 5. Prove they match before publishing:python deploy/verify-release-checksums.py --strict # must exit 0# 6. Stage (copies zip + sums + .minisig into the served download dir):python deploy/miner/build-windows-bundle.py --stage# Publish SHA256SUMS-linux.txt(+.minisig) to website/downloads/ so# setup-seed2.sh's SUMS_URL resolves.```Then, and only then, commit the canonical binaries / references. Until step 5 isgreen, CI stays red and nothing should be advertised as verifiable.> The pending untracked `release/miner/moonbited.exe` and `moonbite-cli.exe` should> not be committed or shipped until they are the confirmed canonical build with> signed sums (this runbook). That was flagged in the audit.---## 4. Verify as a user (what the site should tell people)```bashminisign -Vm SHA256SUMS.txt -P RW… # authenticity (paste the published pubkey)sha256sum -c SHA256SUMS.txt # integrity of each file```Replace the current "compare this hash" prose with "verify this signature" — a barechecksum next to the binary proves nothing against an origin swap.