Initial commit

This commit is contained in:
2025-08-30 21:05:34 +00:00
commit b04557a923
40 changed files with 5469 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
# QuotaEnforcement — Flow Diagram (rev2.1)
> **Scope** this document explains *how* the freetier 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 (rev2.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` | `5000ms` | Delay for *first 30* overquota scans |
| `D_hard` | `60000ms` | 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 autoexpire **24h** after first increment (00:00UTC reset).*
---
## 2·Redis key layout
| Key pattern | TTL | Description |
| ---------------------- | ---- | --------------------------------- |
| `quota:ip:<sha256>` | 24h | Anonymous quota per *hashed* IP |
| `quota:tid:<sha256>` | 24h | Token quota per *hashed* tokenID |
| `quota:ip:<sha256>:ts` | 24h | Firstseen timestamp (ISO8601) |
Keys share a common TTL for efficient mass expiry via `redis-cli --scan`.
---
## 3·Pseudocode (Gostyle)
```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 `HTTP429` 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 < 1s expected |
---
*Generated {{ "now" | date: "%Y%m%d" }} — values pulled from central constants.*