AI budget
Per-tenant AI spend caps, cutoff modes, and call-rate limits that make catastrophic cost-drain impossible by construction.
AI budget
The AI budget is the strict cost gate every AI call passes before Pact invokes a model provider. It exists so that a prompt injection, a compromised session, or a runaway loop can never quietly drain your AI credits — anything over the cap is refused at the door, before the provider is called and before a ledger row is written.
The control lives at Admin → Security → AI budget and is backed by
core.ai.budget, enforced from core.ai.client.AIClient via
check_budget_or_raise(...) on every call.
This is real and enforced
Budget checks run inside the AI client for every feature — not as an
advisory dashboard number. When a cap is breached the call raises
AIBudgetExceededError and a row is written to
ai_budget_blocks so you can see exactly what was refused and why.
The five layers
Checked in order on every call:
- Per-tenant monthly spend cap — the hard ceiling. Sums
ai_usage.cost_centsfor the current calendar month againsttenant_ai_budget.monthly_cap_cents. - Per-tenant daily spend cap — optional, scoped to today (UTC). Limits blast radius without lowering the monthly cap.
- Per-user daily spend sub-cap — optional. Stops one rogue user from draining the whole tenant pool.
- Per-tenant calls-per-minute — in-memory sliding window, default 60. Catches loops before they burn spend.
- Per-user calls-per-minute — default 20, within a tenant.
Layers 4 and 5 are intentionally in-memory (per replica) rather than Redis: the spend cap in the database is the strict gate, and the rate limit is a soft gate that catches runaway loops early. This avoids the failure mode where a Redis blip would let cost-drain succeed.
Cutoff modes
The monthly and daily caps share a cutoff_mode (VALID_CUTOFF_MODES):
hard_block(default) — refuse every call once the cap is hit.soft_block— refuse the expensive tiers (default/complex) but keep allowing the cheapextract(Haiku) tier, so critical classification paths stay alive while a runaway elsewhere is investigated.warn— log and write an audit event, but allow the call. For trusted internal tenants who only want telemetry.
Default budget
Tenants without an explicit tenant_ai_budget row inherit DEFAULT_BUDGET:
- $20.00 / month (
monthly_cap_cents = 2000) hard cap - No daily cap, no per-user sub-cap
- 60 tenant calls/minute, 20 per user
alert_threshold_pct = 80— surfaces an alert at 80% of the caphard_blockcutoff
Admins raise caps or switch to warning-only mode by creating an explicit row.
API
All routes are admin/owner only and tenant-scoped from the auth context:
GET /v1/admin/security/ai-budget # config + live spend + EOM projection
PUT /v1/admin/security/ai-budget # upsert config
GET /v1/admin/security/ai-budget/blocks # recent block events
The GET also returns a linear end-of-month spend projection and a per-feature
cost breakdown (from the ai_usage ledger). Every block is auditable via
ai_budget_blocks (alembic 0149_tenant_ai_budget).
Filed under Security on purpose
Budget caps live under /admin/security, not a standalone billing
page, because they cap the blast radius of prompt-injection and chat-jacking
attacks. An admin doing a security review finds them alongside rate limits,
network policy, and the audit log.
Kill switch
The same admin surface exposes the AI kill switch (core.ai.kill_switch) —
the outermost break-glass control. It can disable AI for a single tenant, and an
auto-trigger engages it when suspicious activity spikes (3+ injection blocks
or 10+ rate-limit blocks in 5 minutes), writing an auto_engaged row to
ai_kill_switch_events. Recovery is always manual. See
AI defense for the full detection pipeline.