Documentation

Policy settings

PyPI adds package-format rules to the core [index.policy] keys.

Policy and access globs match a project after PEP 503 normalization: lowercase the name and replace each run of ., -, or _ with -.

KeyMeaning
fallback_modeVirtual-index selection: fallback, private-first, or no-fallback
allow_versionsAccepted PEP 440 version specifier set
allow_package_typesAccepted distribution types: wheel or sdist
block_package_typesDenied distribution types
allow_wheel_pythonsAccepted wheel Python tags
block_wheel_pythonsDenied wheel Python tags
allow_wheel_platformsAccepted wheel platform tags
block_wheel_platformsDenied wheel platform tags
min_release_age_secsMinimum age from the Simple API upload-time
required_attestationsRequired in-toto predicate types on hosted uploads
attestation_modeenforce rejects missing attestations; audit records and admits
[index.policy]
fallback_mode = "private-first"
allow_versions = ">=1,<3"
allow_package_types = ["wheel"]
block_wheel_platforms = ["win_amd64"]
min_release_age_secs = 604800
required_attestations = ["https://docs.pypi.org/attestations/publish/v1"]
attestation_mode = "enforce"

private-first serves hosted candidates when both hosted and cached members contain a project. no-fallback refuses to query an immediate cached member. fallback preserves filename-level merging. protected_names takes precedence over all three modes.

Project isolation

The default fallback mode keeps the first occurrence of each filename and merges the remaining filenames. If a hosted layer contains acme-1.0-py3-none-any.whl and a cached layer contains acme-9.0-py3-none-any.whl, the project page contains both files. An installer can select 9.0.

Set private-first on the virtual route to exclude cached files whenever a hosted member contains the project:

[[index]]
ecosystem = "pypi"
name = "pypi"

[[index.upstream]]
name = "primary"
url = "https://pypi.org/simple/"

[[index]]
ecosystem = "pypi"
name = "hosted"
hosted = true

[[index]]
ecosystem = "pypi"
name = "packages"
layers = ["hosted", "pypi"]
write_target = "hosted"

[index.policy]
fallback_mode = "private-first"

With this policy, uploading an acme file to hosted removes all cached acme candidates from the packages project page. no-fallback excludes the immediate cached member for all projects. protected_names denies cached candidates for matching name globs in each fallback mode. The team-index tutorial adds upload credentials and commands for a runnable setup.

Release-age policy hides files with no upload-time because their age cannot be established. Attestation policy checks the bound statements' predicate types; it does not verify signatures, certificates, transparency logs, or publisher identity. See upload attestations for validation order.

Retention ordering

PyPI retention plans order versions under PEP 440. 2.0 ranks after 2.0rc1, and 2.0+local ranks after 2.0. Equivalent spellings such as 1.0 and 1.0.0 share one rank, so keep-latest counts releases instead of distribution filenames.

A version that does not parse as PEP 440 follows valid versions and uses its string as a stable tie-break. Within one version, plans order distribution filenames and digests. The visibility selector accepts active, yanked, and hidden for Python package records.

Preview decisions

peryx policy dry-run --index root/pypi --project flask scans cached Simple pages and hosted file records without fetching upstreams or changing the served index. It prints tab-separated denial rows with the action, route, normalized project, filename, version, rule, field, and reason.

Upload quotas

max_project_size_bytes bounds logical bytes for one normalized project. A hosted upload reserves its distribution size after validation and commits the reservation in the transaction that makes the filename visible. Failed validation, storage, metadata, project-status, cancellation, and disconnect paths release pending capacity. Re-uploading an identical filename is idempotent and adds no allocation.

A virtual upload route combines its project limit with the target hosted index. The lower limit applies. Audit mode applies only when every configured layer enables quota_audit; any enforcing layer keeps the combined policy enforcing. A denial returns 403 Forbidden with rule max-project-size. Lowering a limit below current use does not block reads.

quota_audit = true records a violation and admits the upload. Decisions increment peryx_pypi_quota_admitted_total or peryx_pypi_quota_rejected_total without a project label. With neither quota mode enabled, uploads perform no reservation.

On this page