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:
- Part I-II: the note data model, an async save/load API with a task queue limiting concurrent encrypt operations.
- Part III: verify the app's DOM updates don't force synchronous layout on every keystroke (Performance panel check, §24).
- Part IV: the sync-to-server
fetchclient, with retry/backoff (§31) for flaky connectivity — exactly the resilient client built in §31. - 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.
- Part VI: move encryption into a dedicated worker (§38, §41) so a large note's encryption never drops a keystroke frame.
- 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.
- 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.