Package indexes are at their safest when boring mechanics make bad outcomes harder. PyPI's new 14-day release-file cutoff is one of those mechanics. It does not turn the Python ecosystem into a formal proof. It does not replace account security, trusted publishing, attestations, hash pinning, or careful dependency review. It does close a strange old door: adding fresh files to a release that everyone already thinks they understand.
LWN surfaced the change on July 22 after Python Software Foundation security developer-in-residence Seth Larson announced it on the PyPI Blog. The rule is straightforward: PyPI now rejects new files uploaded to releases older than 14 days. A maintainer can still publish a new release. What changes is the ability to quietly add a new wheel or other artifact to a long-stable version after that grace period has passed.
project: example-package
version: 1.2.3
day 0 - sdist and wheels land
day 14 - old-release upload window closes
day 90 - new artifact for 1.2.3 is rejected
next - publish 1.2.4 or another explicit versionThe Risk Was Old Mutability
The uncomfortable part of the old model is that a PyPI release has historically been a grouping of files under a version, not necessarily a finished sealed object. That made sense when package publishing was simpler and a release might be assembled as files became available. It becomes riskier when projects publish many wheels from CI, tokens are delegated through automation, and attackers actively look for paths that let legitimate infrastructure distribute malicious artifacts.
The PyPI Blog says the direct motivation is preventing old, stable releases from being poisoned if publishing tokens or workflows are compromised. That matters because an old version carries trust. It may be pinned in internal requirements. It may have survived months of production use. It may be approved in a dependency allowlist. If a new artifact appears under that same version later, especially one specific to a platform or Python ABI, the version string alone stops being a useful signal.
The attack surface is not only new packages. It is also old trust that can still accept new files.
This is not theoretical ecosystem hygiene. The discussion restarted after real supply-chain compromises involving Python packages and CI workflow behavior. PyPI's change reduces how much cleanup is needed when a publisher is compromised because attackers have less room to add a malicious subset of files to releases that were previously clean.
Fourteen Days Is A Compromise
The number is not magic. A zero-day window would be cleaner from an immutability perspective, but real package publishing is messy. Some projects still need time to build platform-specific wheels, fix packaging metadata, or finish release automation after the first file lands. PyPI's choice keeps a short assembly period while cutting off the much stranger case: adding new artifacts months or years later.
Larson's post notes that PyPI looked at how disruptive the change would be, including a query against top packages for Python 3.14-compatible wheels added more than 14 days after an existing release. The reported result was small enough for the project to choose the security tradeoff: ask maintainers to publish another version when the old window has closed.
That is the important operational shift. If a maintainer needs to support a new Python version after the cutoff, the path is no longer to backfill files into the old version. The path is to make a new release that dependency tooling, changelogs, mirrors, reviewers, and humans can see.
This Is Not Full Release Finalization
The PyPI Blog is careful about the limitation. Users should not yet rely on this behavior as a complete release-state API. There are no final standardized semantics yet for a release that is closed to new files. Those semantics are expected to come from future work around Upload 2.0 and Staged Previews in PEP 694.
That distinction matters. The 14-day cutoff is a policy guardrail in the current system. Upload 2.0 points toward a better primitive: create a publishing session, upload all release artifacts, test or stage them, then publish the completed release more atomically. That is closer to how package publication should work in a world where one version may include many wheels, attestations, metadata files, and platform builds.
Until that lands, this rule gives PyPI a useful intermediate defense. It makes the old behavior less open-ended without pretending the upload model has already been redesigned.
Where Attestations Fit
PEP 740 and PyPI's digital attestation work sit next to this change. Attestations help connect an artifact to build provenance and publisher identity. They raise the cost of lying about where a package came from. But provenance cannot fully compensate for a release boundary that remains indefinitely open. If old versions can silently acquire new artifacts long after review, every consumer has to reason about a moving target.
Closing the late-file path makes attestations more useful because the set of files under a version becomes more stable. It also makes hash pinning and lockfiles easier to reason about. A consumer that pins exact hashes is already better protected, but not every environment does that consistently. Ecosystem-level constraints are valuable because they protect the long tail of installs that are not curated as carefully as the best internal build pipeline.
What Maintainers Should Do
- Expect new files to need new versions once a release is older than 14 days.
- Treat publisher tokens as high-value credentials, especially those reachable from CI workflows.
- Use Trusted Publishing where possible so uploads do not depend on long-lived PyPI API tokens.
- Publish attestations when the release pipeline supports them, then make sure downstream users can inspect them.
- Document wheel timing for projects that build platform artifacts asynchronously, so users know what to expect inside the 14-day window.
For consumers, the answer is still layered: pin hashes when the deployment risk justifies it, prefer maintainers with sane publishing hygiene, review lockfile changes, and avoid treating a familiar version number as proof that every file attached to it has the same history.
The Takeaway
PyPI's 14-day cutoff is the kind of supply-chain security change that looks small because it is deliberately boring. That is the point. It removes a surprising mutability edge from old releases and nudges maintainers toward explicit new versions when new files need to ship later.
The best ecosystem defenses often work this way. They do not ask every developer to become a security specialist. They make the dangerous path narrower, the expected path clearer, and the weird state harder to create accidentally. Old releases should feel old for a reason. PyPI just made that reason enforceable.

// Discussion
Comments
No comments yet. Start the thread.