✨ feat(oci): enforce repository quotas on pushes#608
Open
gaborbernat wants to merge 3 commits into
Open
Conversation
gaborbernat
enabled auto-merge (squash)
July 18, 2026 03:18
gaborbernat
force-pushed
the
feat/enforce-oci-push-quotas-479
branch
from
July 18, 2026 04:09
dd35163 to
881be80
Compare
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
force-pushed
the
feat/enforce-oci-push-quotas-479
branch
from
July 18, 2026 04:21
881be80 to
5eded8c
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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, andquota_audit) build aQuotaLimitsset, withmax_file_size_bytesserving 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 throughcommit_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-specDENIEDcode (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 = truerecords 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 thequota_admittedorquota_rejectedmetric 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