Artikel/Engineering

Local Store Adapter Visual Overview

Technical notes from the Jett team.

Status: Working visual companion for V4 local-first storage Canonical sources: Roadmap V4, ADR-001 Goal: Show how Dexie, native SQLite, SQLite WASM/OPFS, the shared adapter contract, sync, and UI fit together.


1. Mental Model

Jett should not let React components care whether data comes from Dexie, native SQLite, or browser SQLite. The app talks to a shared domain boundary. Runtime-specific adapters hide the database details.

System map

The rule is simple: UI and agent code depend on the contract, not on a database driver. Dexie stays alive while we migrate, but it stops being the architecture.


2. Current State vs Target State

System map

What already exists

  • LocalMailStore type boundary.
  • DexieMailStore adapter.
  • Runtime feature flags for adapter selection.
  • Contract checks for the Dexie adapter.
  • Basic local thread list projection.

What still needs to be built

  • Native SQLite Rust module and Tauri IPC commands.
  • Native adapter parity with Dexie.
  • Hot cache as the primary render source.
  • Durable cursor change feed.
  • Modifier/outbox queue.
  • FTS5 local search.
  • Removal of direct Dexie imports from UI paths.

3. Runtime Adapter Selection

The app chooses one store implementation based on runtime capability and feature flags.

System map

Dexie is the safety net. Native SQLite is the desktop target. SQLite WASM/OPFS is the browser target.


4. Data Ownership

System map

Data rules:

  • Provider owns raw bodies and attachments.
  • Local store caches bodies and derived indexes.
  • Supabase should be metadata and coordination, not a mailbox clone.
  • Zustand is fast UI memory, not durable storage.
  • Pending local modifiers are important because they may not exist remotely yet.

5. Local Database Shape

Target local schema, shared logically across desktop and browser:

System map

The physical implementation can differ by runtime, but the behavior must pass the same contract tests.


6. Read Path: Open Mailbox

System map

The target render path is memory first. Database work happens off the render path.


7. Read Path: Open Thread Body

System map

Bodies can transit the Jett API, but the V4 target says they should not become permanent Supabase mailbox storage.


8. Write Path: Mark Done / Star / Move

System map

This is the move from “optimistic UI plus refetch” to real local-first mutation durability.


9. Realtime and Durable Sync

Realtime should wake the client up. It should not be the correctness mechanism.

System map

If Realtime is missed, duplicated, delayed, or reordered, the next catch-up still converges because the cursor feed is durable.


10. Migration Phases

System map

The practical sequence for today starts at step 1/2: make native SQLite real, prove it against the same contract, then move reads behind a flag.


11. File Map

Current important files:

  • src/lib/local-store/types.ts - shared local-store contract.
  • src/lib/local-store/current.ts - active adapter wiring.
  • src/lib/local-store/adapters/dexie-mail-store.ts - current fallback adapter.
  • src/lib/local-store/runtime.ts - feature flag and runtime selection helpers.
  • scripts/test-local-store.ts - current adapter contract checks.
  • scripts/benchmark-local-store.ts - current synthetic baseline.
  • src-tauri/src/lib.rs - Tauri shell and security/navigation boundary.
  • src-tauri/Cargo.toml - where rusqlite and native dependencies will be added.

Target files to add:

  • src-tauri/src/local_store/mod.rs
  • src-tauri/src/local_store/schema.rs
  • src-tauri/src/local_store/commands.rs
  • src-tauri/src/local_store/models.rs
  • src/lib/local-store/adapters/native-sqlite-mail-store.ts
  • src/lib/local-store/thread-hot-cache.ts
  • src/lib/local-store/body-cache-service.ts
  • src/lib/local-store/modifier-queue.ts
  • src/lib/local-store/realtime-buffer.ts

12. Implementation Checklist

  • [x] Expand LocalMailStore to include body cache, search, modifiers, cursors, and snapshot operations.
  • [x] Add Rust SQLite module with WAL, synchronous=NORMAL, foreign keys, busy timeout, and FTS5-backed search.
  • [x] Create versioned local migrations.
  • [x] Expose narrow typed Tauri commands only.
  • [x] Implement TS native adapter bridge.
  • [x] Run Dexie and native through focused local-store contract checks.
  • [x] Remove direct Dexie imports from active mail UI surfaces.
  • [x] Add ThreadHotCache foundation and cached store wrapper.
  • [ ] Add modifier queue for done/starred/inbox actions.
  • [x] Add local sync cursor storage and Realtime wake-up buffer foundation.
  • [x] Add atomic local change-batch/cursor apply across Dexie, native SQLite, and hot cache.
  • [x] Wire Realtime wake-ups to paged cursor catch-up instead of full thread-view refresh.
  • [x] Add append-only durable server change log, tombstones, and missed-event convergence tests.
  • [ ] Add FTS5 local search.
  • [ ] Keep Dexie as rollback until parity and telemetry prove native/web SQLite are safe.