Five-slice navigable map of the entire codebase under
docs/architecture-map/. Each slice is a self-contained
breadcrumbed sub-tree:
01-frontend (16) Svelte/SvelteKit UI
02-tauri-runtime (26) src-tauri commands + lifecycle
03-audio-transcription (16) audio + transcription crates
04-llm-formatting-mcp (19) llm, ai-formatting, mcp, cloud
05-core-storage-hotkey-build core, storage, hotkey, workspace,
(26) CI, dev glue
Plus master README.md and data-flow-end-to-end.md tracing
audio bytes from microphone to FTS5 search to MCP read.
Generated by 5 parallel subagents on 2026/05/09 against
HEAD 3c47000. Each page has YAML frontmatter, file:line code
refs, sibling cross-links, plain-English summaries.
Aggregated debt surfaced (full lists in master README):
RB-08 macOS power assertion, schema head drift v14 vs v15,
VAD blocked on ort version conflict, streaming primitives
not wired into live.rs, no prompt versioning, MCP has no
auth, cloud-providers in-memory keystore, SettingsPage
2 484 LOC, commands/live.rs 1 737 LOC, dual theme system,
brand rename to Lumenote pending across the codebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.9 KiB
name, type, slice, last_verified
| name | type | slice | last_verified |
|---|---|---|---|
| App lifecycle | architecture-map-page | 02-tauri-runtime | 2026/05/09 |
App lifecycle
Where you are: Architecture map → Tauri runtime → App lifecycle
Plain English summary. This is the entry point. main.rs calls magnotia_lib::run() and lib.rs::run does everything that has to happen before the user sees a window: sets a Linux Wayland workaround, installs the Rust panic hook, registers Tauri plugins, opens the SQLite database, prunes the error log, builds a JS preferences-injection script, configures the WebKit media-permission grant on Linux, wires close-to-tray, populates AppState and the per-domain managed states, emits any runtime warnings, sets up the system tray, and finally registers all 71 Tauri commands.
At a glance
- Path:
src-tauri/src/main.rs,src-tauri/src/lib.rs. - LOC: 5 (main) + 448 (lib).
- Tauri commands exposed directly here:
save_preferences(string preferences -> SQLite settings table). All other commands live undercommands::*and are registered viatauri::generate_handler!. - Events emitted directly here: none (runtime warnings are emitted by
commands::models::emit_runtime_warnings, called from setup). - Depends on:
tauri,sqlx::SqlitePool,magnotia_core::types::EngineName,magnotia_llm::LlmEngine,magnotia_storage::{init, database_path, get_setting, set_setting, prune_error_log},magnotia_transcription::LocalEngine, plus thecommands::*andtraymodules. - Called from frontend at: every
invoke()site in slice 01 lands in the handler list registered here.
What's in here
main.rs
Single function. Sets windows_subsystem = "windows" for release builds (no console window) and calls magnotia_lib::run(). (src-tauri/src/main.rs:1).
lib.rs
Module declarations (src-tauri/src/lib.rs:1):
mod commands;#[cfg(not(target_os = "android"))] mod tray;— tray uses Tauri'stray-iconfeature which is desktop-only.
Constants:
ERROR_LOG_RETENTION_DAYS: i64 = 90(src-tauri/src/lib.rs:22). Used by the startup prune.
Types managed in Tauri state:
AppState(src-tauri/src/lib.rs:26). HoldsArc<LocalEngine>for whisper and parakeet, theSqlitePool, and anArc<LlmEngine>. This is the central state that almost every command queries.PreferencesScript(pub String)(src-tauri/src/lib.rs:34). Cached preferences-injection JS used when secondary windows are built (so they do not flash unstyled before Svelte mounts).
Functions:
build_preferences_script(prefs_json: Option<String>) -> String(src-tauri/src/lib.rs:38). Builds an IIFE that reads saved preferences (theme, zone, accessibility settings: font family, font size, letter spacing, line height, transcript size, bionic reading, reduce motion) and applies them to<html>before the rest of the document loads. Embeds the JSON viaserde_json::to_stringto keep it safe.save_preferences(state, preferences) -> Result<(), String>(src-tauri/src/lib.rs:73). The single command inlib.rs. Persists the preferences blob to the SQLite settings table under keymagnotia_preferences.ensure_x11_on_wayland()(src-tauri/src/lib.rs:99, Linux only). SetsWEBKIT_DISABLE_DMABUF_RENDERER=1unconditionally on Linux (iGPU idle-cost workaround), and additionally setsGDK_BACKEND=x11plusWINIT_UNIX_BACKEND=x11whenXDG_SESSION_TYPE=wayland. Idempotent: if a value is already set, the function leaves it alone. Must run before any threads spawn — usesunsafe { std::env::set_var(...) }.run()(src-tauri/src/lib.rs:135). The Tauri builder pipeline.
run() step-by-step
- Linux env-var prelude. Calls
ensure_x11_on_wayland()on Linux (src-tauri/src/lib.rs:137). - Panic hook. Calls
commands::diagnostics::install_panic_hook()to dump panic info tocrashes_dir()(src-tauri/src/lib.rs:141). - Plugin wiring (always-on).
tauri_plugin_opener,tauri_plugin_dialog,tauri_plugin_notification(src-tauri/src/lib.rs:144). - Plugin wiring (desktop-only).
tauri_plugin_global_shortcut,tauri_plugin_autostart(LaunchAgent on macOS),tauri_plugin_window_state(src-tauri/src/lib.rs:158). - Setup hook. This is where the bulk of startup work lives:
- Initialise SQLite via
magnotia_storage::init(&database_path()).awaitusingtauri::async_runtime::block_on(src-tauri/src/lib.rs:180). TheInstant::now()timing is logged. - Prune
error_logrows older than 90 days (src-tauri/src/lib.rs:189). Best-effort: a failure logs but does not block startup. - Load saved preferences from the settings table; build the JS injection script (
src-tauri/src/lib.rs:204). - Apply the injection script to the main window via
WebviewWindow.eval()(src-tauri/src/lib.rs:215). - On Linux, configure
webkit2gtkpermission requests: enablemedia_streamandmedia_capabilitiessettings; auto-grant audio capture but deny everything else (camera, geolocation, pointer lock, etc.) (src-tauri/src/lib.rs:222). This is the critical piece that makesgetUserMediawork on Linux without a permission dialog (because WebKitGTK has no dialog, it just silently denies by default). - Wire close-to-tray on desktop: intercept
WindowEvent::CloseRequestedand callwindow.hide()instead of letting the platform exit (src-tauri/src/lib.rs:281). - Stash the
PreferencesScriptand the per-domain managed states (HotkeyState,NativeCaptureState,LiveTranscriptionState,TtsState,MeetingState) (src-tauri/src/lib.rs:294). - Build the
AppStateitself: freshLocalEngines for whisper and parakeet, the openSqlitePool, a freshLlmEngine(src-tauri/src/lib.rs:302). - Emit runtime warnings (CPU baseline, Vulkan loader) via
commands::models::emit_runtime_warnings(src-tauri/src/lib.rs:312). - Setup the system tray on desktop (
src-tauri/src/lib.rs:314).
- Initialise SQLite via
- Command registration.
tauri::generate_handler![...]lists 71 commands (src-tauri/src/lib.rs:321). The order in the macro is grouped by domain (preferences, models, LLM, transcription, audio, tasks, feedback, TTS, rituals, nudges, intentions, profiles, transcripts, diagnostics, live, windows, clipboard, fs, paste, meeting, hardware, hotkey, updater). - Run.
.run(tauri::generate_context!())blocks the main thread until the app exits. Panics are wrapped withexpect("error while running Magnotia").
Data flow
- Frontend bootstrap: the webview is built per
tauri.conf.jsonwindow config. Tauri'seval()hook fires the preferences script before the SvelteKit bundle parses, so the user never sees a flash of unstyled content. - Database: opened once, owned by
AppState, cloned byArcsemantics into everystate.dbborrow. - Engine state: the two transcription
LocalEngines and theLlmEngineare sharedArcs; commands clone them and run inference insidetokio::task::spawn_blockingso the async runtime stays responsive. - Per-domain state:
HotkeyState,NativeCaptureState,LiveTranscriptionState,TtsState,MeetingStateare all stashed viaapp.manage(...)and retrieved by their command files viatauri::State<'_, T>.
Watch-outs
tauri::async_runtime::block_oninsidesetupblocks startup. The DB init and prefs read are explicitly timed and logged so regressions show up. Adding more synchronous async work here directly pushes the time-to-first-paint up.- The Linux media-permission wire-up is non-fatal: if
with_webviewfails the app still boots, butgetUserMediawill be silently denied or fall back to a prompt the user cannot answer (no UI). The error path logs[startup] failed to configure webview media permissions: ...to stderr. - The
unsafe std::env::set_varcalls inensure_x11_on_waylandare sound only because nothing else in the binary has spawned a thread yet at that point. Do not introduce another startup-time env mutation outside this function unless the same invariant is preserved. - Close-to-tray works only on desktop (the
cfg!(not(target_os = "android"))block). On Android, closing the activity terminates the process, which is the expected platform behaviour. - The
prewarm_default_modelcall is not wired here.commands::models::prewarm_default_modelexists, butsetupdoes not invoke it. The frontend invokes the matchingprewarm_default_model_cmdcommand after the main page mounts. If you ever want to shift pre-warm into setup, watch the spawn_blocking ordering against the engineArcclones.
See also
- Commands index — every command registered by
lib.rs::run. - System tray — what
tray::setup(app)builds. - Tauri config — the window config that drives the
get_webview_window("main")retrieval. - Capabilities and ACL — the permission set that decides which commands each window can call.
- Cargo and features — the dependency block that determines which plugins compile in.