2026-09-23·by Sijie Wang#node#lucerna#software#architecture

architecture

Parent: lucerna · Repo root: ~/Develop/projects/Otium/

Source of truth

Code is authoritative; ops/ARCHITECTURE.md (2026-05-23) has drifted on almost every infra claim — all confirmed against code:

  • Desktop is Electron, not "Tauri 2" (desktop/package.json electron 33 + electron-builder + better-sqlite3; main/* import from 'electron'). A Tauri→Electron migration happened after the doc; many wa-sqlite/Tauri comments in desktop code are stale copy-paste.
  • Desktop local store is native better-sqlite3 via IPC, not wa-sqlite (the doc's "wa-sqlite cache" is only true for web). The schema block is FULL_SCHEMA_SQL reapplied every boot; the PRAGMA user_version migration framework survives only on web.
  • Bearer-token auth is shipped on desktop, not "planned" (setSessionProvider, Authorization: Bearer).
  • Still aspirational: the library hybrid slice (quota/local-move), real per-user scoping (every desktop row uses the literal 'local' user + wipe-on-session-change), and the advertised browser-plugin/mobile shells (do not exist).
  • landing/ is an empty stale clone; the real site is LandingPage/ (Payload CMS). Identity is dual/in-flight (Appwrite service + better-auth tables both present).

Stack (verified)

  • Domain core: framework-agnostic TS (@lucerna/core, peer-dep only zustand, tsup).
  • Shared UI: @lucerna/design-system (React 19, Radix, CVA, Tailwind 4).
  • Shells: Next.js 15 (web) · Electron + Vite (desktop) · Next.js 15 + Payload CMS 3 (landing).
  • Backends: Go 1.26 + echo (auth = Appwrite-backed; mainbackend = domain API + pgx + google/wire, owns the SRS) · Python + FastAPI + spaCy (nlp-api, DE+EN).
  • Data: Postgres; client SQLite (wa-sqlite web / better-sqlite3 desktop); flexsearch. Cohesion via a private Verdaccio registry; Coolify deploy; R2 assets.

The reading pipeline (the spine)

Ingest is 100% client-side; the server stores the parsed result. The load-bearing idea is the content-keyed, two-coordinate sentence.

  • Parse (core/src/upload/parseEpub|parsePdf|parseText): EPUB via JSZip → OPF/spine → HTML-stripped chapters joined; TOC from NCX/EPUB3-nav with cumulative char offsets; rejects scanned/thin books.
  • contentHash (upload/contentHash.ts, MD5, deliberately legacy-compatible) is the join key — vocab/notes/sentence-cache all anchor to it, so re-upload relinks.
  • CachedSentence (persisted unit, key (contentHash, sentenceIdx)) carries two coordinate systems — raw source offsets and display offsets the user reads — plus displayToRaw[]. This is the backbone: see raw-display-cursor.
  • Page is a transient runtime aggregate (only the current page in RAM); layout decides which sentences share a pageNumber — see dynamic-pagination (DOM-measured, reflows on font/viewport).
  • On top of the same sentence stream: full-text-search (per-book FlexSearch, disk-persisted) and, on desktop, tts-audiobook (local Piper audiobook with raw-offset-anchored karaoke). Ingestion that feeds all of this: upload-ingestion.

Reading domain model (plain terms)

Book (full text + ISBN provenance) → split into CachedSentences → read page by page (Page, transient) → position/marks stored content-anchored, not geometry-anchored so they survive font/layout changes: Anchor (current position = ≤200-char sentence prefix), Bookmark, ReadingNote (highlight with book-absolute raw offsets), VocabularyEntry (lemma + all inflected forms[] + POS/VerbTable/NounInfo + context + SRS state). POSCategory (10-way taxonomy, colors, 11-language labels, alias folding) and VerbTable (conjugation grid) are the linguistic value-objects.

Poly-repo shape

No monorepo tool — ~12 sibling repos cohere via the Verdaccio registry.

reporolestack
core@lucerna/core domain coreTS, tsup
design-systemshared UI atoms + tokensTS/React 19
webweb shell (online-first + read caches)Next.js 15, wa-sqlite
desktopdesktop shell (offline-first)Electron, better-sqlite3, sherpa-onnx
LandingPagemarketing siteNext.js 15 + Payload CMS 3
authidentity (Appwrite-backed)Go + echo
mainbackenddomain API, owns SRSGo + echo + pgx + wire
nlp-apispaCy/dep-parse/translate/OCRPython + FastAPI
e2e · ops · blog · landing(empty)tests · infra · content · —

Key designs (the pillars)

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

architecture