I’ve been building a client against the current draft—a prototype in a fork of uv, prompted by astral-sh/uv#20789 ran into a few places where I wasn’t sure how to interpret the spec. Thought I’d flag them in case any are easy to clarify before implementations start diverging. I don’t think Round 4 covers these.
1. blake2b doesn’t pin a digest length.
The spec says any hashlib algorithm may be used as a key, with at least one from hashlib.algorithms_guaranteed, and the example shows:
"hashes": {"sha256": "...", "blake2b": "..."}
But hashlib.blake2b defaults to a 512-bit digest, while PyPI’s legacy upload API has always used Blake2b-256 (blake2_256_digest). Since the server has to choose a digest length to compute and compare, the key name alone isn’t enough—a client following the hashlib default and a server carrying over legacy behaviour would both believe they’re conformant and still disagree.
A possible fix would be to pin the digest length for variable-output algorithms, or name the key blake2b_256.
2. The mechanism object is snake_case while the rest of the protocol is kebab-case.
Everything else is api-version, expires-at, session-token, file-upload-session, but the mechanism object uses file_url and attestations_url. This feels like something that’s easy to normalize now, but harder once implementations exist.
3. The error example uses details; RFC 9457 specifies detail.
Since the PEP mandates RFC 9457 problem details, a client using an off-the-shelf RFC 9457 parser would silently miss that field.
4. Minor: the 409 on a duplicate name/version MUST carry a Location header, but the text doesn’t say whether it should be absolute. Resolving it relative to the endpoint handles both cases; explicitly stating it would remove the ambiguity.
On @EWDurbin question about listing open publishing sessions: from the client side, I don’t think it’s necessary for automation. The CI retry case only needs a deterministic way to re-address the session for a given (name, version), and the 409 + Location on duplicate creation already provides that. A list endpoint seems more useful for human cleanup and observability than for tooling.
Happy to be corrected on any of these—these are just things I noticed while reading the draft and implementing against it, rather than from testing against a real server, since there isn’t one yet.