The Pocket DB
Server-grade data discipline on the device in someone's pocket.
The incident
The offline feature shipped fast by caching the working set in localStorage. Then a field tablet was stolen. localStorage is plaintext. The working set was patient appointment data. The incident report wrote itself; the disclosure letters took longer.
Why the obvious approach fails
localStorage is the path of least resistance and fails every requirement that matters: it's synchronous (blocking the main thread on every read), capped around 5MB, string-only (so every access pays serialization), and stored in plaintext readable by anyone with the device or any script that slips past your CSP. Offline-first applications don't need a cookie jar — they need a database at the edge: indexed, queryable, encrypted at rest, and disciplined about growth.
The standard
- Client persistence uses a real store — IndexedDB (web) or SQLite (mobile) — behind one wrapper; direct localStorage use for domain data is banned.
- Data is encrypted at rest with AES-256; keys derive from the authenticated session, so a stolen device without a session yields ciphertext.
- The local store supports indexed, relational-style queries — offline search and filtering never require the network.
- Growth is bounded: LRU pruning with declared per-entity retention keeps the footprint predictable.
- Local schema is versioned with migrations, exactly like the server database — because it is one.
How Cortex validates it
At plan time
Offline blueprints declare the client data model, encryption approach, and retention bounds.
At validation
assert: localStorage.domain_data == noneassert: at_rest(client_store) == ciphertextassert: offline_queries.network_calls == 0Assertion syntax is illustrative.
At review
The approver sees the device-extraction test: store dumped without a session, nothing readable; plus query performance on the seeded offline set.