---
name: Slice 5 — Core, Storage, Hotkey, Build
type: architecture-map-page
slice: 05-core-storage-hotkey-build
last_verified: 2026/05/09
---

# Slice 5: Core, Storage, Hotkey, Build / CI

> **Where you are:** [Architecture map](../README.md) → Core, Storage, Hotkey, Build

**Plain English summary.** This slice is Lumotia's foundations. Three Rust crates the rest of the workspace builds on top of: `lumotia-core` (shared types, hardware probes, model registry, thread tuning), `lumotia-storage` (the SQLite database, FTS5 search, file-system paths), and `lumotia-hotkey` (a Wayland-friendly evdev hotkey listener for Linux). Plus the workspace-level glue that wires the Rust workspace together with the SvelteKit frontend: `Cargo.toml`, the GitHub Actions pipelines, the dev launcher, the static asset folder, and the package configuration files.

If a new engineer wants to know where a public type comes from, where a setting is persisted, or how a release artefact gets built, the answer is somewhere in this slice.

## At a glance

- **Crates:** three. `lumotia-core` (1,805 LOC), `lumotia-storage` (3,771 LOC), `lumotia-hotkey` (632 LOC). Total ~6,200 LOC.
- **Workspace glue:** `Cargo.toml` (161 bytes), `package.json`, `vite.config.js`, `svelte.config.js`, `jsconfig.json`, `run.sh`, `static/`, `.gitignore`.
- **CI:** three workflows — `check.yml` (per-push compile + lint + libs tests + frontend), `build.yml` (release-bundle build for tags + manual dispatch), `audit.yml` (weekly Mondays cargo-audit + npm-audit).
- **Database head:** schema version **15** (commit on disk, supersedes the v14 figure quoted in `HANDOVER.md` 2026/04/25).
- **Key external deps:** `sysinfo 0.35` (hardware), `libloading 0.8` (Vulkan loader probe), `sqlx 0.8` (no default features, runtime-tokio + sqlite only), `evdev 0.12` (Linux hotkeys), `notify 7` (device hotplug), `uuid 1` (v4, random).
- **Targets:** Linux (primary, evdev backend), Windows, macOS. Hotkey crate is no-op on non-Linux platforms — Tauri's global-shortcut plugin handles those targets.
- **Default profile UUID:** `00000000-0000-0000-0000-000000000001`. Created by migration v6, protected by SQL triggers from rename or delete.

## Map of this slice

`lumotia-core`:

- [Public types and enums (Segment, Transcript, Megabytes, ModelId, EngineName)](core-types-and-enums.md)
- [Constants module (sample rate, VAD, RAM thresholds, chunk timing)](core-constants.md)
- [Error type and Result alias](core-error.md)
- [Hardware probe (sysinfo + CpuFeatures + Vulkan loader)](core-hardware-probe.md)
- [Power-state probe (sysfs, 10s TTL cache, test override)](core-power.md)
- [Inference thread tuning (Workload, battery halve, GPU clamp)](core-tuning.md)
- [Model registry (Whisper + Parakeet entries with pinned SHA256)](core-model-registry.md)
- [Recommendation scoring (rank_recommendations)](core-recommendation.md)
- [Process-watch (meeting detection by process name)](core-process-watch.md)
- [App paths (database, recordings, models, logs)](core-paths.md)

`lumotia-storage`:

- [Storage overview (sqlx config, init flow, default features rationale)](storage-overview.md)
- [Schema and migrations (v1-v15 catalogue)](storage-schema-and-migrations.md)
- [FTS5 transcript search](storage-fts5-search.md)
- [Transcripts CRUD](storage-crud-transcripts.md)
- [Tasks and subtasks CRUD](storage-crud-tasks.md)
- [Profiles and profile_terms CRUD](storage-crud-profiles.md)
- [Settings, error log, feedback, implementation rules](storage-crud-settings-and-misc.md)
- [File storage paths (database, recordings, crashes, logs)](storage-file-paths.md)

`lumotia-hotkey`:

- [Linux evdev listener (devices, hotplug, modifiers, Pressed/Released)](hotkey-linux-evdev.md)

