Skip to content

Publish GitHub Artifact Attestations for pixi-docker images #152

@killett

Description

@killett

Context

I'm pinning ghcr.io/prefix-dev/pixi by SHA256 digest in a development
environment that has supply-chain audit requirements (regulated
scientific computing — but the pattern generalizes to any environment
where "where did this binary come from?" needs an answer beyond "I
trust the registry").

Digest pinning already gives me content-addressability:
ghcr.io/prefix-dev/pixi:0.66.0@sha256:… cannot be transparently
swapped under me at the registry level. That's a real, valuable
property and it's why I switched from the curl https://pixi.sh/install.sh | sh
pattern to COPY --from=ghcr.io/prefix-dev/pixi:…@sha256:… in the first
place.

What's missing — relative to digest-pinning astral-sh/uv — is an
out-of-band verification path. For uv I can run:

gh attestation verify \
  oci://ghcr.io/astral-sh/uv:0.11.10@sha256:… \
  --repo astral-sh/uv

…and Sigstore's public transparency log gives me cryptographic proof
that the pinned digest was produced by astral-sh/uv's official
GitHub Actions build, by an OIDC-authenticated workflow. That's a
separate trust path from "the registry served me what it served me."

For pixi today there's no equivalent — gh attestation verify against
any pixi-docker digest returns no attestations.

Request

Would the maintainers be open to adding actions/attest-build-provenance
to the pixi-docker release workflow, so that gh attestation verify
works for pixi the same way it does for uv?

Suggested implementation

Roughly five lines added to the existing build-and-push workflow,
after the step that publishes each image. Approximately:

- name: Generate build provenance attestation
  uses: actions/attest-build-provenance@v2
  with:
    subject-name: ghcr.io/prefix-dev/pixi
    subject-digest: ${{ steps.push.outputs.digest }}
    push-to-registry: true

…with the workflow's permissions: block extended to include the
three permissions the action needs:

permissions:
  contents: read
  id-token: write       # for OIDC token used in keyless signing
  attestations: write   # for uploading the attestation
  packages: write       # already present for ghcr.io publishing

Because pixi-docker is a public repo, the public-good Sigstore instance
is used automatically — no new secrets, no new infrastructure.

If pixi-docker publishes a manifest list (multi-arch), the attestation
step needs to run once per platform-specific digest, or once for the
manifest list digest depending on which level you want attested. The
action docs cover
both patterns.

Why this is worth the small maintenance cost

  • Parity with uv. Many of us pair pixi and uv in the same
    environment. Matching provenance stories across adjacent tools is a
    one-time fix that compounds over time.
  • Verifiable supply chain for downstream pinners. Anyone digest-
    pinning pixi today is already doing the hard half of the work.
    Attestations let them close the last verification gap.
  • Low maintenance burden. actions/attest-build-provenance is
    GitHub-maintained, stable, and used by major projects (uv, sigstore/*,
    Chainguard, Homebrew). If it ever breaks, it breaks visibly and
    loudly, not silently.
  • No user-visible change unless someone opts in. Existing users
    who don't run gh attestation verify continue using pixi-docker
    exactly as before. The attestation is additive.

Precedent

Happy to send a PR

If the maintainers are interested but bandwidth-constrained, I'm
glad to send a draft PR. Two things would help me pick the right
shape before I start:

  1. Should attestations be pushed to the registry (push-to-registry: true,
    so consumers can fetch them via cosign download attestation as
    well as gh attestation verify), or kept GitHub-API-only?
    astral-sh/uv does both; either is fine.
  2. For the multi-arch images — is one attestation per architecture
    the preferred shape, or one per manifest list?

Either way, thanks for maintaining pixi and pixi-docker. They're a
great tool and the security posture is already solid; this would just
close one specific gap that matters for downstream consumers like me.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions