Skip to content

Publish the image to GHCR with signed build provenance - #95

Merged
csvance merged 1 commit into
mainfrom
ghcr-attestations
Oct 4, 2026
Merged

csvance merged 1 commit into
mainfrom
ghcr-attestations

Conversation

@csvance

@csvance csvance commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

The image was pushed to a personal Docker Hub account with a long-lived access token stored as a repository secret. EnzymeAD has no Docker Hub presence but already publishes its container images on the GitHub Container Registry, so the image moves to ghcr.io/enzymead/reactantserver. The workflow now logs in with the run's own GITHUB_TOKEN, so the DOCKERHUB_* variable and secrets are no longer used and can be deleted. The owner in the path is the repository's, lowercased, so a fork publishes to its own namespace instead of failing against EnzymeAD's.

After the push, actions/attest (pinned to v4.2.2 by SHA, like the other actions) records SLSA build provenance for the pushed digest: this repository, commit, workflow and run built it. It is signed through Sigstore with the run's OIDC identity, so there is no signing key to hold or rotate, and it is stored with GitHub and pushed beside the image. The build is not bit-for-bit reproducible (the precompile caches differ between builds), so rebuilding from source cannot confirm a published digest; the attestation is what ties a digest to its source. Consumers check it with gh attestation verify oci://ghcr.io/enzymead/reactantserver:latest --repo EnzymeAD/ReactantServer.jl, which the deployment docs and deploy/README.md now describe, along with pinning by digest.

The job gains packages, id-token, attestations and artifact-metadata write permissions; the workflow-level default stays contents: read. The image name changes everywhere the docs, README, compose default and deploy/ comments named it.

The image was pushed to a personal Docker Hub account with a long-lived access token stored as a
repository secret. EnzymeAD has no Docker Hub presence but already publishes its container images
on the GitHub Container Registry, so the image moves to ghcr.io/enzymead/reactantserver. The
workflow now logs in with the run's own GITHUB_TOKEN, so the DOCKERHUB_* variable and secrets are
no longer used and can be deleted. The owner in the path is the repository's, lowercased, so a
fork publishes to its own namespace instead of failing against EnzymeAD's.

After the push, actions/attest (pinned to v4.2.2 by SHA, like the other actions) records SLSA
build provenance for the pushed digest: this repository, commit, workflow and run built it. It is
signed through Sigstore with the run's OIDC identity, so there is no signing key to hold or
rotate, and it is stored with GitHub and pushed beside the image. The build is not bit-for-bit
reproducible (the precompile caches differ between builds), so rebuilding from source cannot
confirm a published digest; the attestation is what ties a digest to its source. Consumers check
it with `gh attestation verify oci://ghcr.io/enzymead/reactantserver:latest --repo
EnzymeAD/ReactantServer.jl`, which the deployment docs and deploy/README.md now describe, along
with pinning by digest.

The job gains packages, id-token, attestations and artifact-metadata write permissions; the
workflow-level default stays contents: read. The image name changes everywhere the docs, README,
compose default and deploy/ comments named it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@csvance
csvance merged commit 056e474 into main Oct 4, 2026
12 checks passed
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.

1 participant