विषय पर बढ़ें

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.yml job 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.yml and attested by digest after the image is pushed.
  • When attestation succeeds, the workflows verify the https://slsa.dev/provenance/v1 predicate, source commit, source ref, and signer workflow before uploading provenance evidence.
  • After the PyPI upload completes, the evidence job in .github/workflows/publish.yml keylessly 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.