Draft: fix(auth): emit the S01 error envelope on PyPI 401 responses

What

Unauthenticated requests on /<slug>/pypi/... returned a bare 401 with an empty body. That contradicts S34's Error Cases ("Auth missing on a non-public endpoint | 401, code: "unauthorized"") and fails the external conformance catalog's pypi.negative.unauthenticated row ("401 with a machine-readable error body"). twine and Poetry surface the response body text to the user, so the empty body also reads as an opaque failure in real clients.

How

  • bodyWriter now takes the request and commits the full 401 response (status line + body); noBody writes the bare status, ociBody is unchanged in behavior.
  • New pypiBody emits the S01 envelope via transport.WriteErrorcode: "unauthorized", message: "authentication required", and the correlation-derived request_id — matching what the middleware already does for its 404 path.
  • The npm/maven/api-v1/v2-auth-token rows keep their spec-pinned empty body: their specs have not been amended, and the equivalent ruling for those formats belongs to their owners.
  • S08's Error Cases table now records the PyPI body shape, deferring to S34 (which already mandated it — the implementation had followed S08's older wording).

Tests

  • Adds the PyPI row to the middleware negative-path table tests (it was the only dispatch row with no middleware coverage) and asserts the exact envelope wire shape via JSONEq.
  • go test -race ./internal/auth/ green; full build + golangci-lint clean.

e2e scenario impact

Confirms e2e.pypi.auth.invalid-credentials ("upload fails with a clear 401") — the envelope is what makes the 401 "clear" in twine's output. No new scenario: the behavior is covered by the external conformance catalog's pypi.negative.unauthenticated row.

Related to #365

Merge request reports

Loading
Loading