Skip to content

✨ feat(oci): enforce repository quotas on pushes#608

Open
gaborbernat wants to merge 3 commits into
tox-dev:mainfrom
gaborbernat:feat/enforce-oci-push-quotas-479
Open

✨ feat(oci): enforce repository quotas on pushes#608
gaborbernat wants to merge 3 commits into
tox-dev:mainfrom
gaborbernat:feat/enforce-oci-push-quotas-479

Conversation

@gaborbernat

@gaborbernat gaborbernat commented Jul 18, 2026

Copy link
Copy Markdown
Member

Peryx has accounted a repository's deduplicated bytes, projects, and versions since #477 (merged in #588), but the OCI hosted-push path never consulted that substrate, so a registry could not bound what a repository stored. This change enforces those limits on OCI pushes, letting an operator cap a repository's accounted bytes, project count, and versions per project. 🔒

Four neutral [index.policy] keys (max_accounted_bytes, max_projects, max_versions_per_project, and quota_audit) build a QuotaLimits set, with max_file_size_bytes serving as the per-file bound. A blob upload, whether monolithic or chunked, along with a cross-repository mount and a manifest publication, reserves capacity before the content becomes discoverable, commits that reservation in the same transaction that records the metadata through commit_driver_txn_with_quota, and releases it when the write fails. The push skips a digest the repository already serves, so a re-push, a mount of a present blob, and racing uploads of one digest charge its bytes once. A push over a limit answers the distribution-spec DENIED code (403) naming the crossed counters and publishes nothing, leaving the blob without membership and the manifest absent from tag and digest discovery.

An index that sets none of the repository, project, or version limits keeps its original write path and pays nothing for the machinery. Setting quota_audit = true records each violation on the durable reservation record and admits the push, so an operator can size limits against real traffic before turning enforcement on. Every decision increments the quota_admitted or quota_rejected metric family, scoped to the hosted role without repository or project labels. The change documents the keys, the error contract, deduplication accounting, audit mode, and metrics.

Closes #479

@read-the-docs-community

read-the-docs-community Bot commented Jul 18, 2026

Copy link
Copy Markdown

@gaborbernat gaborbernat added the enhancement New feature or request label Jul 18, 2026
@gaborbernat
gaborbernat enabled auto-merge (squash) July 18, 2026 03:18
@gaborbernat
gaborbernat force-pushed the feat/enforce-oci-push-quotas-479 branch from dd35163 to 881be80 Compare July 18, 2026 04:09
Add the neutral quota keys the accounting substrate admits against —
max_accounted_bytes, max_projects, max_versions_per_project, and
quota_audit — to the index policy, alongside accessors and an
enforces_quota gate that stays off until a repository, project, or
version limit is set. The per-file size limit enforces itself on the
byte stream, so it does not switch accounting on by itself.

The OCI push path reads these next; PyPI adopts them in later work.
Bracket every hosted push with a quota reservation. A blob upload, a
cross-repository mount, and a manifest publication each reserve capacity
before the content becomes discoverable, commit that reservation in the
same transaction that records the metadata, and release it when the
write fails. A digest a repository already serves is not reserved again,
so a re-push, a mount of a present blob, and racing uploads of one
digest charge its bytes once.

A push over a limit answers the distribution-spec DENIED code and
publishes nothing; audit mode records the violation and admits the same
push. An index that sets no quota keeps its original write path, so an
unmetered registry pays nothing for the machinery. The registry counts
each decision under the quota_admitted and quota_rejected families,
scoped to the hosted role and free of repository or project labels.
Record that the OCI registry now enforces the quota keys: the identity
mapping, the DENIED error contract, deduplication, audit mode, and the
decision metrics. Document the new policy keys and cross-link the
accounting model from the configuration reference.
@gaborbernat
gaborbernat force-pushed the feat/enforce-oci-push-quotas-479 branch from 881be80 to 5eded8c Compare July 18, 2026 04:21
@codspeed-hq

codspeed-hq Bot commented Jul 18, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 32 untouched benchmarks
⏩ 94 skipped benchmarks1


Comparing gaborbernat:feat/enforce-oci-push-quotas-479 (5eded8c) with main (9adc775)

Open in CodSpeed

Footnotes

  1. 94 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enforce quotas on OCI pushes

1 participant