Some checks failed
		
		
	
	Build Test Deploy / authority-container (push) Has been cancelled
				
			Build Test Deploy / docs (push) Has been cancelled
				
			Build Test Deploy / deploy (push) Has been cancelled
				
			Build Test Deploy / build-test (push) Has been cancelled
				
			Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
			
				
	
	
		
			94 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Markdown
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			94 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Markdown
		
	
	
		
			Executable File
		
	
	
	
	
# 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:<key>
 | 
						||
    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)
 | 
						||
 | 
						||
```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.*
 |