--- name: Cargo and features type: architecture-map-page slice: 02-tauri-runtime last_verified: 2026/05/09 --- # Cargo and features > **Where you are:** [Architecture map](../README.md) → [Tauri runtime](README.md) → Cargo and features **Plain English summary.** This page walks the Tauri crate's `Cargo.toml`, its `build.rs`, and its `.cargo/config.toml`. The crate is the binary entry for the desktop app and the library entry for the Android target. The `whisper` cargo feature is the kill-switch for the whisper.cpp backend. Per-target dependency blocks gate macOS objc2 bindings, Linux GTK / WebKitGTK, and Windows base64 (used by the TTS PowerShell shim). `build.rs` enforces the CSP regression guard documented in `docs/whisper-ecosystem/brief.md` item #2. ## At a glance - Path: `src-tauri/Cargo.toml` (108 LOC), `src-tauri/build.rs` (81 LOC), `src-tauri/.cargo/config.toml` (3 LOC). - Tauri commands exposed: none. Build-system files only. - Events emitted: none. - Depends on: every workspace crate (slices 03–05) plus the Tauri ecosystem. - Called from frontend at: nothing direct. The build outputs land in the dev / packaged binary. ## What's in here ### `Cargo.toml` Package metadata: `name = "lumotia"`, `version = "0.1.0"`, `description = "Lumotia — Think out loud"`, `authors = ["CORBEL Ltd"]`, `edition = "2021"`. Lib stanza (`src-tauri/Cargo.toml:8`): ``` name = "lumotia_lib" crate-type = ["staticlib", "cdylib", "rlib"] ``` `staticlib` and `cdylib` are needed for the Android target where Tauri builds an `.aar` from a `cdylib`. `rlib` makes it consumable from `main.rs`. #### `[features]` ``` default = ["whisper"] whisper = ["lumotia-transcription/whisper"] ``` The `whisper` feature transitively enables `lumotia-transcription/whisper`. The crate-level `default-features = false` on `lumotia-transcription` (`src-tauri/Cargo.toml:33`) means a `--no-default-features` workspace build drops `whisper-rs-sys` entirely; Parakeet still works. `commands::models::load_model_from_disk` returns a clear runtime error for `Engine::Whisper` when the feature is off. #### `[build-dependencies]` `tauri-build = "2"`, plus `serde_json = "1"` for the CSP regression guard in `build.rs`. #### `[dependencies]` — workspace crates ``` lumotia-core lumotia-audio lumotia-transcription { default-features = false } lumotia-ai-formatting lumotia-storage lumotia-cloud-providers lumotia-hotkey lumotia-llm ``` The `cloud-providers` crate is referenced here even though no command file currently imports it. Likely reserved for the Phase-N upload flow that the diagnostic-report bundler hints at. #### `[dependencies]` — Tauri ``` tauri = { version = "2" } tauri-plugin-opener = "2" tauri-plugin-dialog = "2" tauri-plugin-notification = "2" ``` These three plugins are unconditional. `notification` supports Android natively (Phase 6 nudges) so it stays out of the desktop-only block. #### `[dependencies]` — runtime ``` serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["rt", "sync"] } arboard = "3.6.1" sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite"] } uuid = { version = "1", features = ["v4"] } ``` The `sqlx` block has `default-features = false` and only opts into `runtime-tokio` and `sqlite`. `lumotia-storage` already pulls the macros / migrate / any / json features via its own re-export, so duplicating them here would just bloat compile time. `sqlx` is named directly because `AppState` types it as `SqlitePool`; naming a transitive dep type still requires the dep be listed. #### `[dev-dependencies]` `tempfile = "3"` for the Phase 9 `commands::fs::write_text_file_cmd` tests. #### `[target.'cfg(not(target_os = "android"))'.dependencies]` ``` tauri = { version = "2", features = ["tray-icon"] } tauri-plugin-global-shortcut = "2" tauri-plugin-window-state = "2" tauri-plugin-autostart = "2" ``` Each is justified inline in `src-tauri/Cargo.toml:74`. Tray icons, global shortcuts, multi-window state, and autostart all either fail to compile against the Android NDK or are structurally meaningless on a single-window mobile activity. The Cargo gate matches the `#[cfg(not(target_os = "android"))]` blocks in `lib.rs` and `commands/windows.rs`. #### Per-OS dependency blocks ``` linux: webkit2gtk = "2.0", gtk = "0.18", gdk = "0.18" macos: objc2 = "0.6.4", objc2-foundation = { ... features = ["std", "NSString", "NSProcessInfo"] } windows: base64 = "0.22" ``` - `webkit2gtk`/`gtk`/`gdk` are used in `lib.rs` (auto-grant audio capture) and `commands/windows.rs` (set GTK `WindowTypeHint::Utility` on the preview overlay). Versions track what `webkit2gtk` 2.0 transitively depends on. - `objc2` + `objc2-foundation` power the macOS App Nap power assertion (`commands/power.rs::objc_bridge`). - `base64` is the Windows TTS encoder. PowerShell `-EncodedCommand` requires UTF-16-LE base64. ### `build.rs` Two responsibilities: 1. **Linker workaround.** `--allow-multiple-definition` is passed to GNU ld / lld on Linux because `llama-cpp-sys-2` and `whisper-rs-sys` both statically link their own copy of `ggml`, leading to duplicate symbols (`src-tauri/build.rs:8`). Documented as INTERIM; the intended fix is system-ggml shared-lib linking. Only emitted on Linux. 2. **CSP regression guard.** `assert_loopback_llm_csp` (`src-tauri/build.rs:30`) parses `tauri.conf.json` with `serde_json`, finds the `connect-src` directive (full-name match, not prefix), and asserts the four required tokens: `http://127.0.0.1:*` and `ws://127.0.0.1:*` must be present, `http://localhost:*` and `ws://localhost:*` must be absent. Fails compilation with a brief-item-#2 reference if any of those break. Re-runs when `tauri.conf.json` changes. `tauri_build::build()` is called last. Order matters: the CSP guard fails fast, before tauri-build does any of its own work. ### `.cargo/config.toml` ``` [env] LIBCLANG_PATH = "C:\\Program Files\\LLVM\\bin" ``` Hard-coded Windows path so `bindgen` (used by Tauri 2's macros and a couple of dependencies) can find clang during a Windows build. Harmless on non-Windows hosts — the env var is just unused. Foot-gun: a Windows developer with Clang in a non-default path will have to override or remove this. ## Data flow - `cargo build` reads `Cargo.toml`, runs `build.rs`, then compiles `src/lib.rs` and `src/main.rs`. The CSP regression guard fires before any source compiles. - The default features include `whisper`. A workspace-wide `cargo build --no-default-features` drops both whisper.cpp and the Tauri default features (which would also drop tray-icon, etc., so this is a niche build). - Plugin crates land in the dependency graph and contribute permission manifests that `gen/schemas/` is regenerated from at build time. ## Watch-outs - Adding a workspace crate dependency here also requires that crate to be in the root `Cargo.toml`'s workspace members (or referenced by `path =` here, which is what is done now). All eight workspace crates appear in this `Cargo.toml`. - The `--allow-multiple-definition` link arg is the kind of thing that hides bugs. If both ggml copies ever drift, the binary picks "the first definition" and the second copy's slightly-different symbol is silently shadowed. Track the `whisper-rs-sys` and `llama-cpp-sys-2` ggml pins together. - Bumping any of `objc2`, `objc2-foundation`, `webkit2gtk`, `gtk`, `gdk` is an ABI move. Test on each platform. - The hard-coded `LIBCLANG_PATH` should ideally come from an environment variable or a build-script probe. Until then, document it in `docs/dev-setup.md` for Windows contributors. ## See also - [App lifecycle](app-lifecycle.md) — `lib.rs::run` is what consumes everything declared here. - [Tauri config](tauri-config.md) — `build.rs` reads `tauri.conf.json` and pins the CSP shape. - [Tests](tests.md) — runtime regression for the same CSP property. - [Capabilities and ACL](capabilities-and-acl.md) — `gen/schemas/` files are regenerated when a plugin in this Cargo manifest changes.