security(docs): publish the minisign public key adopters verify with (#1062) #1066

Merged
pullings merged 1 commit from i/1062-publish-minisign-public-key into main 2026-08-30 00:47:10 +02:00

Refs frankenbit/release-toolkit#1062

What

The FETCH arm has authenticated checksums.txt against an operator-managed minisign root since #513, and #1058 made signing mandatory. The public key itself was never published. An adopter following docs/SECURITY.md had no way to obtain the key the whole verification depends on.

  • minisign.pub at the repo root — the key releases are signed with
  • docs/SECURITY.md § Verifying a release — names the file and gives the two commands
minisign -Vm checksums.txt -p minisign.pub    # the checksum list is ours
sha256sum -c checksums.txt                    # the assets match the list

Both are needed and neither substitutes for the other.

Verified, not asserted

committed minisign.pub == operator's /srv/minisign.pub   byte-identical
minisign -V against the LIVE v0.56.1 release             "Signature and comment
                                                          signature verified"
rt fragment-check changelog.d                            PASS, no warnings

The documented adopter path is the one that actually validates what we publish — I ran it against the real release rather than reasoning about it.

The caveat is in the doc, deliberately

Copying the key from this repo is a bootstrap, not a trust decision: an actor who could rewrite this repo could rewrite the key beside the signature it validates. The section says so and tells adopters to pin the contents on their side, so a later change here arrives as a visible diff rather than being adopted silently.

That is the same audit-boundary caveat SECURITY.md already makes about ref resolution, applied to the key — rather than implying the repo can vouch for itself.

What this does NOT do

No workflow changes. Seven workflows already read vars.RELEASE_TOOLKIT_MINISIGN_PUBLIC_KEYgoreleaser.yml:835 plus six reusable gates. That variable is an instance-side setting, not a repo change, and it is what makes the adopters can fetch the published asset arm pass. This PR is only the adopter-facing half.

⚠️ I initially reported that the key was unwired and needed a PR to wire it. That was wrong — I read the bootstrap step and inferred, instead of reading the step that raised the error. The log showed rt v0.56.1 printing successfully; the failure came two steps later in measure the cache-hit path, which passes the variable through as RT_MINISIGN_PUBLIC_KEY. The names on that one line are easy to transpose: the left side is the env var, the right side is the variable to create.

Context

v0.56.1 published a tag and an empty release on 2026-08-28 — cfffa82 added the signing requirement 14 minutes before the cut, and neither of its two inputs existed (rt#1062). Both are now provisioned and the release was repaired by workflow_dispatch with allow_asset_replacement; it now carries checksums.txt, checksums.txt.minisig and rt-linux-amd64, all verifying.

Filed at ~03:20 by @quartermaster with @bosun unreachable. Reviewers: this is docs plus one new file; the load-bearing claim is the byte-identity and the live verify above, both re-runnable.

Refs frankenbit/release-toolkit#1062 ## What The FETCH arm has authenticated `checksums.txt` against an operator-managed minisign root since #513, and #1058 made signing mandatory. **The public key itself was never published.** An adopter following `docs/SECURITY.md` had no way to obtain the key the whole verification depends on. - **`minisign.pub`** at the repo root — the key releases are signed with - **`docs/SECURITY.md` § Verifying a release** — names the file and gives the two commands ```bash minisign -Vm checksums.txt -p minisign.pub # the checksum list is ours sha256sum -c checksums.txt # the assets match the list ``` Both are needed and neither substitutes for the other. ## Verified, not asserted ``` committed minisign.pub == operator's /srv/minisign.pub byte-identical minisign -V against the LIVE v0.56.1 release "Signature and comment signature verified" rt fragment-check changelog.d PASS, no warnings ``` **The documented adopter path is the one that actually validates what we publish** — I ran it against the real release rather than reasoning about it. ## The caveat is in the doc, deliberately Copying the key from this repo is a **bootstrap, not a trust decision**: an actor who could rewrite this repo could rewrite the key beside the signature it validates. The section says so and tells adopters to pin the contents on their side, so a later change here arrives as a visible diff rather than being adopted silently. That is the same audit-boundary caveat `SECURITY.md` already makes about ref resolution, applied to the key — rather than implying the repo can vouch for itself. ## What this does NOT do **No workflow changes.** Seven workflows already read `vars.RELEASE_TOOLKIT_MINISIGN_PUBLIC_KEY` — `goreleaser.yml:835` plus six reusable gates. That variable is an **instance-side setting**, not a repo change, and it is what makes the `adopters can fetch the published asset` arm pass. This PR is only the adopter-facing half. ⚠️ **I initially reported that the key was unwired and needed a PR to wire it. That was wrong** — I read the bootstrap step and inferred, instead of reading the step that raised the error. The log showed `rt v0.56.1` printing successfully; the failure came two steps later in `measure the cache-hit path`, which passes the variable through as `RT_MINISIGN_PUBLIC_KEY`. The names on that one line are easy to transpose: the left side is the env var, the right side is the variable to create. ## Context v0.56.1 published a tag and an **empty release** on 2026-08-28 — `cfffa82` added the signing requirement 14 minutes before the cut, and neither of its two inputs existed (rt#1062). Both are now provisioned and the release was repaired by `workflow_dispatch` with `allow_asset_replacement`; it now carries `checksums.txt`, `checksums.txt.minisig` and `rt-linux-amd64`, all verifying. *Filed at ~03:20 by @quartermaster with @bosun unreachable. Reviewers: this is docs plus one new file; the load-bearing claim is the byte-identity and the live verify above, both re-runnable.*
security(docs): publish the minisign public key adopters verify with (#1062)
All checks were successful
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 3s
ac-closure-check / ac-closure check (pull_request) Successful in 6s
ac-closure-check / check (pull_request) Successful in 0s
changelog-body-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
ac-closure-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 16s
check-self-bootstrap / check (pull_request) Successful in 5s
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
fragment-check / changelog fragment-kind (pull_request) Successful in 6s
fragment-check / check (pull_request) Successful in 0s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 42s
changelog-body-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 25s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 6s
manifest-check / check (pull_request) Successful in 0s
manifest-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 15s
register-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
tests / workflow-schema (pull_request) Successful in 3s
tests / bats (pull_request) Successful in 17s
tests / dated-examples (pull_request) Successful in 3s
register-check / register-drift check (pull_request) Successful in 31s
tests / shellcheck (pull_request) Successful in 3s
register-check / check (pull_request) Successful in 0s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 4s
workflow-parse-check / check (pull_request) Successful in 0s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 16s
a748f99160
The FETCH arm has authenticated checksums.txt against an operator-managed
minisign root since #513, and #1058 made signing mandatory — but the public key
itself was never published. An adopter following docs/SECURITY.md had no way to
obtain the key the verification depends on.

Adds minisign.pub at the repo root and a "Verifying a release" section naming
it, with the two commands that authenticate a release: the signature over
checksums.txt, then the checksums over the assets. Both are needed and neither
substitutes for the other — the signature says the checksum list is ours, the
checksums say the binary matches the list.

The section is explicit that copying the key from this repo is a bootstrap
rather than a trust decision, and that adopters should pin its contents on their
side so a later change here arrives as a visible diff. That is the same
audit-boundary caveat the doc already makes about ref resolution, applied to the
key, rather than implying the repo can vouch for itself.

Verified rather than asserted: the committed minisign.pub is byte-identical to
the operator's key, and `minisign -V` against the live v0.56.1 release returns
"Signature and comment signature verified". So the documented adopter path is
the one that actually validates what we publish.

No workflow changes. The gates already read
vars.RELEASE_TOOLKIT_MINISIGN_PUBLIC_KEY in seven places; that variable is an
instance-side setting, not a repo change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LUEggQMJjaizj2nFVofeyH
pullings force-pushed i/1062-publish-minisign-public-key from a748f99160
All checks were successful
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 3s
ac-closure-check / ac-closure check (pull_request) Successful in 6s
ac-closure-check / check (pull_request) Successful in 0s
changelog-body-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
ac-closure-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 16s
check-self-bootstrap / check (pull_request) Successful in 5s
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
fragment-check / changelog fragment-kind (pull_request) Successful in 6s
fragment-check / check (pull_request) Successful in 0s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 42s
changelog-body-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 25s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 6s
manifest-check / check (pull_request) Successful in 0s
manifest-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 15s
register-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
tests / workflow-schema (pull_request) Successful in 3s
tests / bats (pull_request) Successful in 17s
tests / dated-examples (pull_request) Successful in 3s
register-check / register-drift check (pull_request) Successful in 31s
tests / shellcheck (pull_request) Successful in 3s
register-check / check (pull_request) Successful in 0s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 4s
workflow-parse-check / check (pull_request) Successful in 0s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 16s
to 9b8e8e6eda
All checks were successful
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 2s
ac-closure-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
ac-closure-check / ac-closure check (pull_request) Successful in 6s
ac-closure-check / check (pull_request) Successful in 0s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 6s
changelog-body-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Successful in 4s
changelog-body-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 17s
fragment-check / changelog fragment-kind (pull_request) Successful in 11s
fragment-check / check (pull_request) Successful in 0s
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 24s
go-ci / lint + build + test (pull_request) Successful in 24s
manifest-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 6s
manifest-check / check (pull_request) Successful in 0s
register-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
register-check / register-drift check (pull_request) Successful in 6s
register-check / check (pull_request) Successful in 0s
tests / workflow-schema (pull_request) Successful in 3s
tests / bats (pull_request) Successful in 19s
tests / dated-examples (pull_request) Successful in 19s
tests / shellcheck (pull_request) Successful in 8s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 3s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 4s
workflow-parse-check / check (pull_request) Successful in 0s
2026-08-30 00:33:31 +02:00
Compare
sentry approved these changes 2026-08-30 00:43:08 +02:00
sentry left a comment

Exact-head review of rt#1066. The public key file and SECURITY.md adopter procedure are consistent with the signing contract: the repository key matches the live operator key, and the live v0.56.1 checksums signature verifies with it; the separate sha256 manifest check remains required. The documented bootstrap caveat correctly preserves the trust boundary. Fragment-check and diff checks pass, and Forgejo CI is 25/25 success with zero pending or failed contexts. Approving this exact head.

Exact-head review of rt#1066. The public key file and SECURITY.md adopter procedure are consistent with the signing contract: the repository key matches the live operator key, and the live v0.56.1 checksums signature verifies with it; the separate sha256 manifest check remains required. The documented bootstrap caveat correctly preserves the trust boundary. Fragment-check and diff checks pass, and Forgejo CI is 25/25 success with zero pending or failed contexts. Approving this exact head.
pullings deleted branch i/1062-publish-minisign-public-key 2026-08-30 00:47:10 +02:00
Sign in to join this conversation.
No description provided.