Repository navigation
M8: enforce /v1 rate limits per organization, not just per key - #27
Merged
Merged
Conversation
Part of M8 (design audit gap #7's third bullet: "it is enforced per caller/key, not both per key and per organisation"). An organization holding several API keys previously got an independent rate-limit budget per key -- N keys meant N times the effective limit, since each key's counter was tracked in total isolation. _rate_limited now hits two counters per request: the existing per-subject (API key or user session) one, and a new one keyed by organization_id. Both share the same limit value (the plan-aware override from the companion M7 work, or the global default); the request is rejected if either is exhausted. The response headers report whichever counter is actually binding (the smaller remaining count), so a key blocked purely by its organization's exhausted shared budget sees 0 remaining, not its own still-fresh per-key count. Tests: 363 passed. ruff clean. New coverage: two different keys in the same organization share one budget (a key with zero requests of its own is still blocked once a sibling key exhausts the shared budget), the organization counter doesn't leak across organizations, and the reported headers reflect the binding counter correctly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
Summary
Part of M8 (design audit gap #7's third bullet: "it is enforced per caller/key, not both per key and per organisation"). An organization holding several API keys previously got an independent rate-limit budget per key — N keys meant N times the effective limit, since each key's counter was tracked in total isolation.
_rate_limitednow hits two counters per request: the existing per-subject (API key or user session) one, and a new one keyed byorganization_id. Both share the same limit value (the plan-aware override from M7, or the global default); the request is rejected if either is exhausted. The response headers report whichever counter is actually binding (the smaller remaining count), so a key blocked purely by its organization's exhausted shared budget sees 0 remaining, not its own still-fresh per-key count.Test plan
pytest— 363 passedruff check .— clean🤖 Generated with Claude Code