Incremental PyPI Simple API caching mirror
Project description
PyTail
pytail is a minimal incremental PyPI caching mirror.
It is no longer a devpi clone. The new server only does four things:
- expose a valid Python Simple Repository API root at
/simple/ - lazily fetch and cache
/simple/<project>/from an upstream index - rewrite file links to a devpi-style
root/pypi/+f/...path - serve cached project pages and cached files on later requests
Plan
- Implement the smallest Simple API surface that
pipanduvactually need: root index, project index, normalized names, file links, and content negotiation for HTML vs JSON responses. - Keep the cache incremental: do not mirror the full upstream project list, only remember projects that have already been requested.
- Use conditional project refresh:
cache upstream project pages, preserve
ETag, and refresh a project after a TTL withIf-None-Match. - Keep package files immutable:
once a file URL is requested, cache it under
+files/and serve it from a stable devpi-style file route on later requests. - Store the index in SQLite: project pages, file metadata, and known-project state live in SQLite, while package bytes live on disk.
- Handle concurrent requests safely inside one process: per-project and per-file locks avoid duplicate upstream fetches and duplicate downloads.
- Explicitly drop old
devpigoals: no users, no ACLs, no upload API, no index inheritance, no replication, no snapshot format, no mirror whitelist, no web UI.
Spec Coverage
The implementation is intentionally narrow:
- PEP 503 HTML Simple API for
/simple/and/simple/<project>/ - PEP 691 JSON Simple API responses when
Acceptasks forapplication/vnd.pypi.simple.v1+json - preservation of
requires-python,yanked,gpg-sig,dist-info-metadata, andcore-metadatamarkers when they are present on the upstream project page - lazy local root index containing only already-cached projects
The implementation does not currently fetch or synthesize a full global upstream project list. That is deliberate: the root index is only a local catalogue of known projects, while project pages are fetched on demand.
Why This Is Enough
Package resolution for pip and uv depends primarily on per-project Simple API
pages. A full pre-fetched mirror root is not required for normal dependency
resolution as long as:
/simple/<normalized-project>/is available and correct- file links are reachable
- project metadata such as hashes and
requires-pythonare preserved
Run
cargo run -- \
--bind 127.0.0.1:3141 \
--upstream-base-url https://pypi.org \
--cache-dir .cache/pytail
Then point tools at it:
uv pip install --index-url http://127.0.0.1:3141/simple/ requests
pip install --index-url http://127.0.0.1:3141/simple/ requests
Configuration
--bind: listen address, default127.0.0.1:3141--upstream-base-url: upstream index origin, defaulthttps://pypi.org--cache-dir: local cache directory, default.cache/pytail--project-cache-ttl-secs: refresh age for cached project pages, default900--request-timeout-secs: upstream HTTP timeout, default15--verbose: enable debug logging for pytail
Package
Build a Python wheel with maturin:
maturin build --release
Publish to PyPI:
maturin publish --release
The wheel installs the pytail command as a native Rust binary.
Cache Layout
<cache-dir>/
index.sqlite3
+files/
root/
pypi/
+f/
f0a/
f3fc39e7459b0/
gradio_client-1.0.2-py3-none-any.whl
_url/
<url-digest>/
<filename>
- SQLite stores project cache entries, parsed file rows, upstream
ETag, and known project names +files/root/pypi/+f/<first-3-sha256>/<next-13-sha256>/<filename>stores hashed files in the same shape as devpi's filesystem layout+files/root/pypi/+f/_url/<url-digest>/<filename>is used only when an upstream file link does not provide a usablesha256hash
Non-Goals
- full PyPI mirroring
- authentication or private indexes
- package upload
- merge of local and upstream package sources
- replica mode or background synchronization
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytail-0.1.1.tar.gz.
File metadata
- Download URL: pytail-0.1.1.tar.gz
- Upload date:
- Size: 51.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9ab14589a82ceacb1848e91817ffefc20c322be2fd013eb70f47b87a390738b
|
|
| MD5 |
a0d723871aa20fddfd9b12bdcef2c498
|
|
| BLAKE2b-256 |
76dd166d88fc0e62097f22b9deceb1cd4585cbba43b61e478f75a327273b3953
|
Provenance
The following attestation bundles were made for pytail-0.1.1.tar.gz:
Publisher:
release.yml on AndPuQing/PyTail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytail-0.1.1.tar.gz -
Subject digest:
e9ab14589a82ceacb1848e91817ffefc20c322be2fd013eb70f47b87a390738b - Sigstore transparency entry: 1556729416
- Sigstore integration time:
-
Permalink:
AndPuQing/PyTail@4095e676401e813521f4c07a2ec3337cf9d552dd -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/AndPuQing
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4095e676401e813521f4c07a2ec3337cf9d552dd -
Trigger Event:
push
-
Statement type:
File details
Details for the file pytail-0.1.1-py3-none-win_amd64.whl.
File metadata
- Download URL: pytail-0.1.1-py3-none-win_amd64.whl
- Upload date:
- Size: 2.3 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42a072b872d4bb8f1aeb606a1e086617d7ab6f7738c24b7a539e492d6f32d97c
|
|
| MD5 |
4c48e0431b71efafe1bb2817e4e7ac33
|
|
| BLAKE2b-256 |
6ef257f231d5e812d90bceaf43d778344c2f02ad3904f2fecefbd7f4b2bd94ef
|
Provenance
The following attestation bundles were made for pytail-0.1.1-py3-none-win_amd64.whl:
Publisher:
release.yml on AndPuQing/PyTail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytail-0.1.1-py3-none-win_amd64.whl -
Subject digest:
42a072b872d4bb8f1aeb606a1e086617d7ab6f7738c24b7a539e492d6f32d97c - Sigstore transparency entry: 1556729663
- Sigstore integration time:
-
Permalink:
AndPuQing/PyTail@4095e676401e813521f4c07a2ec3337cf9d552dd -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/AndPuQing
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4095e676401e813521f4c07a2ec3337cf9d552dd -
Trigger Event:
push
-
Statement type:
File details
Details for the file pytail-0.1.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: pytail-0.1.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.5 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89d6e246d93c6330b0845d23f386b398afeb7392d5bbf9f467b7e31b9c6583be
|
|
| MD5 |
4f3d773db5b6e238e6fe5fde4d83dd1b
|
|
| BLAKE2b-256 |
a3574c177a1a8315502b21c7bc446cd5144c25eec4edf33bf1fcb6b136d3f2ab
|
Provenance
The following attestation bundles were made for pytail-0.1.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on AndPuQing/PyTail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytail-0.1.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
89d6e246d93c6330b0845d23f386b398afeb7392d5bbf9f467b7e31b9c6583be - Sigstore transparency entry: 1556730002
- Sigstore integration time:
-
Permalink:
AndPuQing/PyTail@4095e676401e813521f4c07a2ec3337cf9d552dd -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/AndPuQing
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4095e676401e813521f4c07a2ec3337cf9d552dd -
Trigger Event:
push
-
Statement type:
File details
Details for the file pytail-0.1.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.
File metadata
- Download URL: pytail-0.1.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
- Upload date:
- Size: 4.5 MB
- Tags: Python 3, macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39bb6c7e54be617776c3737034dd22cad3c351eb61137cb203dcc590ab17fa9a
|
|
| MD5 |
30b600bebe2f5d1d78359587ca7777fa
|
|
| BLAKE2b-256 |
38a8e36f2db6f188169568151b6c3a07bb3ef9fca57538fa063b6042a9d6b3ba
|
Provenance
The following attestation bundles were made for pytail-0.1.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:
Publisher:
release.yml on AndPuQing/PyTail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytail-0.1.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl -
Subject digest:
39bb6c7e54be617776c3737034dd22cad3c351eb61137cb203dcc590ab17fa9a - Sigstore transparency entry: 1556729840
- Sigstore integration time:
-
Permalink:
AndPuQing/PyTail@4095e676401e813521f4c07a2ec3337cf9d552dd -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/AndPuQing
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4095e676401e813521f4c07a2ec3337cf9d552dd -
Trigger Event:
push
-
Statement type: