Documentation

PyPI

PyPI defines the wheel and sdist formats and the HTTP protocol that installers use to find and download them. A wheel (.whl) is a built package that an installer can install; an sdist (source distribution, a .tar.gz or a .zip) contains the source used to build a wheel. Both are artifacts, the files an installer fetches.

PyPI-to-peryx mapping

peryx uses Python's index, project, version, and file terms for PyPI. See the index model and glossary.

The binary includes the PyPI implementation. An index with ecosystem = "pypi" activates it. Omitting ecosystem selects PyPI. Without a PyPI index, peryx installs no PyPI routes, jobs, or services. [availability] selects coordination for the process.

Python termperyx conceptMeaning
index (--index-url)indexClient endpoint; a cached index fronts one upstream
project or packageprojectOne distribution name, such as requests
release or versionversionOne released project version
distributionartifactA wheel or source-distribution file
filefileOne content-addressed distribution file
publish or uploaduploadAdd a distribution to a hosted index
install or downloaddownloadFetch a distribution through peryx
pull-through mirrorcached indexRead-through proxy for one upstream index

peryx uses the role names cached, hosted, and virtual, plus shadowing, across ecosystems.

PyPI index roles

The three index roles map to PyPI as follows:

  • cached: a read-through cache of an upstream Python index such as pypi.org. On a miss, peryx fetches, stores, and serves the project page or artifact. Later requests use the stored copy. A cached index can front pypi.org, TestPyPI, Artifactory, or a GitLab registry.
  • hosted: a store for wheels and sdists published through the standard upload API. Twine or uv publish writes the files without using an upstream.
  • virtual: an ordered stack of cached and hosted indexes under one URL. The default mode unions versions and keeps the first occurrence of each filename. Clients use one index-url without --extra-index-url. Configure project isolation when a hosted project must exclude cached candidates with other filenames.

A cached route retries upstream server errors, timeouts, and 429 responses with bounded backoff. A valid Retry-After delay or HTTP date takes precedence, capped at 30 seconds.

Protocol

Python installers speak the Simple API. An index exposes one page per project with links to its files. peryx supports these forms:

  • PEP 503: the original HTML page of download links. peryx parses it from upstreams that only speak HTML.
  • PEP 691: the modern JSON form of the same data. peryx canonicalizes every upstream to this once, at fetch time, and serves JSON (with HTML on request) downstream.
  • PEP 658/714: a .metadata sibling next to each file lets a resolver read a few kilobytes of dependency metadata without downloading a wheel. peryx serves it and synthesizes it with byte-range reads when an upstream lacks it.
  • Legacy upload API: the POST endpoint twine and uv publish use to publish into a hosted index.

For the full standards map, see standards.

Configure clients

Assume peryx is running at http://127.0.0.1:4433 with the default virtual route root/pypi. Installers read from .../simple/; publishers post to the route root.

Install

pip

# one-off
pip install --index-url http://127.0.0.1:4433/root/pypi/simple/ requests

# persistent: environment
export PIP_INDEX_URL=http://127.0.0.1:4433/root/pypi/simple/

# persistent: pip.conf (~/.config/pip/pip.conf or venv pip.conf)
# [global]
# index-url = http://127.0.0.1:4433/root/pypi/simple/

uv

# one-off
uv pip install --index-url http://127.0.0.1:4433/root/pypi/simple/ requests

# persistent: environment
export UV_INDEX_URL=http://127.0.0.1:4433/root/pypi/simple/

Poetry

poetry source add --priority=primary peryx http://127.0.0.1:4433/root/pypi/simple/

Publish

Publishing can use a hosted layer with an upload token or exchange a GitHub Actions or GitLab CI identity for a short-lived token. The CI flow scopes that token to one repository route and the configured project globs.

Inject the token as TWINE_PASSWORD with TWINE_USERNAME=__token__, or as UV_PUBLISH_TOKEN, through your secret environment before publishing.

Twine

twine upload --repository-url http://127.0.0.1:4433/root/pypi/ dist/*

uv

uv publish --publish-url http://127.0.0.1:4433/root/pypi/ dist/*

.pypirc

# ~/.pypirc
[distutils]
index-servers = peryx

[peryx]
repository = http://127.0.0.1:4433/root/pypi/
username = __token__
password = <token>

GET /root/pypi/+api returns a ready-made .pypirc snippet for any configured route.

Generate configuration files

peryx config-snippet prints pip.conf, uv.toml, or .pypirc without starting the server:

peryx config-snippet --base-url https://packages.example --index root/pypi pip.conf
peryx config-snippet --base-url https://packages.example --index root/pypi uv.toml
peryx config-snippet --base-url https://packages.example --index root/pypi .pypirc

--base-url is the public origin, including any proxy path prefix and excluding the index route. pip.conf and uv.toml work for read-only and writable indexes. .pypirc requires a hosted upload target that accepts writes, and the generated file contains <upload-token> instead of the configured secret.

PyPI metadata pages use the transformed-page memory cache controlled by the top-level hot_cache_bytes setting. Setting it to 0 disables that cache without removing stored pages or distributions.

Web UI

The PyPI implementation labels searchable entities as packages and opens an index card on its project list. A project page shows the long description, summary, install command, versions, dependencies, project links, classifiers, and files. Release groups follow PEP 440 order. Files that do not map to one declared release remain visible under Legacy or unassociated files.

Each file row shows size, upload time, sha256, yank state, metadata availability, source, and byte availability. A file with PEP 740 provenance has a disclosure for its predicate types and subject binding. The disclosure reports claims and binding checks; it does not claim that peryx verified a Sigstore signature, certificate, or transparency log.

Wheels, zip files, zipped eggs, .tar, .tar.gz, and .tgz files expose archive contents. The browser lists members and previews bounded text chunks. Other compressed tar formats remain download-only.

An upload-enabled route adds an Upload page for one wheel or .tar.gz source distribution. A project page adds Manage uploads actions for yank, un-yank, delete, and restore. These controls call the same endpoints documented in yank and delete packages.

A project page with description, metadata, releases, and distribution files A project page with description, metadata, releases, and distribution files
A project page with description, metadata, releases, and distribution files
  • Performance

    Cold and warm installs, file throughput, parallel CI, and request throughput for peryx, devpi, proxpi, pypiserver, and pypicloud.

  • Upload compatibility

    Wheel names, declared digests, equivalent versions, mutation paths, provenance, and durability rules.

  • Simple API response guarantees

    Upstream-derived API versions, reachable signature markers, provenance objects, and canonical URL redirects.

  • Block revoked PyPI content

    How digest revocations govern PyPI discovery and downloads.

  • Tutorials

    Learning-oriented lessons for serving and publishing Python packages through peryx.

  • How-to guides

    Recipes for publishing, CI caches, virtual indexes, yanking, and deletion in the PyPI ecosystem.

  • Reference

    PyPI protocol, policy, upload, mirroring, and availability reference.

  • Migration

    Move to peryx from another Python index server: devpi, proxpi, pypiserver, pypicloud, bandersnatch, and more.

On this page