* AI-1238: per-app daily limits for the router
An app and its organization set what one end user may spend in a day in
their policy's quota; the deployment's ROUTER_RATE_LIMIT_* numbers are
only the default. The organization's number is a ceiling, 0 is no cap.
Server-side callers are never counted, mock answers are neither checked
nor debited, the address bucket is per app, and counted HTTP answers
carry X-RateLimit-Limit, -Remaining and -Reset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: hold the backend-reply quota test in an app of its own
Other suites answer from the same address under the shared fixture app,
so its address bucket could be spent mid-test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: test the gaps the review found in the daily limit rules
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: keep an app's daily limit when only its spend is unread
- policy: Limits no longer drops the limits the policies set when the
budget's spend read fails; an unread policy still yields the defaults.
- quota: Source.Limits returns Limits only; the error nobody used is gone.
- cmd/router: one guard for "no limit can hold"; limitsApply, its stub
and test removed; the nil Enforcer answers the defaults itself.
- policy tests: the merge rule is one table-driven test; a real
spend-only failure (policies cached in Redis, Postgres closed) is tested.
- quota: one clock reading per Allow for the day's key and the reset;
lower's parameters renamed; scratch-file reference removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: leave the server-side check to the limiter, tidy quota tests
withQuota asks only whether the customer is known; Limiter.keys is the one
server-side check. A test comment no longer points at a scratch file, and the
refused request is read once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: one test proves a mock answer is neither checked nor counted
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: let a browser read the rate-limit headers of an answer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: say at startup when no default daily limit is set and only app policies apply
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: keep the put helper's comment, test a quota that sets no number
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: delete Caller.Anonymous, which nothing calls now
* AI-1238: derive Retry-After from the status reset, read the midnight once
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: ask the daily limit through Session.allow
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: wrap the quota debit in Session.debit, beside allow
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: pass quota.New a nil Source when there is no policy Enforcer
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: name the quota type Quota, beside Budget, and its converters quotaOf and quotaFor
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: gofmt api/policies.go and rename store.QuotaPolicy to store.Quota
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: bound Debit's policy lookup by writeTimeout too
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: rename the shadowing real variable in the mock-quota test
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: name the exposed CORS headers once
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: say what Allow's nothing-to-count-against case means now
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: let only Stream staff set an app's daily limit, through the ops key
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: regenerate the clients and describe the daily limit as Stream staff's to set
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: record every staff change of an app's daily limit, with who, why, when and the numbers before and after
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: serialise an app's quota changes with an advisory lock, since its first change has no row to lock
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: let each app set its users' daily limit, charge each answer to whoever sent the turn
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: regenerate the clients and describe the daily limit as the app's own
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: say an empty sender with Caller.IsZero
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* AI-1238: name the daily quota headers X-Quota-* so the gateway's X-RateLimit-* are not doubled
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* AI-1238: give listen its own copy of the inherited socket in its test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>