Frontend Labs
⋯

Part IX — Capstone I: the encrypted notes PWA

52. Capstone: an offline-first, multi-threaded, end-to-end-encrypted notes PWA

Architecture combining nearly every part of this guide into one app:

┌──────────────────────────────────────────────────────────────────┐
│  Main thread (Part I-II: app logic, Part V: rendering)             │
│   - Signal/VDOM-based UI (§33) renders the notes list              │
│   - On save: calls into a Worker Pool (§41) for encryption          │
│     so a large note never blocks a keystroke's UI update            │
├──────────────────────────────────────────────────────────────────┤
│  Crypto Worker (Part VI, VII: §38, §46)                             │
│   - Owns the derived AES-GCM key (Part VII §46) — kept OUT of the    │
│     main thread's global scope specifically to shrink the window     │
│     an XSS payload could reach it (defense in depth, NOT a fix —     │
│     §47's XSS caveat still fully applies since a compromised main    │
│     thread can still just ASK this worker to encrypt/decrypt for it) │
│   - Encrypts/decrypts notes via postMessage RPC (Comlink-style, §41) │
├──────────────────────────────────────────────────────────────────┤
│  IndexedDB (§45)                                                     │
│   - Stores ONLY ciphertext + iv, keyed by note id                   │
├──────────────────────────────────────────────────────────────────┤
│  Service Worker (§40)                                                │
│   - Precaches the app shell -> works fully offline                  │
│   - Background-syncs encrypted blobs to a server when connectivity   │
│     returns, using the Background Sync API                          │
├──────────────────────────────────────────────────────────────────┤
│  Server (Part IV: HTTP)                                              │
│   - Receives and stores ONLY ciphertext — genuinely zero-knowledge:  │
│     a server breach exposes no readable note content                │
│   - Response headers set Content-Security-Policy (§51),              │
│     Cross-Origin-Opener-Policy + Cross-Origin-Embedder-Policy (§39)  │
│     to enable SharedArrayBuffer if the crypto worker pool needs it   │
│     for large-note streaming encryption                              │
└──────────────────────────────────────────────────────────────────┘

Build order that exercises every Part in sequence:

  1. Part I-II: the note data model, an async save/load API with a task queue limiting concurrent encrypt operations.
  2. Part III: verify the app's DOM updates don't force synchronous layout on every keystroke (Performance panel check, §24).
  3. Part IV: the sync-to-server fetch client, with retry/backoff (§31) for flaky connectivity — exactly the resilient client built in §31.
  4. Part V: render the notes list with a signal-based or VDOM approach (§33) so typing in one note never re-renders the whole list.
  5. Part VI: move encryption into a dedicated worker (§38, §41) so a large note's encryption never drops a keystroke frame.
  6. Part VII: derive the key from the user's password (§46), store only ciphertext in IndexedDB (§45), and register the Service Worker (§40) for offline capability.
  7. Part VIII: set a strict CSP (§51) as the actual XSS defense per §47's threat-model lesson, and heap-snapshot-check (§50) that closing a note view doesn't leak its decrypted plaintext via a lingering closure.

This is deliberately the same shape as real production apps (Signal's web client, Standard Notes, Bitwarden's web vault) — zero-knowledge encryption, offline-first via Service Worker, and worker-offloaded crypto are not academic exercises, they're the actual architecture of every serious client-side-encrypted web app shipping today.

Generated from real-life-JS-guide.md · 8 Oct 2026 16:36