Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 73 additions & 1 deletion peps/pep-0694.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PEP: 694
Title: Upload 2.0 API for Python Package Indexes
Author: Barry Warsaw <barry@python.org>, Donald Stufft <donald@stufft.io>, Ee Durbin <ee@python.org>
Author: Barry Warsaw <barry@python.org>, Donald Stufft <donald@stufft.io>, Ee Durbin <ee@python.org>, Cary Hawkins <hawkinscary23@gmail.com>
PEP-Delegate: Dustin Ingram <di@python.org>
Discussions-To: https://discuss.python.org/t/pep-694-pypi-upload-api-2-0-round-2/101483
Status: Draft
Expand Down Expand Up @@ -29,6 +29,9 @@ Along with standardization, the upload API provides additional useful features s
* "staging" a release, which can be used to test uploads before publicly publishing them,
without the need for `test.pypi.org <https://test.pypi.org/>`__;

* entering the publishing session workflow from the existing legacy upload API, so that staging is
available to publishers before their tooling adopts this API;

* artifacts which can be overwritten and replaced, until a session is published;

* detailed status on the state of artifact uploads;
Expand Down Expand Up @@ -285,6 +288,14 @@ The unguessable :ref:`stage preview URL <staged-preview>` is a separate capabili
governed by this authorization check; it grants read-only preview access to any client that holds the token,
so that (for example) a CI job can install-test a staged release without project upload credentials.

As one such stricter policy, an index **MAY** require *additional* authorization, beyond upload permission, to
:ref:`publish <publishing-session-completion>` or :ref:`cancel <publishing-session-cancellation>` a session,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for publishing, but can you provide some additional scenarios about why cancelation should require additional authorization?

while still allowing session creation and file upload with upload permission alone. This lets a publisher
separate duties: an automated system can be given a credential that can create a session and upload files to
it but cannot publish it, while the authorization to publish is held elsewhere, so that compromise of the
automated system alone does not result in a published release. How such an additional authorization is
expressed, and whether it is offered at all, is determined by the index operator.


.. _session-errors:

Expand Down Expand Up @@ -743,6 +754,20 @@ change: deferred processing resolves to either ``published`` on success or ``err
resolves to ``error``, the session remains editable and the reason is reported in the session's ``notices``,
as described in :ref:`publishing-session-states`.

The ``processing`` state **MAY** be used to run asynchronous review of a session's files before it is
published, such as malware scanning. If the review completes without an adverse result, the session resolves
to ``published`` as normal. If the review does not complete within a period chosen by the index, the server
**MAY** treat it as though it had completed without an adverse result and publish the session, so that a
backlogged or unavailable review system does not indefinitely prevent publication. If the review produces an
adverse result, the session resolves to ``error`` with the reason reported in the session's ``notices``; the
server **MAY** decline to publish such a session on any subsequent retry, in which case it is eventually

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section (if not covered below), should provide details on what happens if files in the session are deleted and reuploaded. It could be:

  • Continue to await human review
  • Rescan newly uploaded files
  • Reset the entire malware scan state

It probably would also be helpful to say something about denial of service attacks on the scanner. E.g. bad actor starts an upload session, uploads known malware knowing it will get flagged. Bad Actor deletes the files and reuploads them to trigger another scan. Rinse and repeat until the scanning service is overwhelmed.

:ref:`canceled <publishing-session-cancellation>` and its data discarded. Where a session has resolved to
``error`` because of an adverse result, an index **MAY** provide a way for the publisher to request that the
result be re-examined. Such a re-examination **SHOULD** be performed by a human, to confirm whether the
adverse result was a false positive; if it was, the index **SHOULD** allow the session to be published. The
review itself, including which checks run, how long they are allowed to take, and how such requests are
handled, is determined by the index operator and is out of scope for this specification.

A publish attempt that fails *synchronously* (i.e. within the publish request itself) is returned to the
client as an :ref:`error response <session-errors>` and leaves the session in its current editable state; it
does **not** move the session to ``error``.
Expand Down Expand Up @@ -1348,6 +1373,42 @@ changes to the installer tool required.
The details of this user experience are left to installer tool maintainers.


