Caller-scoped token-bucket quotas with distinct 429 and 503 classes, Retry-After on throttle, burst versus sustained windows, and tenant-fair sharing that never discloses a neighbor's identity.
Package status: reference context ready for human review. The contract and test scenarios are complete, but no claim is made that an adopting implementation has passed them.
Decision
Public and partner HTTP APIs admit a request only after a caller-scoped token bucket grants a token. Authenticated traffic is keyed by principal and tenant. Unauthenticated public routes use a documented anonymous class from hashed network range plus route class, never a neighbor tenant id. Burst and sustained refill are independent profile values. Personal quota exhaustion is a client throttle; shared admission exhaustion or limiter-store failure is a service class. Neighbor identity and remaining budget never appear in caller-visible headers, bodies, or logs.
Scope
- Admission for public-apis and partner-apis route classes in the adoption profile.
- Quota identity, token-bucket refill, Retry-After, shard fairness, and limiter keys that could leak another tenant.
Outside this block
- Circuit opening for a failed dependency (related circuit-breaker package).
- Remaining-deadline arithmetic (related deadline-propagation package).
Contract
- Limiter identity is the authenticated principal and tenant, or a documented anonymous class, never a raw neighbor tenant identifier.
- Burst tokens and sustained refill are evaluated independently; burst is allowed only while that identity still has refill credit.
- Personal quota exhaustion returns HTTP 429 with a catalog code and Retry-After computed from this caller's bucket.
- Shared-capacity exhaustion or limiter-store failure returns HTTP 503 with a different catalog code.
- On a shared shard, no tenant may occupy more than the profile share of shared admission slots in a window.
- Caller-visible responses and logs omit other tenants' identifiers, remaining quotas, and neighbor route statistics.
Implementation guidance
- Key buckets by principal and route class; do not key authenticated traffic solely by IP. Decrement atomically so lost updates cannot grant extra burst.
- When the limiter store is unreachable, fail closed to the capacity error class rather than admitting unbounded traffic.
Failure handling and safeguards
- Clients must distinguish personal throttle from provider capacity. If Retry-After cannot be computed for this caller, omit the header rather than copying a neighbor's delay.
- Bucket refill uses limiter-store time, not the client Date header.
Verification and operations
- Drive two tenants on one shard and prove one cannot starve the other beyond the profile share, and that 429 bodies never include the other tenant id.
- Alert when capacity-class rejects exceed the profile error budget.
The executable-looking examples in this package are fixtures and acceptance contracts. Run
python tools/validate.py from the collection root to check package structure and metadata; then
implement and execute the scenarios in the target repository.
Adoption assumptions
- Names and numeric values in
example.yamlare an adoption profile, not universal defaults. - The adopting team must map actors, data classes, error vocabulary, and ownership to its system.