Eduin Garcia
Personal product · open source (MIT)

One Rust codebaseSix platforms

LightNotes is a local-first notes and diary app — WYSIWYG Markdown editing, folders and tags, reminders, and encrypted on-device storage. The hard parts were making one Rust codebase render on six platforms and keeping a background sync loop correct against a self-hosted REST + SSE API.

LightNotes notes list on desktop
LightNotes — workspace scaleRust 2021Sheet 01
6
Platform targets
5 + 6
Crates — apps + shared
77
UI components
~22k
Lines of Rust
12
REST / SSE endpoints
78
Tests
9
CI/CD workflows
400
i18n message keys

~22k lines of Rust across 317 files. Six platform targets: web (WASM), macOS, Windows, Linux, Android and iOS.

01 · Architecture

Cargo workspace monorepo

02 · Crates in the workspace

5 apps · 6 packages
apps/webNotes app, WASM SPA shell (nginx-served in production).
apps/desktopNotes app, native shell for macOS / Windows / Linux.
apps/mobileNotes app, Android / iOS shell.
apps/landingSSR marketing site (Dioxus fullstack, standalone server binary).
apps/apiAxum REST + SSE sync backend over MongoDB.
packages/appShared routes, views, 32 app components and global state (sync, session, scheduler, reminders).
packages/ui45 generic, app-agnostic presentational components + theme.
packages/editorRich-text / Markdown editor engine.
packages/store-sdkEncrypted on-device persistence and change queue.
packages/api-sdkTyped REST + SSE client.
packages/sync-dtoShared client/server sync contract types.

03 · Client — shared Rust UI

Dioxus 0.7
Core
Rust 2021 (stable), Dioxus 0.7 — signals, RSX, #[component], hooks, context
Routing
Dioxus Router — type-safe #[derive(Routable)] enum, 4 levels of nested #[layout]
Render targets
WebAssembly (wasm32-unknown-unknown), WKWebView, WebView2, WebKitGTK
SSR
Dioxus Fullstack — server-side rendering + hydration for the landing site
Components
dioxus-primitives (headless/accessible) wrapped into a 45-component in-house design system (packages/ui)
Styling
Tailwind CSS 4 — compiled by the Dioxus CLI from Rust sources, no Node/npm step; dark/light themes + 6 accent colors
Editor
taino-edit (core + extensions) — ProseMirror-style schema, 14 extensions, WYSIWYG round-tripping to Markdown
i18n
dioxus-i18n + unic-langid + Fluent — 400 message keys, English & Spanish
Icons / time
dioxus-icons, time, web-time
JS interop
document::eval bridges for Google Sign-In (web), splash screen, timezone offset, accent theming
Notifications
notify-rust (Linux), windows + WinRT toasts, objc2-user-notifications (macOS/iOS), jni + ndk-context (Android)
Testing
dioxus-ssr component render tests, unit tests across app / ui / sync-dto

04 · Local-first storage & sync

Encrypted at rest
Database
SQLite via sqlx 0.9 — async pooled queries, WAL, PRAGMA user_version schema versioning
Encryption
SQLCipher (libsqlite3-sys, bundled + vendored OpenSSL) — full DB encrypted at rest under a random 256-bit key, with a plaintext → encrypted migration path
Key custody
keyring — Keychain (macOS/iOS), Credential Manager (Windows), Secret Service (Linux), app-private storage (Android)
Sync engine
Custom change-log reconciliation: offline pending-change queue, debounced batching, cursor-based catch-up, exponential-backoff reconnect, last-write-wins conflict resolution, manual offline toggle
Transport
api-sdk — custom REST + SSE client (reqwest, rustls, async-stream, futures-util) compiling for both native and wasm
Scheduling
tokio timers on native, gloo-timers on wasm; local reminder scheduler feeding OS notification centers

05 · Backend

Axum · MongoDB · SSE
Runtime
Rust 2021, Tokio 1 (multi-thread, signal handling)
Framework
Axum 0.8, Tower / tower-http (CORS, body limits, tracing)
Database
MongoDB 7 — mongodb driver + mongodm, custom versioned index and collection migrations
Realtime
Server-Sent Events, fanned out in-process via tokio::broadcast + tokio-stream
Auth
Google OAuth 2.0 / OIDC — web ID-token flow and native authorization-code flow with PKCE, plus short-lived auth tickets
Sessions
JWT (jsonwebtoken, RustCrypto backend) — access/refresh rotation, revocation, TTL-configurable
Security
axum-helmet security headers, 2 MB request body limit, rustls-only TLS, fail-fast secret validation at startup
Observability
OpenTelemetry (OTLP HTTP/protobuf) — traces, metrics & logs; tracing + tracing-subscriber + tracing-opentelemetry, JSON output, log rotation
Metrics
http.server.request.duration, http.server.active_requests, db.client.operation.duration, lightnotes.sse.active_streams, lightnotes.changes.processed, lightnotes.auth.attempts, plus process CPU/memory/uptime
Health
/healthz liveness, /readyz MongoDB-backed readiness; graceful SIGTERM drain + telemetry flush
Config
dotenvy, environment-driven, serde / serde_json throughout

06 · Infrastructure & DevOps

k3s · ArgoCD · 9 workflows

07 · Engineering highlights

Seven decisions
Encrypted local database

Encrypted at rest with OS-keychain key custody, including a one-way migration that re-encrypts an existing plaintext database in place while preserving its schema version.

Offline as the default state

The pending-change queue, debounce window, backoff reconnect and cursor catch-up all live client-side, so the server stays a thin append-only change log.

One UI tree, six platforms

Platform differences are isolated to per-target dependency tables and a handful of cfg blocks; notifications are the only subsystem with four native implementations behind one trait.

Build-time API base URL

option_env! bakes the endpoint into the WASM bundle, with the cache-invalidation trade-off documented — a deliberate trade of runtime configurability for a fully static SPA image.

Single-replica API by design

SSE fan-out is in-process tokio::broadcast; scaling out is documented as requiring Redis or Mongo change streams first, rather than left as an accidental constraint.

Telemetry opt-in by env var

Export is entirely off unless OTEL_EXPORTER_OTLP_ENDPOINT is set, so dev and CI behave identically to a pre-instrumentation build.

Release integrity as a hard gate

The workflow fails when a tag disagrees with Cargo.toml, because bundlers stamp the version independently of the artifact filename.

LightNotes diary calendar on desktop
LightNotes notes list on mobile
← All workNext: Qollabi 2.0