Files
Lumotia/docs/architecture-map/02-tauri-runtime/app-lifecycle.md
Jake 26c7307607
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled
agent: lumotia-rebrand — docs, scripts, root config, residuals
Phase 9 of the rebrand cascade. Sweep covers everything the Phase 8
frontend pass deliberately skipped: docs/, root markdown, scripts,
Cargo.toml descriptions, code comments that survived earlier
word-boundary sed, plus a handful of identifiers caught on the final
verify pass.

transcription-app changes:
- README.md, HANDOVER.md, KNOWN-ISSUES.md, run.sh — magnotia/Magnotia
  -> lumotia/Lumotia.
- docs/ — sweep across all subdirs except docs/handovers/ (preserved
  as immutable audit trail). Includes architecture-map references
  to magnotia_core::*, magnotia_storage::*, etc. now pointing at
  lumotia_*; dev-setup.md tracing output examples (lumotia_startup
  target); brief/ + superpowers/ + issues/ + whisper-ecosystem/ +
  audit/.
- Cargo.toml descriptions on 9 crates (core, audio, cloud-providers,
  hotkey, llm, mcp, plus referenced others).
- crates/core/src/{error,hardware,recommendation,paths}.rs +
  crates/audio/src/wav.rs + crates/llm/src/model_manager.rs +
  crates/cloud-providers/src/keystore.rs + crates/mcp/src/lib.rs —
  doc comments and a model-manager user-agent string.
- Caught on final pass: BroadcastChannel("magnotia_task_sync") -> ...
  ("lumotia_task_sync"); magnotia_locale i18n localStorage key
  renamed + migration shim added; CSS keyframe names
  magnotiaPulse / magnotiaBar / magnotiaFade renamed in the design-
  system kit; magnotia_viewer_item / magnotia_viewer_mode handoff
  keys renamed in HistoryPage + viewer/+page.svelte; src/assets/
  wordmark.svg text.
- src-tauri/src/lib.rs comment cleanup ("magnotia era" was sed'd
  to "lumotia era" earlier — restored).

Preserved (intentional):
- crates/core/src/paths.rs — keeps "magnotia" / "Magnotia" / ".magnotia"
  legacy detection strings in legacy_and_target_paths() so the
  migration shim can still find user data from the magnotia era.
- src/lib/stores/{page,focusTimer}.svelte.ts + src/lib/i18n/index.ts
  — migration call sites reference the legacy magnotia keys
  deliberately.
- docs/handovers/ — historical audit trail.

cargo build --workspace passes. npm run check: 0 errors / 0 warnings
(3958 files). cargo test --workspace: 339 pass / 0 fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:38:03 +01:00

9.3 KiB

name, type, slice, last_verified
name type slice last_verified
App lifecycle architecture-map-page 02-tauri-runtime 2026/05/12

App lifecycle

Where you are: Architecture mapTauri runtime → App lifecycle

Plain English summary. This is the entry point. main.rs calls lumotia_lib::run() and lib.rs::run does everything that has to happen before the user sees a window: initialises tracing, checks the Linux launcher env-var contract, 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) + 491 (lib).
  • Tauri commands exposed directly here: save_preferences (string preferences -> SQLite settings table). All other commands live under commands::* and are registered via tauri::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, lumotia_core::types::EngineName, lumotia_llm::LlmEngine, lumotia_storage::{init, database_path, get_setting, set_setting, prune_error_log}, lumotia_transcription::LocalEngine, plus the commands::* and tray modules.
  • 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 lumotia_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's tray-icon feature 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). Holds Arc<LocalEngine> for whisper and parakeet, the SqlitePool, and an Arc<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 via serde_json::to_string to keep it safe.
  • save_preferences(state, preferences) -> Result<(), String> (src-tauri/src/lib.rs:73). The single command in lib.rs. Persists the preferences blob to the SQLite settings table under key lumotia_preferences.
  • init_tracing() (Linux/macOS/Windows). Initialises the process-wide tracing subscriber once, honours RUST_LOG, and writes structured startup/runtime logs to stderr.
  • warn_if_x11_env_unset_on_wayland() (Linux only). Emits a lumotia_startup warning when the launcher has not pre-set WEBKIT_DISABLE_DMABUF_RENDERER (always expected on Linux), or GDK_BACKEND=x11 / WINIT_UNIX_BACKEND=x11 when XDG_SESSION_TYPE=wayland. It does not mutate the process environment; run.sh owns the dev-time contract and package wrappers/.desktop files must own the distribution-time contract.
  • run(). The Tauri builder pipeline.

