Quotas und Kosten — Nutzungs- und Ausgaben-Obergrenzen
Die Quota-Engine deckt drei Gattungen ab: die Token-Quota und die Storage-Quota (Phase 207) samt der Kosten-Quota (Phase 216, hold-and-settle über die echte-Geld-Mathematik). Die Quotas sind das Betreiberwerkzeug für Nutzungsobergrenzen in Mehrbenutzer-I…
Die Quota-Engine deckt drei Gattungen ab: die Token-Quota und die Storage-Quota (Phase 207) samt der Kosten-Quota (Phase 216, hold-and-settle über die echte-Geld-Mathematik). Die Quotas sind das Betreiberwerkzeug für Nutzungsobergrenzen in Mehrbenutzer-Installs; sie binden jeden Benutzer mit konfigurierter Quota — Administratoren eingeschlossen — und eine nicht gesetzte Quota heißt unbegrenzt (fail-open: null Verhaltensänderung für bestehende Installs).
Community-Funktion (Betreiberwerkzeug). Quellen:docs/QUOTAS.md(Phase 207 + 216),docs/MODEL_COSTS.md,docs/API.md(der Abschnitt Costs, quotas, and per-user model policy). Provenienz-Zeilen:FACT-CENSUS.mdF-QUOTA-1.
Die Kern-Invarianten
- Kein zweiter Zähler. Die Wahrheit des Verbrauchs (Token/Kosten) bleibt beim bestehenden Ledger
WorkspaceTokenUsage(packages/server/src/services/quotaService.ts).QuotaBalance/QuotaHold/QuotaResetsind abgeleiteter und geankerter Zustand, keine Zählungstabelle. - Reset = Ledger-Anker. Ein Reset schreibt erneut
QuotaReset(kind, at), das das rollierende Fenster begrenzt — die Geschichte von Nutzung/Kosten wird nie gelöscht oder genullt. - Gate auf Service-Ebene, pre-turn — KEIN Auth-Middleware (der Check braucht Domänen-Daten, nicht die Form der Anfrage). Die Gates starten vor dem ersten LLM-Aufruf im Orchestrator und vor der Ingest auf den Upload-Pfaden.
- Fail-open bei nicht gesetzten Quotas. Die Auflösungskette endet auf „unbegrenzt", wenn nichts konfiguriert ist (D-08).
- Die Geldmathematik ist Decimal→cents. SÄMTLICHE Kostenarithmetik läuft über
Prisma.Decimalund landet als ganze cents — nie float (costQuotaService.ts, die COST-01-Doktrin).
Die Auflösungskette (D-08)
| Ebene | Quelle | Hinweise |
|---|---|---|
| 1 | Pro-Benutzer-Override | User.tokenQuotaLimit / User.storageQuotaGb / die Kosten-Zeilen pro Benutzer — via PUT /api/quota/:userId (admin) |
| 2 | Installations-Preset | Die SystemConfig-Schlüssel (unten) — DB > ENV > Default |
| 3 | Unbegrenzt | Die Sentinel "0" / "" / unset = nicht konfiguriert → fail-open |
Die Konfigurationsschlüssel (SystemConfig, admin-editierbar via Settings)
| Schlüssel | Standard | Bedeutung |
|---|---|---|
QUOTA_TOKEN_DEFAULT | unset-Sentinel | Die Token-Obergrenze der Installs für jeden Benutzer ohne Override |
QUOTA_STORAGE_GB_DEFAULT | unset-Sentinel | Die Storage-Obergrenze der Installs (GB) |
QUOTA_COST_EUR_DEFAULT | unset-Sentinel ("0"/"" = unbegrenzt) | Die KOSTEN-Obergrenze der Installs — DEZIMALZEICHENKETTE EUR |
QUOTA_COST_FALLBACK_INPUT_CENTS_PER_1M | integrierter Floor ungleich null | Der SC-4-Fallback-Tarif für Modelle OHNE Preis — input, ganze cents pro 1M Token |
QUOTA_COST_FALLBACK_OUTPUT_CENTS_PER_1M | integrierter Floor ungleich null | Der SC-4-Fallback-Tarif für Modelle OHNE Preis — output |
Die SC-4-Doktrin: ein Modell ohne Preis umgeht die Kosten-Quota NIEMALS — die Fallback-Tarife greifen mit warn-once (warnFallbackOnce): ein preisloses Modell kann nie als gratis gelten.
Token-Quotas (quotaService.ts)
resolveTokenQuota(userId)→{ limit, source }—limit: null= unbegrenzt;sourcesagt, welche Ebene der D-08 entschieden hat.- Das rollierende Fenster startet am letzten
QuotaReset-Anker für(userId, kind: "token")(oder an der Epoche, falls nie zurückgesetzt); die In-Fenster-Nutzung summiert sich vom LedgerWorkspaceTokenUsage. - Das Überschreiten der Quota wirft
QuotaError(status+payload) — das Feldquotades Payloads ist der Vertrag, den frontend, widget und die Connector-Handler lesen. Es gibt keine Middleware zum Umgehen: das Gate läuft INNERHALB des Service-Pfads.
Kosten-Quotas (costQuotaService.ts, Phase 216) — hold-and-settle
Die zweiphasige Buchung:
- Reserve (vor dem LLM-Aufruf) —
reserveCostQuota(principal, estimateCents)fährt EINE interaktive Transaktion: ein bedingtesupdateMany, dessen Prädikat (spentCents + estimate <= limitCents) Postgres mit row-lock auswertet, sodass N konkurrierende Gates sich SERIALIZIEREN statt alle über einen alten Check zu laufen (SC-2) + dasQuotaHold-Insert in derselben Transaktion. - Settle (beim Nutzungsabschnitt) —
settleCostHoldschließt den hold und erstattetestimate − actualzurück. Der In-flight-Overshoot (actual > estimate) ist das akzeptierte Fenster — kein Kill bei laufendem Stream. - Im Ruhezustand gilt die Invariante:
settledSpent + Σ(offene lead) ≤ limit. - Die Preisquelle je Zeile: der FROZEN-Snapshot von Phase 203, wo vorhanden; sonst die SC-4-Fallback-Tarife (warn-once); Zeilen in einer Nebenwährung werden trotzdem bepreist (nie still gratis, nie ausgeschlossen).
- Reset —
resetCostQuotaschreibt denQuota(kind "cost")-Anker und baut das Guthaben aus Ledger-Summe + offenen holds neu auf (D-02).
Storage-Quotas
checkStorageQuota / resolveStorageQuota erzwingen den Pro-Benutzer-Override storageQuotaGb, das Preset QUOTA_STORAGE_GB_DEFAULT oder das Unbegrenzte. Die Gates starten auf den Upload-Pfaden vor dem Dispatch zum Collector.
Der Principal für Widgets
Die anonymen Widget-Besucher sind keine Benutzer — resolveWidgetQuotaPrincipal bindet den Widget-Traffic an den Principal der Organisation des Widgets: der Widget-Verbrauch wächst auf der Quota-Kette der Organisation, nicht auf per-Besucher-Identitäten.
Wo die Gates starten (die Verbrauchsstellen)
| Oberfläche | Gate | Datei |
|---|---|---|
| Chat (JWT, widget proxy, connector — ein orchestrator) | Token- + Kosten-Gate pre-turn / pre-LLM | packages/server/src/agent/orchestrator.ts |
| Dokument-Upload | das Storage-Gate pre-ingest | packages/server/src/routes/uploads.ts, routes/documents.ts |
| Das Reset-Sweep | stündlicher Token-Quota-Reset (pg-boss cron: initQuotaResetScheduler) | services/quotaResetJob.ts |
| Die self-service Abrufe | GET /api/quota/me, rollups, manueller Reset | routes/quota.ts |
Der Betreiber-Workflow
- Der Installations-Default — Settings-UI → die
QUOTA_*-Schlüssel (oderPUT /api/system/settings). - Der Pro-Benutzer-Override —
PUT /api/quota/:userId(admin) oder die Quota-UI. - Die Pro-Benutzer-Preise — die COSTS-Familie (Phase 209:
/api/users/:userId/pricing, die own-provider-Schlüssel unter/api/me/provider-keys) speist dieselbe Engine. - Der manuelle Reset jederzeit:
POST /api/quota/:userId/reset; der stündliche Sweep trägt den Ruhezustand.
Die Modell-Preise (MODEL_COSTS)
Setup: Settings → Providers → den Provider wählen → die Model-action pricing → der Kostenwert pro 1M-Token input/output (z. B. 1.50 und 6.00) + die Währung → Save. Die Formel: cost = tokens × rate, serverseitig bei jedem Nutzungs-Schreibzugriff berechnet, mit einem zur Laufzeit eingefrorenen Snapshot (spätere Tarifänderungen schreiben die Geschichte nie um). Währungen: USD, EUR, GBP, JPY, CNY, INR — Summen je Währung, keine Konversion in v1. N/A = Preis nicht konfiguriert (die Cloud-Modelle unset; die UI zeigt „N/A" — der Admin muss konfigurieren); $0.00 = ausdrücklich gratis (die lokalen/Ollama-Modelle wurden im backfill zur Migration auf 0 gesetzt; Override möglich). Die v1-Grenzen: keine Währungskonversion; nur die Kosten der Hauptiteration (die Sub-Pipelines tool/MCP/synthesis ausgeschlossen); die Kosten stehen beim done-Ereignis zur Verfügung (nicht zur Stream-Mitte).
Die Endpunkt-Referenz
Die volle Tabelle lebt in API reference → Costs, quotas, and per-user model policy.
Siehe auch
- Scaling — das Quota-Verhalten bei Multi-Instanz-Deployment
Diese Anleitung behandelt die Betriebsoberfläche für die Quotas und Kosten; die technische Tiefe steckt im Zwillingsbeitrag:
Provenance
| Abschnitt | Quelle |
|---|---|
| Invarianten / Auflösungskette / Schlüssel | docs/QUOTAS.md (core invariants, D-08, die Schlüssel — Phase 207/203) — F-QUOTA-1 |
| Kosten-Quotas / hold-and-settle | docs/QUOTAS.md (§ Cost quotas — Phase 216) + costQuotaService.ts |
| Modell-Preise | docs/MODEL_COSTS.md (Phase 203/209/216) |
| Die Gate-Stätten | docs/QUOTAS.md (§ Where the gates fire) |
---
Betreiber-Referenz — Simmetric Chat v0.28.1 (Dokumente aktualisiert am 2026-10-02, Census-SHA `aca5b8ff`).