SLSA Build Provenance¶
OpenMed release artifacts carry SLSA provenance generated by GitHub Actions artifact attestations. The provenance binds each subject to the source commit, source ref, builder workflow, and the workflow identity that signed the attestation.
Release coverage¶
- Python wheel and source distribution files are built and checked by the reusable
.github/workflows/provenance.ymljob before the PyPI upload job starts. The job also attempts to generate and verify SLSA provenance, and uploads that evidence when GitHub OIDC attestation services are available. - The GHCR manifest list is built by
.github/workflows/container-multiarch.ymland attested by digest after the image is pushed. - When attestation succeeds, the workflows verify the
https://slsa.dev/provenance/v1predicate, source commit, source ref, and signer workflow before uploading provenance evidence. - After the PyPI upload completes, the
evidencejob in.github/workflows/publish.ymlkeylessly signs each wheel and sdist with Sigstore and attaches the release evidence to the tagged GitHub release.
The PyPI publish action currently uploads with the project-scoped PyPI API token, so PyPI-native Sigstore attestations are disabled. The repository signs the same distributions itself instead: the evidence job produces a detached Sigstore bundle per artifact, verifiable with the sigstore CLI independently of GitHub's attestation store.
Release evidence assets¶
Evidence generation is best effort — a GitHub OIDC or Sigstore outage never blocks a release — but any evidence that is produced must verify against the signing workflow identity and the source revision before it is attached. When the evidence job succeeds, a tagged release carries:
| Asset | Purpose |
|---|---|
python-distributions.intoto.json | SLSA provenance bundle covering every dist/* subject |
release-artifact-digests.txt | sha256sum-format digest manifest for the release artifacts |
<artifact>.sigstore.json | Detached Sigstore bundle, one per wheel and sdist |
Because the bundles are release assets, offline verification no longer needs a gh attestation download round trip against GitHub's attestation store: fetch the release assets alongside the artifact and verify entirely offline.
Online verification¶
Download the artifact you plan to use, then verify it against the OpenMed repository and the expected release tag:
VERSION=v2.0.0
COMMIT=<release-commit-sha>
ARTIFACT=openmed-2.0.0-py3-none-any.whl
sha256sum "$ARTIFACT"
gh attestation verify "$ARTIFACT" \
--repo maziyarpanahi/openmed \
--predicate-type https://slsa.dev/provenance/v1 \
--signer-workflow github.com/maziyarpanahi/openmed/.github/workflows/provenance.yml \
--source-digest "$COMMIT" \
--source-ref "refs/tags/$VERSION"
Use the same command for the source distribution by setting ARTIFACT to the downloaded openmed-2.0.0.tar.gz file.
For the container image, verify the manifest-list digest rather than a mutable tag:
VERSION=v2.0.0
COMMIT=<release-commit-sha>
IMAGE=ghcr.io/maziyarpanahi/openmed:$VERSION
DIGEST="$(docker buildx imagetools inspect "$IMAGE" --format '{{ .Manifest.Digest }}')"
gh attestation verify "oci://ghcr.io/maziyarpanahi/openmed@$DIGEST" \
--repo maziyarpanahi/openmed \
--predicate-type https://slsa.dev/provenance/v1 \
--signer-workflow github.com/maziyarpanahi/openmed/.github/workflows/container-multiarch.yml \
--source-digest "$COMMIT" \
--source-ref "refs/tags/$VERSION"
Offline verification¶
Offline verification needs the artifact, the attestation bundle, and the trusted root material. Fetch the bundle and roots while online:
ARTIFACT=openmed-2.0.0-py3-none-any.whl
gh attestation download "$ARTIFACT" \
--repo maziyarpanahi/openmed \
--predicate-type https://slsa.dev/provenance/v1
gh attestation trusted-root > trusted_root.jsonl
sha256sum "$ARTIFACT" > artifact.sha256
Move the artifact, artifact.sha256, trusted_root.jsonl, and the downloaded sha256:*.jsonl bundle into the offline environment. Verify the local digest first, then verify the bundle:
VERSION=v2.0.0
COMMIT=<release-commit-sha>
ARTIFACT=openmed-2.0.0-py3-none-any.whl
BUNDLE=<downloaded-sha256-bundle>.jsonl
sha256sum --check artifact.sha256
gh attestation verify "$ARTIFACT" \
--repo maziyarpanahi/openmed \
--bundle "$BUNDLE" \
--custom-trusted-root trusted_root.jsonl \
--predicate-type https://slsa.dev/provenance/v1 \
--signer-workflow github.com/maziyarpanahi/openmed/.github/workflows/provenance.yml \
--source-digest "$COMMIT" \
--source-ref "refs/tags/$VERSION"
For the container image, download the attestation bundle while online:
VERSION=v2.0.0
IMAGE=ghcr.io/maziyarpanahi/openmed:$VERSION
DIGEST="$(docker buildx imagetools inspect "$IMAGE" --format '{{ .Manifest.Digest }}')"
gh attestation download "oci://ghcr.io/maziyarpanahi/openmed@$DIGEST" \
--repo maziyarpanahi/openmed \
--predicate-type https://slsa.dev/provenance/v1
gh attestation trusted-root > trusted_root.jsonl
printf '%s %s\n' "$DIGEST" "ghcr.io/maziyarpanahi/openmed@$DIGEST" \
> image-digest.txt
In the offline environment, verify the downloaded OCI bundle against the pinned digest:
VERSION=v2.0.0
COMMIT=<release-commit-sha>
DIGEST=sha256:<manifest-list-digest>
BUNDLE=<downloaded-sha256-bundle>.jsonl
gh attestation verify "oci://ghcr.io/maziyarpanahi/openmed@$DIGEST" \
--repo maziyarpanahi/openmed \
--bundle "$BUNDLE" \
--custom-trusted-root trusted_root.jsonl \
--predicate-type https://slsa.dev/provenance/v1 \
--signer-workflow github.com/maziyarpanahi/openmed/.github/workflows/container-multiarch.yml \
--source-digest "$COMMIT" \
--source-ref "refs/tags/$VERSION"
Sigstore signature verification¶
Each wheel and sdist is also signed keylessly with Sigstore, and the detached bundle is attached to the release. This signature is independent of GitHub's attestation store: it is verifiable with the sigstore CLI or cosign alone.
The distributions themselves are published to PyPI, not attached to the GitHub release; the release carries the evidence for them. Fetch the artifact from PyPI and its evidence from the release:
VERSION=v2.0.0
pip download openmed=="${VERSION#v}" --no-deps --no-binary :all: -d .
pip download openmed=="${VERSION#v}" --no-deps --only-binary :all: -d .
gh release download "$VERSION" \
--repo maziyarpanahi/openmed \
--pattern '*.sigstore.json' \
--pattern 'python-distributions.intoto.json' \
--pattern 'release-artifact-digests.txt'
Confirm the artifact digests match the published manifest, then verify the signature offline against the exact signing workflow identity and the release commit:
VERSION=v2.0.0
COMMIT=<release-commit-sha>
ARTIFACT=openmed-2.0.0-py3-none-any.whl
sha256sum --check --ignore-missing release-artifact-digests.txt
pip install sigstore
python -m sigstore verify github "$ARTIFACT" \
--bundle "$ARTIFACT.sigstore.json" \
--offline \
--cert-identity \
"https://github.com/maziyarpanahi/openmed/.github/workflows/publish.yml@refs/tags/$VERSION" \
--repository maziyarpanahi/openmed \
--sha "$COMMIT" \
--ref "refs/tags/$VERSION"
The --cert-identity value is the signing workflow, not the build workflow: Sigstore bundles are produced by publish.yml, while the SLSA provenance in python-distributions.intoto.json is signed by provenance.yml. Verifying against the wrong identity is expected to fail.
Repeat with ARTIFACT set to the source distribution to verify it as well.