Workspace and build glue:

- [Workspace `Cargo.toml` (members, release profile)](workspace-cargo.md)
- [CI: `check.yml`, `build.yml`, `audit.yml`](ci-pipeline.md)
- [Dev launcher (`run.sh`) and root `package.json`](dev-launcher-and-scripts.md)
- [Vite, SvelteKit, jsconfig](frontend-build-config.md)
- [Static assets and `pcm-processor.js` worklet](static-assets.md)
- [`HANDOVER.md` and root `README.md` pointer](dev-handover-pointer.md)

## How this slice connects to others

- **`lumotia-core` is the universal lower bound.** Every other crate in the workspace depends on it. The shape contract is: `Segment`, `Transcript`, `ModelId`, `EngineName`, `Megabytes`, `AudioSamples`, `MagnotiaError`, `Result`, plus `paths::AppPaths`. See [`core-types-and-enums.md`](core-types-and-enums.md).
- **Slice 1 (frontend)** never imports any of these crates directly. It reaches them through Tauri commands (slice 2). The single setting key the frontend cares about is `lumotia_preferences` (a JSON blob), persisted via `lumotia_storage::get_setting` / `set_setting` (slice 5) called from the Tauri preferences command (slice 2).
- **Slice 2 (Tauri runtime)** is the heaviest consumer. It calls `lumotia_storage::init` at startup, registers the SQLite pool as Tauri-managed state, and every command in `src-tauri/src/commands/*.rs` reaches into `lumotia_storage` for persistence and `lumotia_core::paths` for filesystem locations. The hotkey command at `src-tauri/src/commands/hotkey.rs` is the sole consumer of `lumotia_hotkey::EvdevHotkeyListener`.
- **Slice 3 (audio + transcription)** consumes `lumotia_core::types::{AudioSamples, Segment, Transcript}` and `lumotia_core::constants::{WHISPER_SAMPLE_RATE, WHISPER_CHANNELS, PARAKEET_*, CHUNK_INTERVAL_MS}`. The transcription engines also consume `lumotia_core::tuning::inference_thread_count(Workload::Whisper, gpu_offloaded)` for thread sizing.
- **Slice 4 (LLM + formatting + MCP)** consumes `lumotia_core::tuning::{inference_thread_count, Workload::Llm}`, `lumotia_core::paths::AppPaths::llm_models_dir()`, `lumotia_core::types::Segment` (formatting), and `lumotia_core::constants::SMART_PARAGRAPH_GAP_SECS` (formatting). The MCP server is the only consumer of `lumotia_storage::init_readonly` — it opens the DB read-only so no MCP tool can mutate user data even if the dispatcher misroutes a request.

## Existing in-repo docs

These are the critical-issue write-ups from the 2026-04-22 review that overlap with slice 5. Cross-referenced from the relevant per-page file. **Not duplicated here.**

- [`docs/issues/c3-migrations-atomicity.md`](../../issues/c3-migrations-atomicity.md) — drove the v9-onwards transactional migration design. See [`storage-schema-and-migrations.md`](storage-schema-and-migrations.md).
- [`docs/issues/c4-transcript-profile-fk.md`](../../issues/c4-transcript-profile-fk.md) — drove migration v9, the table rebuild that landed `transcripts.profile_id` with a real foreign key. See [`storage-schema-and-migrations.md`](storage-schema-and-migrations.md).
- [`docs/issues/keystore-thread-safety.md`](../../issues/keystore-thread-safety.md) — keystore lives in `crates/cloud-providers/` (slice 4) but the lesson generalises to every shared mutable state in `lumotia-core`. Cross-referenced from the tuning page.
- [`docs/issues/hotkey-linux-device-filter.md`](../../issues/hotkey-linux-device-filter.md) — the RB-12 hard-coded `KEY_A` / `KEY_R` filter that the current `device_supports_combo` replaced. See [`hotkey-linux-evdev.md`](hotkey-linux-evdev.md).
- [`docs/issues/power-assertion-macos-objc2.md`](../../issues/power-assertion-macos-objc2.md) — RB-08, the only open MAJOR per `HANDOVER.md`. The implementation lives in slice 2 (`src-tauri`) but the API design lives near `lumotia-core` conceptually. Linked from the slice debt section.
- [`docs/audit/phase0-cartography.md`](../../audit/phase0-cartography.md) — phase-0 codebase cartography, predates this map but covers crate-graph topology in summary form.
- [`docs/code-review-2026-04-22.md`](../../code-review-2026-04-22.md) — full audit. Slice 5 work items: RB-08 (macOS power assertion), RB-12 (hotkey filter, fixed), C3 (migrations atomicity, fixed), C4 (transcript profile FK, fixed). Several MAJORs across the slice 2 / 3 / 4 surface area too — see the document.

