Skip to content

Attest the release archives with build provenance and document how to verify a download - #68

Merged
178inaba merged 4 commits into
mainfrom
feature/65-attest-release-provenance
Aug 31, 2026
Merged

Attest the release archives with build provenance and document how to verify a download#68
178inaba merged 4 commits into
mainfrom
feature/65-attest-release-provenance

Conversation

@178inaba

Copy link
Copy Markdown
Owner

Why

checksums.txt on a Release proves nothing on its own. Whoever can replace an archive there can replace the checksum file sitting beside it, so the two fall together. A build provenance attestation does not: it is signed by Sigstore and stored on GitHub rather than as a Release asset, which lets anyone check that a downloaded archive came from this repository's release workflow, built from a given commit.

This is the second and last stage of #63. The pipeline being attested — tagpr → GoReleaser, writing dist/checksums.txt under a fixed name — landed with #64, which pinned that name specifically so this step could attest through it.

What

  • .github/workflows/release.yml — an actions/attest-build-provenance@v4 step after GoReleaser, under the same if: steps.tagpr.outputs.tag != '' gate, with subject-checksums: dist/checksums.txt. One attestation covers all six archives; the checksum file's lines are the subjects.
  • .github/workflows/release.ymlid-token: write, attestations: write and artifact-metadata: write added to the permissions block, which is what actions/attest documents as required. The comment above it is rewritten: it said the workflow's own token needs no more than the checkout, and this step makes that untrue. The App token from Release rdsh with tagpr and GoReleaser: prebuilt binaries, a Homebrew cask, and --version #64 is untouched — the attestation uses the default ${{ github.token }} and wants nothing from the App.
  • README.md — a ### Verify a download subsection at the end of ## Install with the one command and one sentence on what a pass proves.
  • .goreleaser.yaml — one word of tense. Its checksum.name_template comment said the provenance was "being added next"; this PR is that change, and the comment travels to the sibling CLIs on the next copy, so it is corrected here rather than left to go stale.

skills/rdsh/SKILL.md is unchanged: verification is a one-time human step and no rdsh CLI surface moves, so CLAUDE.md's three-way documentation sync is not triggered.

Deploy notes — a patch release is needed after this merges

v1.0.0 is already published (2026-08-30) and was built before this change, so it carries no attestation and never will. That makes the conditional in #63's Manual release steps fire:

If v1.0.0 was published before #65 merged, cut a patch release after it so the attestation can be verified end to end.

Until a v1.0.1 is cut (the default patch bump — no tagpr: label needed), there is no attested artifact and acceptance criterion 1 cannot be checked. The README's new subsection is worded to stay true in the meantime: it says what a pass proves, without asserting that the archives currently on the Releases page carry an attestation.

Two decisions worth a look

The wrapper vs actions/attest directly. The Issue asks for attest-build-provenance, so that is what this uses. Worth knowing that its v4 README now says "new implementations should use actions/attest instead" — the wrapper is a composite over actions/attest@v4.2.2 that forwards every input, and provenance is actions/attest's default mode, so the attestation produced is byte-for-byte the same decision either way. Raising it rather than swapping it silently; happy to switch if you would rather track the upstream recommendation.

subject-checksums rather than subject-path: dist/*.tar.gz,dist/*.zip. The subject list then follows whatever GoReleaser actually built, so a later change to the target matrix needs no edit in the workflow. It also reuses digests GoReleaser already computed instead of re-hashing the six archives on the runner. Per the Issue's Deferred to implementer judgment, checksums.txt is not attested as a subject of itself — its lines are the subjects, and gh attestation verify <archive> resolves by the archive's own digest.

How this was verified

The attestation itself cannot be produced off a real release, so what is checked locally is the shape of the change:

  • yq '.permissions' and yq '.jobs.release.steps[-1]' — the file parses and both edits landed as intended.
  • The new step carries the same gate as the two steps above it, which is the whole argument for "an ordinary push to main skips it" and for this PR not breaking main: a non-release push runs checkout → app token → tagpr and skips the rest, and widening the workflow token's scopes cannot fail a run.
  • go test -race ./... and golangci-lint both pass — no Go code changes, so this is a formality; CI's lint job covers Go and .goreleaser.yaml only.

Left for after merge, on the v1.0.1 release: gh attestation verify <any of the six archives> --repo 178inaba/rdsh succeeds on a fresh download and fails on a modified copy, and the Release page still carries six archives plus checksums.txt with no attestation file among them.

Closes #65
Part of #63

checksums.txt alone proves nothing: whoever can replace an archive on the
Release can replace the checksum file beside it. The attestation is signed
by Sigstore and stored on GitHub rather than as a Release asset, so a
download can be checked against the workflow and commit that built it.
The checksum name was pinned for an attestation step that this branch adds,
so the comment that pointed forward to it now points at the file next door.
@178inaba 178inaba self-assigned this Aug 30, 2026
@178inaba
178inaba marked this pull request as ready for review August 30, 2026 19:25
The permissions comment still answered why the block held only a read
scope, which stopped being the question when the writes were added, and
the attest step named an input whose own name says the same thing.
@178inaba
178inaba merged commit b9346d9 into main Aug 31, 2026
3 checks passed
@178inaba
178inaba deleted the feature/65-attest-release-provenance branch August 31, 2026 10:23
@daemon-bot daemon-bot Bot mentioned this pull request Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Attest the rdsh release artifacts with build provenance and document how to verify them

1 participant