Configuration¶
One file, tokenbiryani.yaml. ${VAR} and ${VAR:-default} are expanded from the
environment; a reference to an unset variable with no default is an error at load
rather than a mystery at runtime.
The complete annotated reference is
tokenbiryani.example.yaml, in the repository root.
This page covers what tends to need explaining.
Hot reload¶
The file is re-read when its mtime changes (server.hot_reload_seconds, 0 disables),
and POST /admin/reload forces it.
Accounts that survive a reload keep their runtime state — limit mirror, breaker, counters. Editing an unrelated setting must not hand a cooling account a clean bill of health and re-stampede an upstream. The exception is rotated credentials, which do clear a disabled account, because a rotated key is usually why it was disabled.
A file that fails to parse is rejected and the running config is kept.
Prices¶
Costs are reported, and spend caps enforced, only for models that have a price. Two ways to give them one.
The table that ships with this release, dated:
pricing: builtin
The Settings screen shows its as_of date beside every cost, so a stale table is
visible rather than silent. Check the current numbers at
anthropic.com/pricing.
Your own, which always wins:
pricing:
builtin: true # optional — start from the shipped table
claude-opus-4-*:
input: 15.0
output: 75.0
cache_read: 1.5
cache_write: 18.75
USD per million tokens. Prices live in a dated data file rather than in code for the reason they always did: the gateway must never bill you against a number nobody can attribute. A dated file the console shows the date of can be attributed. A dict compiled into a release cannot.
Without either, every cost reads —, and the Usage screen says so rather than
drawing an empty chart.
Spend caps are windowed¶
spend.window_hours (default 24) is a rolling window, not a lifetime total. A lifetime
cap on a persistent store would eventually wedge the gateway shut and stay that way,
which is not what an operator means by "cap".
Caps only survive a restart if the store does — store.backend: memory loses the
ledger, silently resetting every cap.
Per-conversation caps¶
A key cap bounds everything a key does. It cannot see a runaway inside that allowance — one agent loop resending a large prefix a few hundred times is the whole cap with that key's name on it, and there is nothing to look at until it trips.
sessions:
track: true # record per-session cost and turns
runaway_turns: 200 # what GET /admin/sessions flags
runaway_spend_usd: null
sessions.track writes two extra ledger rows per request; those rows are what the
caps below and the report are made of. Turning it off makes both go quiet — and stops
the caps being enforced, since the counters they read are the ones tracking writes.
Sizing the output lease¶
routing:
output_estimate: adaptive # or max_tokens
output_estimate_quantile: 0.95
output_estimate_min_samples: 20
adaptive leases what a model has actually been returning rather than the caller's
max_tokens, bounded by that ceiling either way — it can only reserve less. See
Routing for why the ceiling starves a pool.
Prompt-cache diagnosis¶
cache:
auto_breakpoint: false # leave it off unless you need it
advice_min_requests: 20
Diagnosis (GET /admin/cache-advice) is always on and costs nothing.
auto_breakpoint makes the gateway edit the caller's request, which it otherwise
refuses to do — see Why is my bill higher?.
Quota pacing¶
pacing:
enabled: true
mode: advisory # advisory | enforcing
curve: linear # or business_hours
weekly_budget_usd: null # required for API-key accounts
Subscriptions report their own rolling windows and are paced automatically. API keys report no weekly window at all, so without a stated budget they are simply not paced. Full detail in Spending the week on purpose.
Keys¶
keys:
- key: ${TOKENBIRYANI_KEY}
name: default
admin: true # required for /admin/*
models: ["*"]
pool: [] # empty means every account
rpm: null
spend_cap_usd: null
session_cap_usd: null # bounds one conversation
session_max_turns: null # within this key's allowance
priority: interactive
max_wait_seconds: null
admin gates the whole /admin surface, which exposes account ids, spend and key
management. Issue tenant keys without it. A gateway with no keys at all is
loopback development mode and stays open; it refuses to bind a public interface in
that state.
Keys minted at runtime live in the store instead — see Operating.
Binding¶
server:
host: 127.0.0.1
allow_remote: false
The gateway refuses to bind anything but loopback without allow_remote: true and
at least one configured key. Both, not either.