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 see33_333_QUOTA_OVERVIEW.md.
0 · Key parameters (rev 2.1)
| Symbol | Value | Meaning |
|---|---|---|
L_anon | 33 | Daily ceiling for anonymous users |
L_jwt | 333 | 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
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:<sha256> | 24 h | Anonymous quota per hashed IP |
quota:tid:<sha256> | 24 h | Token quota per hashed token‑ID |
quota:ip:<sha256>: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)
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.