Blog
Technical Deep Dives

Quotas and cost accounting: the hold-and-settle engine

How Simmetric Chat's quota engine covers tokens, storage, and real money - the ledger-backed no-second-meter design, the fail-open unset semantics, and the two-phase hold-and-settle reservation that turns spending limits from a report into a gate.

Simmetric Team8 min read14/10/2026
Quotas and cost accounting: the hold-and-settle engine

Ceilings that apply before the spend happens

Usage ceilings in a multi-user AI install usually live in a dashboard - a report you read after the bill exists. Simmetric Chat's quota engine puts them earlier: in the request path, before the first LLM call. The engine covers three kinds of ceilings - tokens, storage, and cost (real-money math, in cents) - and it does so with the same shape everywhere: one ledger as the usage truth, derived balances, pre-turn gates, and failure that is always explicit.

This article walks the design: the ledger-backed "no second meter" invariant, the resolution chain that decides each user's limit, how the rolling reset works (the history is never deleted), and the two-phase hold-and-settle reservation that makes a cost quota a real gate rather than a delayed alarm.

An operator tool (the Community feature class). Facts per FACT-CENSUS.md F-QUOTA-1.

No second meter

The one non-negotiable invariant: the consumption truth stays inside the platform's EXISTING token-usage ledger. Balances, holds, and reset anchors are derived state - never a second counter table that could drift from reality. The corollaries:

  • A reset is a ledger anchor, not a wipe. A reset writes an anchor row that bounds the rolling window; the usage/cost history is never deleted or zeroed. Operators can audit backwards any time.
  • The gates are service-level and pre-turn - they run where the domain data lives (before the first LLM call in the orchestrator, before the ingest in the upload paths). There is no middleware to bypass: the check is in the service path itself.
  • Money math is Decimal-to-cents. Every cost computation converts through the decimal layer and lands as INTEGER cents - never floating point, never rounding-drift.

The resolution chain

A user's ceiling resolves through a three-tier chain, in order:

  1. The per-user override - the admin sets a named user's token/storage/cost ceilings directly.
  2. The install preset - the install-level default keys in the system configuration (token ceiling, storage GB, cost ceiling in EUR).
  1. Unlimited - the "0"/empty sentinel means NOT configured, and the engine fails open: zero behavior change for installs that never touch quotas.

Fail-open is a feature with an explicit shape: the quota engine is an operator's opt-in tool, and "not configured" must never silently become "blocked".

The three kinds

Token quotas roll over a windowed usage sum from the ledger, bounded by the latest reset anchor. The resolution returns a limit plus its source tier - the operator's UI can say which row decided, per user. Exceeding = a named quota error whose payload the frontend, the widget, and the connector handlers all key on.

Storage quotas enforce in the upload paths before dispatching to the collector: the per-user override, the install default, or unlimited.

Cost quotas - the real-money arm - are the hold-and-settle design, below.

Hold-and-settle: a two-phase reservation

A token spend happens when the tokens happen; a euro spend happens while the stream runs. The cost gate therefore reserves BEFORE the LLM call and settles at the usage seam:

  1. Reserve (before the call) - a conditional update whose predicate the database evaluates row-locked (spent + estimate ≤ limit): N concurrent requests SERIALIZE instead of all racing through a stale check, plus the hold row in the same transaction (the reservation is atomic with the check).
  2. Settle (at the seam) - the hold finalizes and refunds estimate minus actual. The accepted window is the in-flight overshoot (actual > estimate) - no mid-stream kill for a cost reason; the invariant at rest is exact: settled spend + open holds ≤ limit.

That pair gives the cost quota the property a report can never have: the ceiling applies before the spend commits to the model, by a row-locked reservation that is honest under concurrency.

Pricing: per-model, snapshot-frozen, never silently free

The pricing source per row: a frozen model-costs snapshot where one exists (a later rate edit never rewrites history), else the install's fallback rates (the SC-4 doctrine: an unpriced model never bypasses the cost quota - a non-zero built-in floor applies with a warn-once log, so nothing can appear free by omission). Currencies: the ledger tracks per-currency totals (USD, EUR, GBP, JPY, CNY, INR in v1 - no conversion); the cost lands at the stream's done event.

Setup lives in the operators' path: Settings → Providers → the model's pricing action (the per-1M-token input/output rates + the currency), or per-user pricing for own-provider keys. N/A means "configure a rate"; $0.00 means explicitly free (the local models backfilled to 0).

The widget principal

Anonymous widget visitors are not users: the widget's consumption binds to the widget's organization principal - widget traffic draws from that org's quota chain rather than inventing per-visitor identities. Connectors' traffic and internal chats share the same one orchestrator, so the pre-turn gates cover all three classes of chat surface uniformly.

Reading on

The guide carries the full operator reference - the keys, the endpoints, the reset behavior, the pricing detail - at /en/docs/quotas-and-costs:

Provenance: Simmetric Chat docs/QUOTAS.md (v0.28.1, Phases 207/216) + docs/MODEL_COSTS.md (Phases 203/209/216), quotaService.ts/costQuotaService.ts - at the census's pinned docs state (SHA aca5b8ff).

Related articles