Per-user quotas
Per-user quotas cap how much each user can consume — by cost, tokens, or request
count — over a configured period. Enforcement happens as early as possible in the
request lifecycle: an over-quota caller is rejected with 429 before any
body parsing, signal evaluation, or model call.
Quotas build on persistence and require token-based identity
(auth.tokens_file) to be meaningful.
Defining limits
Section titled “Defining limits”Attach a quota to a token entry in your tokens file. Any dimension left unset
is unlimited; a user with no quota is unlimited.
tokens: - id: alice secret: env: TOKEN_ALICE quota: max_cost: 50.0 # dollars per period max_tokens: 5000000 # prompt + completion tokens per period max_requests: 10000 # requests per period
- id: bob # no quota → unlimited secret: env: TOKEN_BOBGive each dimension a positive value. Unlimited is expressed by omitting the
dimension, never by setting it to 0 — a max_requests, max_tokens, or
max_cost of 0 (or any non-positive max_cost) is rejected at config-load
time, e.g. quota.max_requests must be greater than 0 (omit the field for unlimited).
Quotas are loaded into memory at startup, so the enforcement check is a couple of atomic reads with no database round-trip.
Period semantics
Section titled “Period semantics”The accounting period is set once, globally, on the persistence config:
persistence: enabled: true quota_period: monthly # daily | weekly | monthly | totaldaily— resets at UTC midnight.weekly— resets at UTC midnight on Monday.monthly— resets at UTC midnight on the first of the calendar month (default).total— never resets (a lifetime cap).
When a period rolls over, a user’s current-period consumption resets to zero while their lifetime totals are preserved. The period boundary is persisted, so both the boundary and the current-period consumption are restored after a restart — a user who was over quota before a restart stays over quota until the period actually rolls over.
Enforcement
Section titled “Enforcement”When an identified user’s current-period consumption meets or exceeds any of
their limits, the next request is rejected with HTTP 429 and an
OpenAI-compatible error:
{ "error": { "message": "You have exceeded your usage quota for the current period.", "type": "insufficient_quota", "code": "insufficient_quota" }}The check runs right after the user is identified and before any expensive work,
so an over-quota request never reaches a model. Each rejection increments the
llmsoup_quota_exceeded_total counter (labeled by user_id).
Anonymous requests
Section titled “Anonymous requests”Requests with no token identity are attributed to a single reserved anonymous
bucket and are never counted against any identified user’s quota. How the
anonymous bucket itself is treated is configurable:
persistence: anonymous_quota: exempt # exempt | rejectexempt(default) — anonymous requests are never rejected for quota.reject— anonymous requests are rejected with429while quotas are in use, effectively requiring token identity for access.