mrkeyoor.com_
Sun 16 Aug 14:47 UTC
Open Source16 Aug 2026 13:33 UTC7 min read

Python Packaging Still Lacks a Reproducible-Build Standard

PyPI can show who published a package, but not yet whether independent builders can recreate it from the claimed source.

Python packaging has many of the parts needed to make independently verifiable builds practical, but it still lacks a standard way to connect a file on PyPI to its exact source and complete build environment. Brett Cannon, a longtime Python contributor and candidate for the 2026 Python Packaging Council, set out the missing pieces this week. His outline is not an adopted proposal, but it identifies a tractable gap in Python's software supply chain.

That gap matters because the artifact developers install is not the repository they inspect. It is usually a wheel produced by a build backend and uploaded through a release pipeline. If that pipeline, backend, or build environment is compromised, the published file can contain code that never appeared in the project's source tree. This is true even for a pure-Python wheel: packaging Python files into an archive is still a build step performed by software.

Reproducible builds offer a way to check that handoff. An independent party takes the same source, instructions, and environment, rebuilds the package, and compares the result with the published artifact. A match provides evidence that the distributed bits came from the expected inputs. The idea is established elsewhere, but Python's standards do not yet carry all the information a verifier would need to perform that check routinely.

Reproducible means more than building twice

The Reproducible Builds project defines a reproducible build as one in which any party can recreate bit-for-bit identical artifacts from the same source code, build environment, and build instructions. Each part of that definition is load-bearing. A repository URL without a commit is ambiguous. A build command without the versions of its tools is incomplete. A matching package name says nothing about timestamps, platform details, environment variables, file ordering, or compiler behavior.

Python's modern packaging flow already creates a useful boundary. A frontend such as build reads pyproject.toml, creates an isolated environment, installs the declared requirements, and calls standardized PEP 517 hooks on a backend such as Hatchling, Flit, or setuptools. The official build documentation describes this sequence and notes the remaining conditions for full reproducibility, including pinned backend versions, a reproducible backend, a consistent Python version, and a consistent platform for platform-specific wheels.

A typical declaration might look like this:

[build-system]
requires = ["hatchling>=1.27"]
build-backend = "hatchling.build"

That tells a frontend what it may install, not necessarily what was installed for a particular release. A range can resolve to one version today and another later. Transitive dependencies can move too. The build tool now reports resolved versions in its diagnostics, but those details are not automatically carried inside every uploaded distribution as a replayable record. Isolation reduces accidental dependence on a developer's workstation; it does not by itself preserve the environment for a future verifier.

PyPI attestations answer a different question

PyPI is not starting from zero. It supports digital attestations under PEP 740, binding an individual wheel or source distribution to a cryptographic digest and a Trusted Publisher identity. PyPI currently accepts publish attestations and SLSA provenance from supported identities including GitHub Actions, GitLab CI/CD, and Google Cloud, according to its attestation documentation.

Those records are valuable. They can show that a release came through a particular repository workflow and that the file was not altered after signing. But an attestation is not the same as an independent rebuild. PyPI's own security model makes the limit explicit: a valid attestation proves access to an identity and can protect against modification after a build, but it does not establish that malicious or vulnerable code was not introduced before or during that build.

The distinction is between provenance and correspondence. Provenance can say, in effect, “this workflow published this digest.” Reproducibility asks, “does this digest follow from the source and build recipe we expected?” A strong supply chain needs both. Signing a compromised artifact faithfully preserves evidence about who signed it; rebuilding can expose that it differs from what the declared inputs produce.

The first missing link is exact source

Cannon's first proposed addition is a reliable record of the source used to make each distribution. Project metadata often contains a homepage or repository link, but that is not an immutable input. Releases can be built from a tag, a commit, a source archive, an uncommitted working tree, or even a directory that differs from the public repository.

Python already has a model that could inform such a record. The Direct URL Data Structure can represent a version-control URL and an exact commit ID, or an archive URL with a hash. Installers use it when recording packages installed directly from a repository or archive. Cannon's suggestion is to make comparable source information available for wheels and source distributions published to an index, potentially as distribution metadata.

That would remove one of the least glamorous but hardest parts of independent rebuilding: discovering which repository and revision correspond to a package release. Names do not always line up, tags may be mutable, and generated files in a source distribution may not exist in version control. A verifier should not have to infer the build input from convention.

