Repository navigation
Provenance: each release's tarball attested and held to the bytes hex.pm serves - #13
Conversation
…tes hex.pm serves .github/workflows/provenance.yml, after beam_mcp's: on a v* tag CI builds the tarball with tools/release_tarball.sh, attests its digest with actions/attest-build-provenance (pinned by commit), verifies it with --signer-workflow, and holds it to the bytes hex.pm serves (a fetch or a mismatch fails the tag's run). A manual run takes `ref`, a published tag, and is held to hex.pm the same way, so 0.2.1, released before this workflow, can be attested now. docs/verifying-releases.md gives the verify command; CHANGELOG entry under Unreleased. Signed-off-by: Ayla Croft <aylacroft@proton.me>
Signed-off-by: Ayla Croft <aylacroft@proton.me>
minitru
left a comment
There was a problem hiding this comment.
Post-merge review. It works: the 0.2.1 tarball hex.pm serves has an attestation in the repo's store. The follow-ups below are about what else the workflow will attest and how tightly the provenance is bound; none blocks.
What I checked
- All four action pins match their version tags (checkout v7.0.1, setup-beam v1.24.1, attest-build-provenance v4.2.2, upload-artifact v7.0.1).
- The one run so far is the manual one on 7 Oct, and it succeeded.
- hex.pm's checksum for 0.2.1 (
df44fba2…) has one attestation. - I did not download the tarball or run
gh attestation verifymyself.
Findings, most important first
-
It attests before it knows the tarball is the release. The step order is build, attest, then compare with hex.pm. A tag run whose bytes don't match hex.pm fails, but the attestation is already in the store. A manual run with no
refattests the branch head under the release's file name by design. Consumers who verify the hex.pm tarball are safe, since the digest must match. But "signed by this workflow" then doesn't mean "a release". Fix: fetch and compare first, attest only on a match, and drop the branch-head mode or have it skip the attest step. -
Tag-controlled code runs in the job that can sign.
release_tarball.shrunsmix deps.getandmix hex.buildon the tag's tree, so that tree'smix.exsexecutes in a job holdingid-token: writeandattestations: write. The usual hardening is two jobs: build withcontents: readonly and upload the tarball, then a second job that attests the artifact. -
No tag ruleset. The only ruleset is "main protection" on branches. Anyone with write access can push, move or delete a
v*tag, and a push triggers an attestation for whatever commit it points at, onmainor not. A tag ruleset onv*closes that, and an "is this commit on main" check in the workflow would too. -
The 0.2.1 provenance names
main, not the tag. A manual run executes at the branch head, so the attestation's source ref and commit aremain's; only the log ties the bytes tov0.2.1. This is unavoidable for 0.2.1, because the workflow didn't exist at that tag. The doc says it was a manual run, but it should also say that--source-ref refs/tags/v0.2.1won't verify for that release. -
The toolchain isn't the pinned pair. The comment says "a copy of .tool-versions' line", but the workflow asks for
"28"and"1.18"where.tool-versionshas 28.1.1 and 1.18.4, and Hex itself is unpinned. If a patch release changes the tarball bytes, a correct release fails the hex.pm comparison after it's already published.version-file: .tool-versionswithversion-type: strictremoves the copy. -
Version string goes straight into shell.
${{ steps.build.outputs.version }}is interpolated intorun:blocks, and it comes from a loose sed over the tag'smix.exs. Pass it throughenv:and reject anything that isn't a version number. This is low risk given who can tag, but it's the pattern theINPUT_REFhandling already gets right. -
GOVERNANCE.md list. The new ruleset paragraph sits between steps 3 and 4, so step 4 renders as a separate list. Move it below step 4.
Good as written: the input ref is passed through env, restricted to v* and checked to be a real tag. A missing or mismatched release fails loudly on a tag run. The workflow never publishes.
beam_mcp's provenance workflow, for the signer: on a release tag CI builds the tarball the canonical way, attests it (SLSA build provenance), and verifies the attestation against what hex.pm serves. A manual run takes a published tag, so once this merges I will run it on
v0.2.1to attest the release that went out today. Docs and CHANGELOG say how to verify.