# Quota Enforcement — Flow Diagram (rev 2.1) > **Scope** – this document explains *how* the free‑tier limits are enforced > inside the scanner service. For policy rationale and legal aspects see > [`33_333_QUOTA_OVERVIEW.md`](33_333_QUOTA_OVERVIEW.md). --- ## 0 · Key parameters (rev 2.1) | Symbol | Value | Meaning | |--------|-------|---------| | `L_anon` | **{{ quota_anon }}** | Daily ceiling for anonymous users | | `L_jwt` | **{{ quota_token }}** | Daily ceiling for token holders | | `T_warn` | `200` | Soft reminder threshold | | `D_soft` | `5 000 ms` | Delay for *first 30* over‑quota scans | | `D_hard` | `60 000 ms` | Delay for all scans beyond the soft window | `L_active` is `L_jwt` if a valid token is present; else `L_anon`. --- ## 1 · Sequence diagram ```mermaid sequenceDiagram participant C as Client participant API as Scanner API participant REDIS as Redis (quota) C->>API: /scan API->>REDIS: INCR quota: REDIS-->>API: new_count alt new_count ≤ L_active API-->>C: 202 Accepted (no delay) else new_count ≤ L_active + 30 API->>C: wait D_soft API-->>C: 202 Accepted else API->>C: wait D_hard API-->>C: 202 Accepted end ```` *Counters auto‑expire **24 h** after first increment (00:00 UTC reset).* --- ## 2 · Redis key layout | Key pattern | TTL | Description | | ---------------------- | ---- | --------------------------------- | | `quota:ip:` | 24 h | Anonymous quota per *hashed* IP | | `quota:tid:` | 24 h | Token quota per *hashed* token‑ID | | `quota:ip::ts` | 24 h | First‑seen timestamp (ISO 8601) | Keys share a common TTL for efficient mass expiry via `redis-cli --scan`. --- ## 3 · Pseudocode (Go‑style) ```go func gate(key string, limit int) (delay time.Duration) { cnt, _ := rdb.Incr(ctx, key).Result() switch { case cnt <= limit: return 0 // under quota case cnt <= limit+30: return 5 * time.Second default: return 60 * time.Second } } ``` *The middleware applies `time.Sleep(delay)` **before** processing the scan request; it never returns `HTTP 429` under the free tier.* --- ## 4 · Metrics & monitoring | Metric | PromQL sample | Alert | | ------------------------------ | ------------------------------------------ | --------------------- | | `stella_quota_soft_hits_total` | `increase(...[5m]) > 50` | Many users near limit | | `stella_quota_hard_hits_total` | `rate(...[1h]) > 0.1` | Potential abuse | | Average delay per request | `histogram_quantile(0.95, sum(rate(...)))` | P95 < 1 s expected | --- *Generated {{ "now" | date: "%Y‑%m‑%d" }} — values pulled from central constants.*