The second is a record of the tools

The next requirement is an inventory of the software that participated in the build. Python has recently gained part of the container for that data. PEP 770 reserves a .dist-info/sboms directory in wheels for software bills of materials. Its motivation specifically notes that SBOMs can record build tools and environments, and that exact tool versions are often necessary to establish reproducibility.

Cannon argues that build backends could record the packages installed in their isolated environment and place that information in an SBOM, reducing the burden on individual maintainers. The standardized [build-system] entry point gives tooling a known place to observe the build. In the best case, maintainers would choose a compatible backend and get the record as part of an ordinary release rather than hand-writing a manifest.

There are limits. Python packages can invoke compilers, linkers, Rust or C toolchains, operating-system libraries, and arbitrary commands outside the Python environment. Capturing only installed Python distributions may be enough for some pure-Python wheels but not for native extensions. Environment variables, locale, timestamps, archive ordering, and platform images may also affect output. A useful standard will have to define what is recorded, not merely provide a directory where a record can live.

Source distributions are another awkward case. Wheels have a structured .dist-info directory, while an sdist is generally a tar archive with a PKG-INFO file and no equivalent standard location for extra metadata. Cannon presents two broad options: accept that reproducibility support applies only to wheels, or design a new source-distribution format with room for structured records. The second path would be more complete, but format transitions in a large ecosystem are slow and compatibility-sensitive.

Real-world rebuilds show why metadata is central

A recent preprint offers a useful measure of the current problem. In No Snake Oil: Verifying Python Package Builds, researchers studied 12,180 popular PyPI releases with two automated rebuild systems. Among releases the systems rebuilt, only 15.4% of one tool's results and 19.1% of the other's were byte-for-byte identical to the published wheel. The authors identify source selection and build-environment reconstruction among the practical obstacles, alongside benign variation such as timestamps and archive ordering.

Those low strict-match rates do not mean most packages were compromised. They show that a naive “different hash equals attack” policy would generate too much noise. The researchers' normalization-based tool could establish equivalence for 60.2% and 78.9% of source-equivalent rebuilds from the two systems, respectively, by accounting for differences that did not change package behavior. That result also exposes a policy question for Python packaging: should PyPI surface only bit-for-bit reproduction, or allow a separately defined and auditable notion of equivalent output? Cannon's outline focuses on exact reproduction, while the research suggests verifiers will also need careful diagnostics for mismatches.

Verification could become a shared service

The most consequential part of Cannon's outline is not another metadata field. It is a possible operating model. Rather than expecting every pip user or company to rebuild every dependency, independent organizations could perform rebuilds and submit signed results. PyPI could display which files were successfully reproduced and expose that status through its index API. Installers or organizational policy tools could then prefer verified artifacts.

This would turn work already done by security teams, distributions, and large software users into a public signal. It would also create a new trust layer: users would need to know who the verifiers are, what environments they used, how failures are handled, and whether multiple independent results agree. A badge without inspectable evidence would be weak. A signed verification tied to source, recipe, logs, and output digest could be much more useful.

Adoption also needs to remain additive. Many maintainers have little time for release engineering, and some packages depend on proprietary platforms or toolchains that are difficult to reproduce. Treating an absent verification as proof of danger would punish projects for ecosystem limitations. The more workable approach is to make successful reproduction a positive signal while making the tooling cheap enough that the covered share grows.

What to watch next is whether Cannon's sketch turns into one or more formal packaging proposals, especially for immutable source references, automatic build-tool recording, and sdist metadata. Equally important will be experiments that publish third-party verification through PyPI's existing attestation machinery. The components are close enough to connect, but the hard work is defining evidence that installers can consume without turning ordinary build variation into a flood of false alarms.

We reviewed this

  1. Libraries — our honest review

Sources

  1. What's missing to have reproducible builds on PyPI
  2. Definitions — Reproducible Builds
  3. How it Works — build
  4. Digital Attestations — PyPI Docs
  5. Security Model and Considerations — PyPI Docs
  6. Direct URL Data Structure — Python Packaging User Guide
  7. PEP 770 — Improving measurability of Python packages with SBOMs
  8. No Snake Oil: Verifying Python Package Builds