Restore component, observability, and security diagrams and add a dedicated notification pairing and delivery flow.
- commit
- 8dfdb697e7f8eb3afbcef44b415602547e52d0f3
- parent
- 8d8a4a17a8277d2b116e8a1e8b6e1726f6d6810a
- author
- Dami <dami.etoile@gmail.com>
| M | ARCHITECTURE.md | 76 | +++++++++++++++++++++++------- |
| M | SECURITY.md | 25 | +++++++--- |
@@ -6,16 +6,15 @@ identically.
66 77 ## Components88 9-```text10- bearer-authenticated API11-Deno CLI ------------------------------------------------> Worker12- |13-visitor -> Workers Cache -> Worker |14- | hit | miss |15- +--------------+--------------------------------+16- | |17- v v18- R2 blobs/manifests D1 state/history9+```mermaid10+flowchart LR11+ CLI["nzip Deno CLI"] -- "bearer-authenticated API" --> W["nzip Worker"]12+ V(["visitor"]) -- "GET /2a3f" --> C{"Workers Cache"}13+ C -- "hit" --> V14+ C -- "miss or bypass" --> W15+ W -- "tagged public response" --> C16+ W --> R2[("R2 blobs and manifests")]17+ W --> D1[("D1 state and history")]1918 ```2019 2120 - `shared/` defines manifests, hashing, target parsing, limits, media types, and API contracts.@@ -74,6 +73,43 @@ Security and notification state:
7473 An R2 object is deleted only when no live site or retained history entry references it and it is7574 older than 24 hours. This prevents concurrent garbage collection from deleting an in-flight push.7675 76+## Notification flow77+78+Notification setup separates enrollment, owner approval, and subscription attachment. Delivery is79+also asynchronous: the owner request persists an event and per-device attempts before background80+work contacts a Web Push provider.81+82+```mermaid83+flowchart LR84+ subgraph Pairing85+ OWNER["owner CLI"] -- "notify pair" --> WINDOW[("10-minute pairing window")]86+ PHONE["phone browser"] -- "begin enrollment" --> PENDING[("pending device and code hash")]87+ WINDOW --> PENDING88+ PENDING -- "display code" --> PHONE89+ OWNER -- "approve code" --> APPROVED[("approved device claim")]90+ PENDING --> APPROVED91+ APPROVED -- "activate claim" --> PWA["installed PWA"]92+ PWA -- "attach PushSubscription" --> ACTIVE[("active device")]93+ end94+95+ subgraph Delivery96+ OWNER -- "notify send" --> API["owner notification API"]97+ API --> EVENT[("event and per-device deliveries")]98+ EVENT --> DRAIN["scheduled delivery drain"]99+ ACTIVE --> DRAIN100+ DRAIN --> PUSH["allowed Web Push provider"]101+ PUSH --> SW["service worker"]102+ SW --> NOTICE["system notification"]103+ NOTICE -- "tap" --> TARGET["claim-authenticated click target"]104+ TARGET --> SITE["root or manifest-pinned site"]105+ end106+```107+108+Pairing codes cannot approve themselves; the approval path always requires the owner bearer token.109+Subscription endpoints must match the configured provider-origin allowlist. Notification click110+targets are resolved at tap time and open only the deployment root or the unchanged site manifest111+recorded with the event.112+77113 ## Background work78114 79115 The five-minute schedule evaluates enumeration windows and drains notification deliveries. The daily@@ -85,14 +121,18 @@ leases, bounded retry schedules, and terminal outcomes so overlapping cron execu
85121 86122 ## Observability87123 88-```text89-Worker response -> classify request -> deterministic scanner sample -> Workers Logs90- |91- +-> bounded D1 probe windows92- |93- five-minute evaluator94- |95- incident log + durable email outbox124+```mermaid125+flowchart LR126+ RESP["Worker response"] --> CLASSIFY["classify security-relevant request"]127+ CLASSIFY --> SAMPLE{"deterministic scanner sample"}128+ SAMPLE -- "selected" --> LOGS["Workers Logs"]129+ SAMPLE -- "not selected" --> DROP["no log event"]130+ PROBES[("bounded D1 probe windows")] --> CRON["five-minute evaluator"]131+ CRON --> WINDOW["security.enumeration_window"]132+ CRON --> OUTBOX[("durable alert outbox")]133+ LOGS --> DASH["Cloudflare Observability"]134+ WINDOW --> DASH135+ PROBES --> METRICS["D1 row metrics"]96136 ```97137 98138 Workers Logs provide sampled request-level context. D1 probe windows remain bounded but unsampled@@ -42,14 +42,23 @@ pending pages do not expose a second enrollment action.
4242 The address space is intentionally small, so nzip observes and limits enumeration rather than4343 claiming addresses are unguessable.4444 45-```text46-request -> surface classifier -> per-surface limiter -> serve or owner API47- |48- +-> HMAC scanner identity -> bounded D1 probe window49- |50- incident evaluator51- |52- durable email outbox -> operator45+```mermaid46+flowchart LR47+ REQ(["request"]) --> SURFACE{"request surface"}48+ SURFACE -- "/api/*" --> TOKEN["bearer-token check"]49+ SURFACE -- "GET /xxxx" --> ENUM["enumeration limiter"]50+ SURFACE -- "POST /xxxx/__unlock" --> UNLOCK["password limiter"]51+ TOKEN --> API["owner API"]52+ ENUM --> SERVE["site lookup and serve"]53+ UNLOCK --> SERVE54+ SERVE --> GATE{"password protected?"}55+ GATE -- "yes" --> COOKIE["PBKDF2 and signed cookie"]56+ GATE -- "no" --> PUBLIC["public response"]57+ ENUM -. "HMAC scanner identity" .-> PROBES[("bounded D1 probe windows")]58+ PROBES --> EVAL["five-minute incident evaluator"]59+ EVAL --> OUTBOX[("durable email outbox")]60+ OUTBOX --> EMAIL["verified-destination email"]61+ EMAIL --> INBOX(["operator inbox"])5362 ```5463 5564 Raw client IPs are neither logged nor stored. Scanner identities are HMAC-derived. Probe rows are