To publish to PyPI with provenance, set up a trusted publisher so your CI job exchanges a short-lived OIDC identity for a 15 minute upload token, with no stored API token. Then attach PEP 740 attestations, which bind each wheel and sdist to the workflow that published it, so users can verify where a file came from.
What problem does trusted publishing solve?
A long-lived API token in a CI secret store works until someone copies it. It stays valid until a human notices and revokes it. PyPI's trusted publishing replaces that token with OpenID Connect. The PyPI docs describe it as the way to publish with OIDC instead of manually created API tokens, and say it complements tokens rather than replacing them.
How does the exchange work?
The PyPI docs give four steps. First, the CI service acts as an OIDC identity provider and issues short-lived tokens that identify the service and the specific workflow, user or repository that ran. Second, the project owner configures PyPI to trust a particular CI configuration for that project. Third, during a release the CI job sends its OIDC token to PyPI, which matches it against the trusted configurations and, on a match, issues a short-lived API token. Fourth, that token behaves like a project-scoped API token but expires 15 minutes after creation, which is enough time to upload.
Three security properties follow from the docs. No long-lived secret sits in the CI provider. A leaked minted token expires on its own. And access is limited to projects whose configuration matches the identity.
What do attestations add?
Trusted publishing proves, at upload time, that a particular CI identity pushed the files. Attestations make that proof durable and visible. PyPI implements digital attestations under PEP 740: each signature binds a release file, an sdist or a wheel, to a strong digest of its contents, so a user can check that a specific identity, such as a GitHub Actions workflow, attested to it. PyPI uses the in-toto attestation framework.
The docs name two predicate types. Publish attestations are proof that a package went out through a specific trusted publisher. SLSA provenance records the package's original source location. Each file can carry at most two attestations, one per allowed predicate, and uploads with more or with repeats are rejected. Only the listed trusted publisher identities can sign. The docs list GitHub Actions, GitLab CI/CD and Google Cloud among them.
What are the limits?
Be honest about what provenance proves. The PyPI docs say trust depends on the CI provider being a reliable identity provider and the publisher configuration being correct. An attestation says "this workflow published this file." It does not say the workflow's code was clean, that a maintainer account was not abused to edit the workflow, or that the dependencies were safe. A compromised repository can still publish a validly attested malicious release.
So provenance narrows the question to "which workflow, from which repository?" Verifiers still need to decide whether they trust that answer.
How do you set it up?
Do the configuration on PyPI first, in the project's publishing settings, naming the repository and workflow that may publish. Keep that workflow small: build, then publish, with only the permissions the OIDC step needs. The PyPI docs link to guides for adding a trusted publisher to an existing project and for creating a new project with one. Check them for the current exact steps, since the settings screens change.
What to do
- Add a trusted publisher for each project, scoped to one repository and one workflow file.
- Remove the old API token from CI secrets and revoke it on PyPI.
- Restrict who can edit the publishing workflow, and require review for changes to it.
- Publish with attestations enabled and confirm they appear on the release files.
- On the consuming side, decide what identity you expect and verify against it.
- Treat a provenance check as one signal alongside dependency review. Safeguard's free scan at gold.safeguard.sh/scan checks up to 100 dependencies.
Quick check
How long does the token from trusted publishing last?
PyPI says it expires 15 minutes after creation, which is enough time to upload.
Does an attestation prove a package is safe?
No. It shows which identity published the file. It does not show that the source or its dependencies were free of malicious code.
Sources
Last reviewed: 18 October 2026.