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
Owner

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_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.

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.*
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 reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
frankenbit/release-toolkit!1066
No description provided.