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.


0 · Key parameters (rev 2.1)

SymbolValueMeaning
L_anon33Daily ceiling for anonymous users
L_jwt333Daily ceiling for token holders
T_warn200Soft reminder threshold
D_soft5 000 msDelay for first 30 over‑quota scans
D_hard60 000 msDelay for all scans beyond the soft window

L_active is L_jwt if a valid token is present; else L_anon.


1 · Sequence diagram

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 patternTTLDescription
quota:ip:<sha256>24 hAnonymous quota per hashed IP
quota:tid:<sha256>24 hToken quota per hashed token‑ID
quota:ip:<sha256>:ts24 hFirst‑seen timestamp (ISO 8601)

Keys share a common TTL for efficient mass expiry via redis-cli --scan.


3 · Pseudocode (Go‑style)

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

MetricPromQL sampleAlert
stella_quota_soft_hits_totalincrease(...[5m]) > 50Many users near limit
stella_quota_hard_hits_totalrate(...[1h]) > 0.1Potential abuse
Average delay per requesthistogram_quantile(0.95, sum(rate(...)))P95 < 1 s expected

Generated {{ “now” | date: “%Y‑%m‑%d” }} — values pulled from central constants.