WASM PWA Book Reader — Architecture Notes

Status: backlog — design decided, not started
Linked TODO: TODO.md → Backlog → WASM PWA Book Reader


Problem

Books are gated behind push subscription. Once a user downloads a chapter, nothing prevents them from sharing the file. Need practical protection against casual copying and automated scraping — not NSA-level DRM, but enough to make sharing impractical.

What WASM buys

Layer JS only WASM + Canvas
Decryption logic Readable in DevTools Compiled binary, no source view
Key in memory JS variable, console-accessible WASM linear memory, isolated from JS heap
Rendered content DOM text nodes — trivially selectable Canvas bitmap — no text nodes
Anti-instrumentation Easy to patch Hard to hook without recompiling

What WASM cannot fix

Architecture

Cloudflare KV
  └── chapters/{bookId}/{chapterId}  ← AES-256-GCM encrypted blob

push-gate worker  /key endpoint (authenticated by push endpoint + HMAC)
  └── returns per-user AES key, short TTL, tied to push subscription endpoint
  └── key revoked automatically when user unsubscribes (endpoint gone)

PWA Service Worker
  └── caches encrypted chapter blobs (never plaintext)
  └── fetches key from push-gate on first chapter open, caches key in memory only

WASM module (Rust → wasm-pack, or AssemblyScript)
  └── receives: encrypted blob + AES key (passed from JS, never stored back)
  └── decrypts in WASM linear memory
  └── renders page text to OffscreenCanvas
  └── transfers canvas bitmap to main thread

Reader shell (HTML/JS)
  └── page-flip UI
  └── receives canvas bitmaps, draws to visible <canvas>
  └── aria-label per page for screen readers (text summary, not full content)

Key derivation flow (important detail)

  1. User subscribes to push → push-gate stores { endpoint, grants } in KV
  2. User opens book chapter → reader JS calls push-gate /key?bookId=X
  3. push-gate: verifies the request comes from a valid push endpoint (HMAC or subscription check) → returns AES key
  4. Key lives only in JS memory (not localStorage, not IDB) — gone on tab close
  5. User unsubscribes → endpoint invalidated → /key returns 403

Accessibility mitigation

Canvas rendering disables: screen readers, in-page search, copy-paste, print-to-text.

Mitigation options (choose one per book):

WASM implementation options

Option Pros Cons
Rust + wasm-pack Best performance, AES crates available, smallest binary Rust learning curve
AssemblyScript TypeScript-like syntax, easier for JS devs Fewer crypto libs, larger binary
C + Emscripten Established, many crypto libs Complex toolchain

Recommendation: Rust + wasm-pack. aes-gcm crate is well-audited.

Open questions (decide before build)