---
name: Tauri config
type: architecture-map-page
slice: 02-tauri-runtime
last_verified: 2026/05/09
---

# Tauri config

> **Where you are:** [Architecture map](../README.md) → [Tauri runtime](README.md) → Tauri config

**Plain English summary.** The base `tauri.conf.json` declares the bundle identity, the main window size and chrome, the Content Security Policy, and the icons. The Linux overlay flips the main window from frameless to native-decorations, because Tauri's frameless path on Wayland does not honour diagonal resize reliably and webkit2gtk's drag-region adds latency.

## At a glance

- Paths: `src-tauri/tauri.conf.json` (43 LOC), `src-tauri/tauri.linux.conf.json` (17 LOC).
- Identifier: `uk.co.corbel.lumotia`.
- Tauri version targeted: schema `https://schema.tauri.app/config/2`.
- Main window labels: `main` (defined here), plus `tasks-float`, `transcript-viewer`, `transcription-preview` (built imperatively from `commands::windows`).
- Frontend: `npm run dev:frontend` for dev (port 1420), `npm run build` produces `../build` for release.
- Tauri commands exposed: none (config files only).
- Events emitted: none.
- Depends on: nothing at runtime; consumed by `tauri-build` in `build.rs` and by `build.rs::assert_loopback_llm_csp` (build-time CSP regression guard).
- Called from frontend at: the dev URL is what `vite` ships to during `cargo tauri dev`.

## What's in here

### `tauri.conf.json`

```
productName: "Lumotia"
version: "0.1.0"
identifier: "uk.co.corbel.lumotia"
```

#### `build`

```
beforeDevCommand: "npm run dev:frontend"
devUrl:           "http://localhost:1420"
beforeBuildCommand: "npm run build"
frontendDist:     "../build"
```

#### `app.windows[0]`

The main window. Title `"Lumotia"`, 1020×720 (min 960×600), centred, resizable, **frameless** (`decorations: false`). The Linux overlay flips this to `decorations: true`.

#### `app.security.csp`

```
default-src 'self';
script-src  'self';
style-src   'self' 'unsafe-inline';
img-src     'self' asset: https://asset.localhost;
connect-src ipc: http://ipc.localhost
            asset: https://asset.localhost
            http://127.0.0.1:* ws://127.0.0.1:*;
media-src   'self' asset: https://asset.localhost
```

The two `http://127.0.0.1:*` and `ws://127.0.0.1:*` entries are pinned by the build-time guard in `src-tauri/build.rs:30`. They must stay (so the bundled llama.cpp server / a BYO Ollama install can be `fetch()`-ed from the webview), and `localhost:*` must stay forbidden (so endpoints normalise to a single name and are never bypassed by the resolver). See [Tests](tests.md) for the runtime assertion.

`'unsafe-inline'` is permitted for `style-src` because Svelte injects component-scoped styles; without it, every `<style>` element would need a nonce. `script-src 'self'` keeps inline scripts forbidden.

#### `bundle`

`active: true`, `targets: "all"`, icons listed: `icons/32x32.png`, `icons/128x128.png`, `icons/128x128@2x.png`, `icons/icon.icns`, `icons/icon.ico`. `bundle.android.minSdkVersion: 24`.

### `tauri.linux.conf.json`

Tauri 2 merges per-platform overlays on top of the base config when the build target is detected. The Linux overlay re-declares `app.windows[0]` with the same dimensions as the base config but with `decorations: true`. This is the only difference. Reasons documented inline in `commands::windows::open_task_window` (`src-tauri/src/commands/windows.rs:58`): on Wayland the frameless path mishandles resize edges and adds drag latency; native KWin / Mutter decorations are reliable.

## Data flow

- `tauri-build::build()` consumes the config at compile time to generate the inline runtime context.
- `build.rs::assert_loopback_llm_csp` parses `tauri.conf.json` and asserts the `connect-src` directive includes the loopback entries. A regression breaks compilation, not just tests.
- `WebviewWindowBuilder` calls in `commands::windows` re-declare the secondary windows imperatively rather than adding them here, because they need preferences-injection, conditional decorations, GTK type-hints, and skip-taskbar / always-on-top settings that the static config can't express. The `secondary-windows` capability (see [Capabilities and ACL](capabilities-and-acl.md)) is what binds them to the same allowlist.

## Watch-outs

- Don't add `localhost:*` to `connect-src`. The build will fail at compile time. Use `127.0.0.1:*`.
- Don't add new `*.localhost` entries unless they pair with a Tauri-managed asset URL. Adding wildcards weakens CSP without giving up much in return.
- The `frontendDist` is a relative path resolved from `src-tauri/`. Moving the frontend out of `../build` requires updating both this and `package.json`'s build script.
- The bundle identifier is the macOS bundle ID and the Android applicationId. Changing it after a release would orphan installed apps from updates.
- The base config has `decorations: false`; macOS and Windows pick that up. macOS gets a custom Titlebar component drawn by Svelte. Windows would too if you ever ship a Windows build (none today).

## See also

- [App lifecycle](app-lifecycle.md) — the main window is fetched here via `app.get_webview_window("main")`.
- [Capabilities and ACL](capabilities-and-acl.md) — pairs with the CSP to constrain what the JS can call.
- [Cargo and features](cargo-and-features.md) — `build.rs` is what enforces the CSP at build time.
- [Tests](tests.md) — `csp_keeps_loopback_narrow` regression-tests the same property at runtime.
- [Window management](commands/windows.md) — the imperative builder for `tasks-float` / `transcript-viewer` / `transcription-preview`.
