Commit Graph
11 Commits
Author SHA1 Message Date
Bernd Rodler 3338ceb5eb docs: correct the mobile replica — it is NOT encrypted
I described vncmail-native's offline mail replica as "SQLCipher-encrypted"
in ARCHITECTURE.md and to the user. That is wrong, and it overstates a
security property.

Verified against the shipped code: src/sync/schema.ts sets
STORE_FORMAT = 'sqlite-plain', src/sync/store-sqlite.ts's own header says
"plain expo-sqlite, no SQLCipher", sqlite-driver.ts opens via
openDatabaseAsync() with no PRAGMA key, and there is no SQLCipher
dependency in package.json at all. SQLCipher is a documented future
native-build flip (expo-sqlite's useSQLCipher flag), not shipped behaviour.

Full mail bodies therefore sit in cleartext on the device — a materially
different posture from the Electron search index, which really is
encrypted (@signalapp/sqlcipher with an OS-keychain key via safeStorage).
Worth being precise about given the product positioning.
2026-08-05 17:58:04 +02:00
Bernd Rodler 15b189357e docs: architecture overview, sandbox dev manual, production scale-out plan
Written from direct SSH inspection of both real clusters (node1-3 prod HA,
dev-k8s-1-3 dev) done while building the GitLab CI + ArgoCD pipeline (MR
!1) - not re-derived from the aspirational docs/manifests that predated
that inspection.

ARCHITECTURE.md: system diagram (clients, both clusters, Stalwart, EJBCA
CA, the CI+ArgoCD flow) plus the storage-coupling fact that everything
else hinges on - 4 RWO PVCs + strategy:Recreate is why the app is
single-replica today.

SANDBOX-DEV-MANUAL.md: day-to-day branch/MR/CI/ArgoCD flow, one-time
bootstrap, troubleshooting, and what's explicitly out of scope for normal
dev work (the CA, the still-inert prod overlay).

PRODUCTION-SCALE-OUT-PLAN.md: phased path to a 100k+-user production
deployment on node1-3 - breaking the storage coupling first (rook-ceph
CephFS RWX as the fast path, migrating mutable state into the
already-installed-but-unused CNPG Postgres as the correct one), then
autoscaling, Stalwart's own scaling track, networking/edge, the
observability gap (none found on either cluster), security hardening,
load testing, DR, and the go-live sequence. Includes a "scale at any
time" manual lever, not just HPA.
2026-08-05 17:58:04 +02:00
Bernd Rodler 12908ab706 Merge branch 'claude/electron-offline-design' into dev
Encrypted SQLite/FTS5 offline search index for the Electron desktop
client: event-driven reindex (mail, calendar, contacts, files) driven
off the existing JMAP push connection, per-account keys held in OS
keychain via safeStorage, search API returns ranked context ready for
an LLM/RAG prompt.
2026-08-05 11:08:59 +02:00
Bernd RodlerandClaude Sonnet 5 31b4ea2ecd docs: mark the offline-engine design + review as superseded
Both describe a full offline mail replica with a persistent cursor-based sync
engine. That scope was dropped in favour of "a SQLite index we can prompt
against" - see the notes prepended to each file for what shipped instead
(lib/mail-index/** + app/api/offline/{reindex,search}).

Kept rather than deleted because several findings are still accurate and still
load-bearing: the SQLCipher binding investigation, the PRAGMA-key
silent-no-op landmine, the safeStorage Linux basic_text hazard, the
hosted-deployment gate, and the codebase survey.

The review's note also records the disposition of every CRITICAL/HIGH finding.
Most became MOOT rather than fixed - C2, C3, C4, H1 and H2 were all
consequences of a long-lived worker holding credentials, and the new shape has
no worker. C1 (the Docker build breakage) and H2's env-vs-fd point were fixed
as specified, and the review's two corrections to the design (the
cipher_version check needing a non-empty string, getSelectedStorageBackend
being Linux-only) are both in the shipped code.

Also recorded: two things the design got wrong beyond the scope change - its
claim that the chosen process needs no new secret handling (the review was
right) and its assumption that Next's file tracing would carry the native
module (it does not).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 23:44:57 +02:00
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 RodlerandClaude Opus 5 b6fdfe72ca chore: housekeeping — rescue orphaned doc, ignore .DS_Store, adopt vnc-v0.3.0
Commits the offline-client architecture analysis doc that was sitting
untracked in docs/ — its own header already warns this exact thing
happened once before (~/vncmail-plus is a shared checkout; an earlier
untracked copy was lost to a concurrent branch switch). Confirmed the
hazard is still live: vnc/VNC-CHANGES.md itself was found deleted from
disk mid-edit by this session, by something else touching the checkout
concurrently, and had to be restored with `git checkout --` before this
commit. Committing on sight is the only defense against that, not a
process improvement for later.

Also:
- .DS_Store added to .gitignore (was untracked in docs/)
- introduces a VNC-side feature version, separate from package.json's
  upstream-tracking version (1.7.8, must stay that way per the fork's own
  rule 4 - bumping it would turn merging upstream releases into a diffing
  exercise). Retroactively bucketed at the milestone boundaries the commit
  history already has: v0.1.0 fork bootstrap, v0.2.0 S/MIME plugin
  audit+fixes, v0.3.0 the internal-CA foundation just landed. Tagged
  vnc-v0.3.0 on this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 13:10:03 +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