Verifying releases
In short
Section titled “In short”Every SpeculaPro release is built, signed and published by an automated release process. Before you run a release, you can prove two things:
- It came from SpeculaPro’s release process, not from someone who got hold of a registry password: the image carries a signature, which you check with cosign.
- What is inside it: each release has a software bill of materials (SBOM) listing every component and version, so your security team can check it against vulnerability advisories.
Then deploy the image by its digest (sha256:…), so the cluster runs exactly the image you
checked, whatever its tag points to later.
What a release contains
Section titled “What a release contains”A release is a Git tag such as v1.2.3. For each one, the release process publishes:
| Item | Where |
|---|---|
The container image for linux/amd64 and linux/arm64 |
ghcr.io/spalce/speculapro, tagged 1.2.3 and 1.2 |
| A signature on the image | Stored with the image in the registry, and recorded in the public Sigstore transparency log (Rekor) |
| The SBOM, in SPDX JSON | Attached to the image as a signed attestation, and as speculapro-v1.2.3.spdx.json on the GitHub release |
| Build provenance (how and from which commit it was built) | Attached to the image |
| The Helm chart | speculapro-1.2.3.tgz on the GitHub release |
| An offline bundle of the image with its signatures | speculapro-v1.2.3-oci.tar.gz on the GitHub release |
| The image digest | image-digest.txt on the GitHub release, and in the release notes |
How the signature works
Section titled “How the signature works”The signature is keyless: there is no long-lived SpeculaPro signing key that could leak. When
the release process runs, the build service vouches for its identity (this is the release.yml
workflow of the spalce/speculapro repository, running for tag v1.2.3), a short-lived
certificate is issued for that identity, and the image is signed with it. The signature and
certificate are recorded in the public transparency log.
Checking the signature therefore means checking who signed: the identity must be SpeculaPro’s release workflow, running for a version tag.
How to verify an image
Section titled “How to verify an image”You need cosign 2 or later on the machine you check from.
-
Find the digest of the release, from
image-digest.txton the GitHub release or the release notes. -
Verify the signature:
Terminal window cosign verify ghcr.io/spalce/speculapro@sha256:<digest> \--certificate-identity-regexp '^https://github.com/spalce/speculapro/.github/workflows/release.yml@refs/tags/v' \--certificate-oidc-issuer https://token.actions.githubusercontent.comIt prints the verified signature details and exits with status
0. Anything else, such asno matching signatures, means don’t deploy it: tell [email protected]. -
Verify the SBOM attestation, with the same identity:
Terminal window cosign verify-attestation ghcr.io/spalce/speculapro@sha256:<digest> --type spdxjson \--certificate-identity-regexp '^https://github.com/spalce/speculapro/.github/workflows/release.yml@refs/tags/v' \--certificate-oidc-issuer https://token.actions.githubusercontent.com -
Deploy by digest. In the Helm chart set
image.digest: sha256:<digest>; with Docker Compose setSPECULAPRO_IMAGE=ghcr.io/spalce/speculapro@sha256:<digest>in.env.
The software bill of materials
Section titled “The software bill of materials”The SBOM lists every package in the image: the Go modules compiled into the program, and the few
files of the distroless base image. Load speculapro-v1.2.3.spdx.json into your vulnerability
scanner or software inventory. Because it is also a signed attestation on the image, you can be
sure the list belongs to the image you verified.
How to bring a release into an air-gapped network
Section titled “How to bring a release into an air-gapped network”On a connected machine:
-
Verify the image as above.
-
Save the image with all its architectures, signatures and the SBOM attestation, plus the images it depends on if you use Docker Compose:
Terminal window cosign save ghcr.io/spalce/speculapro@sha256:<digest> --dir speculapro-ociskopeo copy --all docker://postgres:18 oci-archive:postgres-18.tarskopeo copy --all docker://docker.redpanda.com/redpandadata/redpanda:v24.3.1 oci-archive:redpanda.tarskopeo copy --all docker://valkey/valkey:8.1 oci-archive:valkey.tarThe GitHub release’s
speculapro-v1.2.3-oci.tar.gzis the samecosign saveoutput, ready made. The Helm chart’s migration Job also usespostgres:18, for itspsqlstep. -
Run
cosign initialize, so the Sigstore trust root is saved under~/.sigstore/. -
Carry the files and the
~/.sigstore/directory across with your approved media.
Inside the network:
cosign load --dir speculapro-oci registry.internal/speculapro/speculaproskopeo copy oci-archive:postgres-18.tar docker://registry.internal/library/postgres:18Then verify again, from inside, with the same identity flags plus --offline. Offline
verification uses the transparency-log proof stored with the signature and the trust root you
copied, so it needs no connection:
cosign verify --offline registry.internal/speculapro/speculapro@sha256:<digest> \ --certificate-identity-regexp '^https://github.com/spalce/speculapro/.github/workflows/release.yml@refs/tags/v' \ --certificate-oidc-issuer https://token.actions.githubusercontent.comPoint the Helm chart’s image.repository (and migrations.grants.image), or Compose’s
SPECULAPRO_IMAGE, at your internal registry, by digest.
Licences and rule-pack updates work offline too. See Licensing and Signed rule-pack updates.