.. _legacy-interop:

Legacy Upload API Interoperability

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes sense to be more explicit about the "control plane" API for managing the session (and file uploads), and/or lump this and the explicit session creation together? Maybe add a top-level "Session Creation" section, putting this and the original session creation request together, then the rest of it would be the same control plane API.

It also occurs to me that we might want to update the state diagrams so that it's obvious that there are two entry points "open" state. However, if I'm reading this section correctly, the index can also auto-publish if the conditions allow (no adverse scan or scan timeout). Maybe that'll make the diagram too cluttered in which case we could skip it or add a second diagram. LMK what you think and I can take a crack at updating the diagram once this PR lands.

The other thing this section should discuss is the interaction between legacy and 2.0 sessions. E.g. what happens if there's an open 2.0 session? Can someone use the legacy API to add a file to that existing session? I would think that would get rejected with a 409. Also vice versa (meaning: do we allow mixing of session creation requests?). We just need to be explicit about how these mix.

----------------------------------

Publishers cannot use the features of this API until their upload tooling adopts it, and the legacy API is
expected to remain available for a long time. To make staging available to those publishers sooner, an index
**MAY** allow a legacy upload to create a publishing session, so that everything from session creation onward
happens exactly as described in this PEP.

An index that supports this **MUST** document it, and **SHOULD** accept a ``staged`` field with the value
``true`` in the legacy ``multipart/form-data`` upload request. When that field is present, the index creates a
publishing session in the ``open`` state for the uploaded file's project and version, adds the file to it as a
:ref:`completed <file-upload-session-states>` file upload, and does not publish it. The index **SHOULD**
return the :ref:`publishing session creation response body <publishing-session-response>` from that upload,
including the ``links`` and ``session-token`` keys, so that the publisher can then use the endpoints in this
PEP to :ref:`preview <staged-preview>`, :ref:`publish <publishing-session-completion>`, or :ref:`cancel
<publishing-session-cancellation>` the session. An index **MAY** also create a session for an upload based on
its own policy or the project's configuration, without the field being present; this allows a project to
require that its releases are staged in a way that an upload client cannot bypass.

Because the legacy API uploads a single file per request, subsequent legacy uploads for the same project and
version **SHOULD** be added to the same open session, so that the release is still published as a unit.

A legacy client that is unaware of this PEP cannot issue a :ref:`publish request
<publishing-session-completion>`. Where an index has created a session on such a client's behalf, and the
session is subject only to automated processing, the index **MAY** publish the session itself once that
processing resolves without an adverse result, or once the period allowed for it elapses (see
:ref:`publishing-session-completion`). An index **MUST NOT** publish a session this way if the publisher has
configured the project to require a separate publishing authorization (see :ref:`authentication`).

Aside from how the session is created, this section changes no part of this API: the :ref:`state machine
<publishing-session-states>`, the status endpoints, stage previews, and the publish and cancel actions are the
same whether the session was created through this API or through a legacy upload.


.. _file-upload-mechanisms:

File Upload Mechanisms
Expand Down Expand Up @@ -1847,6 +1908,17 @@ as experience is gained operating Upload 2.0.
Change History
==============

* `01-Aug-2026 <https://discuss.python.org/t/pre-pep-staged-releases-separated-from-pep-694/107804/58>`__

* Add :ref:`Legacy Upload API Interoperability <legacy-interop>`, allowing a legacy upload to create a
publishing session (via a ``staged`` field, or by index or project configuration that an upload client
cannot bypass) so that staging is usable before upload tooling adopts this API, and allowing an index to
publish such a session itself once automated processing resolves or its window elapses.
* Note that the ``processing`` state **MAY** be used for asynchronous review such as malware scanning, with
an index-chosen window after which the review is treated as having produced no adverse result.
* Allow an index to require additional authorization, beyond upload permission, to publish or cancel a
session, so that duties can be separated.

* `29-Jul-2026 <https://discuss.python.org/t/pep-694-pypi-upload-api-2-0-round-4/108320>`__

* Add an **Atomic Publication and Conflicts** section. Specify that publication is atomic with respect to
Expand Down
Loading