Commit Graph
6 Commits
Author SHA1 Message Date
Bernd Rodler 16466c7296 docs: adversarial review of the Electron offline engine design (4 critical, 4 high) 2026-08-04 22:53:06 +02:00
Bernd Rodler 2ff4b7847e docs: record human decisions on Linux keyring policy, retention defaults, review gate 2026-08-04 22:19:23 +02:00
Bernd RodlerandClaude Sonnet 5 46fc221f9e docs: design for the Electron offline/delta-sync engine (design only)
Adapts the mobile client's finalized, twice-reviewed JMAP delta-sync design
(vncmail-native's docs/DELTA-SYNC-ENGINE-DESIGN.md, revision 3) to Electron's
runtime rather than re-deriving JMAP sync theory. Every section is tagged
[reused] / [adapted] / [new] so a reader can tell which is which; the
protocol-level parts (three state machines, cursor provenance with branded
types, error taxonomy, pinned reconcile sweep floor, I1-I13, F1-F49) are
reused by citation, not restated.

Three decisions were genuinely open here and are resolved with evidence:

1. Process placement: the engine + SQLite live in the standalone Next.js
   server process, on a worker thread. The per-account credentials are
   already there in httpOnly AES-GCM cookies, so nothing secret crosses a
   process boundary - and a Node process can put an Authorization header on
   a WebSocket upgrade, which is exactly what makes RFC 8887 push
   unreachable from the renderer today (lib/jmap/client.ts:6038-6059).
   Hosting it in main.ts was rejected because it can only be built by
   moving credentials into a process that currently holds none - the change
   that same comment explicitly declined. A WASM/OPFS renderer engine was
   rejected because it needs 'wasm-unsafe-eval' added to the product-wide
   CSP in proxy.ts, and its only encrypted backends are small third-party
   WASM builds.

2. SQLCipher ships on day one, via @signalapp/sqlcipher (N-API prebuilds,
   verified loading in Electron 43.2.0 in both process modes with no
   rebuild; real SQLCipher 4.10.0; encrypted header, wrong key rejected,
   FTS5 present; AGPL-3.0-only like this repo). The mobile design's
   plaintext-first phase existed only because Expo Go cannot load
   SQLCipher, and that constraint has no Electron analogue. node:sqlite is
   rejected (no encryption - PRAGMA key is a SILENT no-op that leaves the
   mailbox in cleartext - and stability 1.2/RC in the Node 24 that Electron
   43 bundles); better-sqlite3-multiple-ciphers is rejected (Electron
   prebuilds stop at ABI 146, Electron 43 needs 148, so a C++ toolchain on
   every machine, and that lag recurs at every Electron major).

3. Keys use Electron's built-in safeStorage, not keytar, with a mandatory
   getSelectedStorageBackend() check: on Linux without a keyring,
   isEncryptionAvailable() returns true while using a public hardcoded
   password, which is worse than an honest failure.

Also records what this repo has that the mobile one doesn't (a real Stalwart
integration fixture, so the highest-value tests are cheap) and what it
lacks (no /changes wrappers, no offline cache, no outbox - so v1 desktop
offline is read-only by decision, and the mobile design's D1-D8 defects are
not inherited).

Everything not verifiable in this environment is flagged for a Stage A
verify-first gate rather than presented as fact - notably whether an
unsigned build keeps its macOS Keychain item across an electron-updater
upgrade, and whether Next's output file tracing carries the native
prebuilds into .next/standalone.

No source file is touched by this commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 22:15:10 +02:00
Bernd Rodler b15098a6eb docs: record WS push completion + browser-can't-auth-WS-handshake caveat 2026-08-04 14:21:03 +02:00
Bernd Rodler 75876725df docs: log deferred sandbox-login CORS bug (Electron random port vs. real Stalwart origin) 2026-08-04 13:26:14 +02:00
Bernd Rodler 568b7137ea docs: extensive build manual for the native/desktop client program
Consolidates the repo map, architecture recap, full decision log, Phase 1/2
status, remaining roadmap, and known landmines into one canonical reference,
so this doesn't live only in chat history or session memory.
2026-08-04 13:08:58 +02:00