Token budgets on Envoy Gateway's global rate limit, costed from response metadata
Status: Partly superseded by ADR-0053
Partly superseded: Option 1 (Envoy Gateway’s global rate limit), for the agent router only. The budget model stands; ai-gateway’s budgets stay on Envoy Gateway.
Date: 2026-09-25
Deciders: Smana (Platform Owner)
Related Spec: SP4 — LLM complexity routing
Context
Frontier models cost real money, and a looping agent is a denial-of-wallet. Every principal needs a daily token cap: a run, the agent fleet, each human, each API-key client, plus a kill switch on frontier spend. They must be enforced at the gateways, the only path to a provider.
Decision Drivers
- Enforced synchronously in the data path, with no custom data-path code.
- Keyed on identity headers set from a verified credential.
- One bucket per principal across every route the principal can reach.
Considered Options
Option 1: Envoy Gateway global rate limit, cost from response metadata, Valkey
Pros:
- Envoy Gateway’s own global rate-limit API. Agent Router’s
llmRequestCostswrites the token count into metadata, which the rule charges after the response. shared: truegives one bucket across routes, andshadowModegives a measured dry run.
Cons:
- Charged after the response, so an admitted stream can overshoot by one response.
- Windows are fixed UTC days, not sliding.
Option 2: Agent Router QuotaPolicy
Pros:
- Purpose-built for token quotas.
Cons:
- Partly unimplemented:
ServiceQuotais not wired. - In its only mode (Shared), a request passes if any matching bucket has room, so a per-principal cap never binds while a default bucket has headroom.
Option 3: A custom ext_proc
Pros:
- Exact per-run caps with any logic.
Cons:
- Custom code in the data path of every request, and a new SPOF.
Option 4: LiteLLM budgets
Pros:
- Mature budget features.
Cons:
- A second proxy in front of or behind Agent Router, duplicating routing, auth and keys.
Decision Outcome
Chosen option: “Option 1”
Rationale: It enforces in the data path with no custom code, and shared plus shadowMode
make it both correct and safe to roll out.
Consequences
Positive
- A breach answers
429before any provider token is spent on the next request. - Shadow counters measure every rule for a week before enforcement.
Negative
- The exact per-run cap (
spec.budget.maxTokens) cannot live at the gateway, because a ServiceAccount token carries onlysub. The gateway will hold a 5M per-run ceiling (B1, SP4 PR 2); until then SP3’s run meter, which revokes a run at its own cap, is the only per-run cap. - A store outage admits traffic (
failClosed: false). - Envoy Gateway accepts one Gateway-level
BackendTrafficPolicyper Gateway, so all of a Gateway’s budgets live in one object.
Neutral
- Every rule must set
shared: trueand, for now,shadowMode: true. A render gate (assert-ai-gateway.py, in ai-native-ref) fails the build otherwise.
Implementation Notes
| Step | Scope | State |
|---|---|---|
| SP4 PR 1 | The rate limit, the KVStore, and B3–B5 on ai-gateway, all in shadow | Built |
| SP4 PR 2 | B1–B2 on agent-router, in shadow; the oidc listener that sets x-ar-human | B1–B2 built in shadow on agentgateway (ADR-0053), counting runs from its cutover; the oidc listener not built |
| SP4 PR 7 | Enforcement | Not built |
B3, the per-human budget, counts nothing until PR 2’s oidc listener sets x-ar-human: today that
header is only stripped from client requests.
2026-10-16: B1–B2 run on agentgateway (ADR-0053): infrastructure/base/agent-gateway/policy-budgets.yaml,
ratelimit.yaml and kvstore.yaml. B3–B5 stay on ai-gateway.