## Open questions, debt, drift

- **RB-08 still open.** macOS power assertion (`crates/.../power_assertion.rs` — surface lives in slice 2) is a non-functional stub awaiting the `objc2` rewrite plus runtime verification on Apple silicon. Per `HANDOVER.md`, gates v0.1 tagging. Tracked in [`docs/issues/power-assertion-macos-objc2.md`](../../issues/power-assertion-macos-objc2.md).
- **Schema head drift in human-facing docs.** `HANDOVER.md` (2026/04/25) says v14; the migration registry on disk is v15 (commit 2026/05/09 added `idx_transcripts_profile_created`). Worth a single sweep of human-facing handovers when the next session opens.
- **Hotkey is Linux-only at runtime.** macOS and Windows fall back to Tauri's global-shortcut plugin (slice 2). The crate compiles cleanly on all three OSes (the stub module is a no-op), but feature parity across platforms is provided by two different mechanisms. A single user-visible inconsistency: per-device hotplug. The Tauri plugin doesn't have it.
- **Power probe is Linux-only.** macOS / Windows return `PowerState::Unknown`, which callers treat as `OnAc`. Native probes (`IOPSGetProvidingPowerSourceType`, `GetSystemPowerStatus`) are noted-deferred in [`core-power.md`](core-power.md).
- **GPU probe is a stub.** `lumotia_core::hardware::probe_gpu()` returns `None`. CPU probe is wired (sysinfo + CPUID), Vulkan loader is probed via `libloading`, but the actual GPU vendor / VRAM are never populated. Recommendation scoring still works because it inspects `Some(gpu).acceleration` and gracefully scores zero-bonus when `None`. See [`core-hardware-probe.md`](core-hardware-probe.md).
- **`migration_v15` test exists but no migration v15 reverse path.** Migrations are append-only; the registry's contract is forward-only and idempotent. Reverting requires a fresh DB or a `DROP INDEX` migration v16. The contract is documented in [`storage-schema-and-migrations.md`](storage-schema-and-migrations.md).
- **Default profile is enforced by SQL triggers, not by code.** `00000000-0000-0000-0000-000000000001` cannot be deleted or renamed, but if a future migration drops or alters the trigger names (`trg_protect_default_profile_delete`, `trg_protect_default_profile_rename`), the protection silently disappears. No regression test asserts the triggers still exist on the head schema; today the integration tests only assert the protection at DML time.
- **Code-signing not configured.** Both macOS (Apple Developer ID) and Windows (code-signing certificate) are commented out in `build.yml`. Users hit Gatekeeper / SmartScreen on first run. Documented in [`ci-pipeline.md`](ci-pipeline.md).
- **`run.sh` hard-codes a Fedora-style `LIBCLANG_PATH`.** The launcher exports `/usr/lib64/llvm21/lib64`, which is correct on Jake's Monolith but breaks on a Debian/Ubuntu dev box. Worth softening to a `LIBCLANG_PATH=${LIBCLANG_PATH:-/usr/lib64/llvm21/lib64}` form.

## See also

- [Slice 1 — Frontend](../01-frontend/README.md)
- [Slice 2 — Tauri runtime](../02-tauri-runtime/README.md)
- [Slice 3 — Audio + transcription](../03-audio-transcription/README.md)
- [Slice 4 — LLM, formatting, MCP](../04-llm-formatting-mcp/README.md)