run() step-by-step

  1. Tracing init. Calls init_tracing() before any startup warnings/logs are emitted.
  2. Linux launcher contract check. Calls warn_if_x11_env_unset_on_wayland() on Linux. Missing env vars produce warnings only; runtime env-var mutation was removed because Rust 2024 treats environment mutation in multi-threaded programs as unsafe.
  3. Panic hook. Calls commands::diagnostics::install_panic_hook() to dump panic info to crashes_dir().
  4. Plugin wiring (always-on). tauri_plugin_opener, tauri_plugin_dialog, tauri_plugin_notification (src-tauri/src/lib.rs:144).
  5. Plugin wiring (desktop-only). tauri_plugin_global_shortcut, tauri_plugin_autostart (LaunchAgent on macOS), tauri_plugin_window_state (src-tauri/src/lib.rs:158).
  6. Setup hook. This is where the bulk of startup work lives:
    • Initialise SQLite via lumotia_storage::init(&database_path()).await using tauri::async_runtime::block_on (src-tauri/src/lib.rs:180). The Instant::now() timing is logged.
    • Prune error_log rows 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 webkit2gtk permission requests: enable media_stream and media_capabilities settings; 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 makes getUserMedia work 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::CloseRequested and call window.hide() instead of letting the platform exit (src-tauri/src/lib.rs:281).
    • Stash the PreferencesScript and the per-domain managed states (HotkeyState, NativeCaptureState, LiveTranscriptionState, TtsState, MeetingState) (src-tauri/src/lib.rs:294).
    • Build the AppState itself: fresh LocalEngines for whisper and parakeet, the open SqlitePool, a fresh LlmEngine (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).
  7. 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).
  8. Run. .run(tauri::generate_context!()) blocks the main thread until the app exits. Panics are wrapped with expect("error while running Lumotia").

Data flow

  • Frontend bootstrap: the webview is built per tauri.conf.json window config. Tauri's eval() 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 by Arc semantics into every state.db borrow.
  • Engine state: the two transcription LocalEngines and the LlmEngine are shared Arcs; commands clone them and run inference inside tokio::task::spawn_blocking so the async runtime stays responsive.
  • Per-domain state: HotkeyState, NativeCaptureState, LiveTranscriptionState, TtsState, MeetingState are all stashed via app.manage(...) and retrieved by their command files via tauri::State<'_, T>.

Watch-outs

  • tauri::async_runtime::block_on inside setup blocks 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_webview fails the app still boots, but getUserMedia will be silently denied or fall back to a prompt the user cannot answer (no UI). The error path logs a lumotia_startup warning.
  • Linux rendering env vars are a launcher contract, not a runtime mutation. In development, use npm run dev:tauri / ./run.sh; packaged Linux builds need an equivalent wrapper or .desktop Exec=env policy. WEBKIT_DISABLE_DMABUF_RENDERER=0 remains the user opt-out for the DMA-BUF workaround.
  • 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_model call is not wired here. commands::models::prewarm_default_model exists, but setup does not invoke it. The frontend invokes the matching prewarm_default_model_cmd command after the main page mounts. If you ever want to shift pre-warm into setup, watch the spawn_blocking ordering against the engine Arc clones.

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.