← board

Verifiable releases: checksums + signatures + the reproducible-build claim

Why (this is the REAL anti-impersonation defense)

A clone of the website harms nobody by existing. What harms people is a trojaned pxx binary served from a page that looks like ours. Source secrecy does nothing against that — verifiability does:

The claim only we can make

We have a byte-identical self-host fixed-point gate. That means we can credibly say: build it yourself and get the same bytes. A reproducible build is an impersonation defense no clone can match, and it is nearly free for us — the gate already proves determinism. Nobody shipping a compiler gets to say this as easily as we do; say it loudly on the download page.

Scope

  1. SHA256SUMS for every release artifact, generated by the release job, committed/attached to the GitHub Release.
  2. Sign the checksum file (minisign or GPG; minisign is one line and matches the age philosophy — small key, printable, no GPG swamp). Publish the public key in the repo AND on the site.
  3. Reproducible-build doc: exact steps to rebuild a release from source and diff the bytes against the published artifact. Wire it to the existing self-host fixed-point machinery.
  4. Download page copy (Track W/D): canonical URL, the checksums, the "verify it yourself" instructions, the public key. Make verification the default path, not a footnote.
  5. Installer/bootstrap scripts fetch from the canonical GitHub Releases URL and verify the checksum before executing anything.

Acceptance

A published release carries SHA256SUMS + a valid signature; the documented rebuild procedure reproduces the artifact byte-for-byte on a clean machine; the download page shows the verify steps.

Log


UNBLOCKED 2026-08-25 — steps 1-3 are agent work; only the signature waits

[[decide-release-signing-key-custody]] was answered in part on 2026-08-25 (see devdocs/progress/decided/README-agent-decisions.md). The blocked-by edge is removed because it was gating the whole ticket on the one step that needs a human.

Do, in this order:

  1. SHA256SUMS for the published tarball. Today MANIFEST.sha256 covers the prebuilt binaries, so a downloader can only verify after extracting. No key involved.
  2. The reproducible-build doc, wired to the existing selfcheck.sh — which already rebuilds each binary on the downloader's host and diffs against the manifest. The claim is machinery, not copy; say so.
  3. Download-page copy (Track W/D). Claims discipline applies: this is reproducibility of our own build. Write "rebuilds byte-for-byte from source on your host and matches the published manifest" — never anything that could be read as a gcc comparison. See CLAIMS in CLAUDE.md.
  4. The minisign signature step — BLOCKED ON THE OWNER. The tool is preselected (minisign); what is missing is who holds the private key and where it lives. Do not generate a key to unblock this, and do not substitute another tool to route around it.

Steps 1-3 carry most of the anti-impersonation value the ticket argues for: a clone site cannot make its binary match a hash published in a repo it does not control, and cannot make it rebuild byte-for-byte at all.


Steps 1-3 done (frankS, 2026-08-31). Step 4 is still the owner's.

1. SHA256SUMS over the published tarball — done

tools/release.sh writes dist/SHA256SUMS over pxx-<tag>.tar.gz right after the archive is created, and gh release create now attaches it alongside the tarball and MANIFEST.sha256. Until now the only published hash was MANIFEST.sha256, which lives inside the bundle — so verifying anything meant first unpacking the thing you had not verified.

The two files answer two different questions and both ship:

file question where
SHA256SUMS is this the archive we published? Release asset, checkable before extracting
MANIFEST.sha256 do these binaries rebuild here? inside the bundle, driven by selfcheck.sh

Measured, not assumedbuild_dist run for real (all four host targets):

==> build: dist/pxx-v0.0.0-test + pxx-v0.0.0-test.tar.gz + SHA256SUMS
    72ad97c43a7e...  pxx-v0.0.0-test.tar.gz
$ sha256sum -c SHA256SUMS
pxx-v0.0.0-test.tar.gz: OK

Negative control, because a checksum that cannot fail is not a checksum: appending one byte to the tarball makes sha256sum -c report FAILED, and restoring it makes it pass again. Run.

2. The reproducible-build doc — done, and one claim deliberately NOT made

In the generated RELEASE.md (ships in every bundle) and in docs/install/index.md. Both say what selfcheck.sh actually does — recompile each prebuilt binary on the downloader's host from the source in the same bundle and diff against the manifest — and both state it as determinism of our own build, never as a comparison with another compiler's output (CLAIMS discipline).

The tarball is NOT byte-reproducible and the docs say so. gzip records an mtime and the binaries are built fresh, so two runs of build_dist differ. That is exactly why both files exist: SHA256SUMS pins the artifact, selfcheck.sh proves its contents. Writing "reproducible release" unqualified would have been false in the one place it would be checked.

3. Download-page copy — the doc half is done; the website half is W's

docs/install/index.md gained a "What a release will carry, and how to check it" section. It opens by saying no release has been cut yet — verified locally, not assumed: git tag has no version tags and devdocs/release-notes/CODENAMES is empty. So the section describes what a release will carry rather than implying there is something to download, which is the same trap the existing "no binary release channel yet" sentence was already avoiding. The website copy is Track W's, in the separate repo, and is not touched here.

4. minisign signature — UNCHANGED, still blocked on the owner

Not attempted. No key generated, no substitute tool. Until a signature exists, SHA256SUMS is only as trustworthy as the repository serving it — which the install doc now says in as many words rather than letting the checksum imply more than it carries.

So this ticket is done except for step 4, which no agent may do. Leaving it open on that one step is what put it at the head of Track A's ready queue where nobody could finish it — the situation the 2026-08-25 note already flagged. It moves to blocked/ on decide-release-signing-key-custody rather than staying ready, so the queue stops offering three finished steps and one impossible one.