Separate the README architecture into program-flow, security, and observability diagrams so serving, defenses, telemetry, and alert delivery are easier to understand independently.
- commit
- dd91f998905dc81075395a001b38e8370f0ffe1f
- parent
- a4aadccb0874a30b35ee681fb396aa619ff497ea
- author
- Dami <dami.etoile@gmail.com>
| M | README.md | 51 | +++++++++++++++++++++++++----- |
@@ -107,24 +107,57 @@ alias.
107107 108108 ## Architecture109109 110+### Program flow111+110112 ```mermaid111-flowchart TB112- CLI["nzip<br/><i>Deno CLI</i>"] -- "push API<br/>bearer token" --> W113+flowchart LR114+ CLI["nzip<br/>Deno CLI"] -- "push API<br/>bearer token" --> W113115 V(["visitor"]) -- "GET /2a3f" --> C{"Workers Cache<br/>60s public TTL"}114116 C -- "HIT" --> V115- C -- "MISS / BYPASS" --> W["nzip Worker<br/><i>serve · API · security</i>"]117+ C -- "MISS / BYPASS" --> W["nzip Worker<br/>serve · API"]116118 W -- "cacheable response<br/>tagged by site" --> C117-118- W --> RL["Rate-limit bindings<br/>enumeration · unlock · telemetry"]119119 W --> R2[("R2<br/>blobs · manifests")]120- W --> D1[("D1<br/>site metadata<br/>probe windows · incidents")]121- W -. "sampled structured events" .-> LOGS["Workers Logs<br/>security.request"]120+ W --> D1[("D1<br/>sites · vaults · history")]121+```122122 123- CRON(["Cron triggers<br/>5 min · daily"]) --> W124- W -- "incident + digest" --> EMAIL["Email Routing binding<br/>verified destination"]123+### Security lens124+125+```mermaid126+flowchart LR127+ REQ(["request"]) --> SURFACE{"request surface"}128+ SURFACE -- "/api/*" --> TOKEN["bearer-token check"]129+ SURFACE -- "GET /xxxx" --> ENUM["enumeration limiter"]130+ SURFACE -- "POST /xxxx/__unlock" --> UNLOCK["password limiter"]131+ TOKEN --> API["owner API"]132+ ENUM --> SERVE["site lookup / serve"]133+ UNLOCK --> SERVE134+ SERVE --> GATE{"password protected?"}135+ GATE -- "yes" --> COOKIE["PBKDF2 + signed cookie"]136+ GATE -- "no" --> PUBLIC["public response"]137+ ENUM -. "HMAC scanner identity" .-> PROBES[("D1 probe windows")]138+ PROBES --> EVAL["5-minute incident evaluator"]139+ EVAL --> EMAIL["verified-destination email"]125140 EMAIL --> INBOX(["operator inbox"])126141 ```127142 143+### Observability lens144+145+```mermaid146+flowchart LR147+ RESP["Worker response"] --> CLASSIFY["classify security-relevant request"]148+ CLASSIFY --> SAMPLE{"deterministic 1%<br/>scanner sample"}149+ SAMPLE -- "normal + selected" --> LOGS["Workers Logs<br/>security.request"]150+ SAMPLE -- "429 + selected" --> RESAMPLE{"secondary 1%<br/>request sample"}151+ SAMPLE -- "not selected" --> DROP["no log event"]152+ RESAMPLE -- "selected" --> LOGS153+ RESAMPLE -- "not selected" --> DROP154+ PROBES[("D1 probe windows")] --> CRON["5-minute aggregation"]155+ CRON --> WINDOW["security.enumeration_window"]156+ LOGS --> DASH["Cloudflare Observability"]157+ WINDOW --> DASH158+ PROBES --> METRICS["D1 row metrics"]159+```160+128161 A push is a stateless three-step protocol, and the manifest itself is the state:129162 130163 1. `POST /api/push/prepare`: send the manifest, get back which blob hashes the