# Tauri Admin Guide

A reference for engineers, architects, contributors, and operators of the Tauri framework.

## 1. Executive Summary

Tauri is an authoring system for native desktop and mobile applications whose UI is rendered by the host operating system's webview and whose application logic runs in a Rust binary. The two halves communicate over a typed IPC bridge gated by a capability-based access control system (ACL).

The repository at `tauri-apps/tauri` is a **Cargo workspace of 14 crates plus 2 npm packages** that, together, implement the runtime, the build pipeline, the bundler, the mobile glue, the JS bindings, and the developer CLI. It is licensed under MIT or Apache-2.0 (dual). It is governed by a programme inside [The Commons Conservancy](https://commonsconservancy.org/) and funded through Open Collective.

This guide covers the architecture, the build pipeline, every configuration knob, the ACL engine, the security model, the operations playbook, and the contribution workflow. By the end, you should have enough information to deploy Tauri at scale, fork it for a derivative product, or understand any production incident a Tauri app produces.

**Repository at a glance** (commit `df3fb97`, 2026-04-16):

| Metric | Value |
|--------|-------|
| Workspace crates | 14 (+ examples and integration tests) |
| npm packages | 2 (`@tauri-apps/api`, `@tauri-apps/cli`) |
| Source files | 959 |
| Source size | 2.6 MB of Rust + TypeScript |
| Rust edition / MSRV | 2021 / **1.77.2** |
| Default platforms | macOS 10.15+, Windows 7+, Linux (webkit2gtk-4.1), iOS 9+, Android 7+ |
| License | MIT OR Apache-2.0 |
| Tagged as `dev` branch | yes (production releases cut from `release` branch tags) |

> [!IMPORTANT]
> The default branch is `dev`. Releases are tagged from `release/*` branches. If you fork for a downstream distribution, base on a release tag, not on `dev`.

---

## 2. Architecture

### 2.1 The big picture

```text
┌──────────────────────────────────────────────────────────────────┐
│  YOUR APPLICATION                                                │
│                                                                  │
│  ┌────────────────────────┐         ┌─────────────────────────┐  │
│  │  Frontend              │         │  Backend                │  │
│  │  HTML / CSS / JS / TS  │         │  Rust (your code)       │  │
│  │  React / Svelte / Vue  │         │  #[tauri::command]      │  │
│  │  (or Yew / Leptos)     │         │  tauri::Builder         │  │
│  └─────────┬──────────────┘         └────────────┬────────────┘  │
│            │ window.__TAURI_INTERNALS__         │                │
│            │ invoke(cmd, args)                   │                │
│            v                                     v                │
│  ╔═══════════════════════════════════════════════════════════╗   │
│  ║                  IPC Bridge (custom protocol)              ║   │
│  ║   Webview <─ ipc:// ─> RuntimeAuthority <─> Command Handler║   │
│  ║                          ^                                 ║   │
│  ║                          │ ACL check (capability+permission)║  │
│  ╚═══════════════════════════════════════════════════════════╝   │
└──────────────────────────────────────────────────────────────────┘
                              │
       ┌──────────────────────┼──────────────────────┐
       v                      v                      v
┌──────────────┐      ┌──────────────┐      ┌──────────────┐
│ tauri-runtime│      │   plugins    │      │   bundler    │
│  (trait)     │      │ (fs/http/... │      │ (build-time) │
└───────┬──────┘      └──────────────┘      └──────────────┘
        │
        v
┌──────────────────────────────────────────────────┐
│           tauri-runtime-wry (impl)               │
│            wraps WRY + TAO                       │
└─────────────┬─────────────────────┬──────────────┘
              v                     v
       ┌─────────────┐      ┌──────────────┐
       │     TAO     │      │     WRY      │
       │ (windowing) │      │ (webview)    │
       └──────┬──────┘      └──────┬───────┘
              v                    v
       ┌──────────────────────────────────┐
       │   OS-native windowing + webview  │
       │  WebView2 / WKWebView /          │
       │  WebKitGTK / Android WebView     │
       └──────────────────────────────────┘
```

> [!NOTE]
> The IPC bridge is the most security-critical path in the architecture. Every frontend-to-backend call traverses `RuntimeAuthority::resolve_access` — if the ACL rejects it, the command handler never executes. This default-deny design means a compromised webview can only reach commands explicitly whitelisted for its window label.

### 2.2 Workspace layout

The Cargo workspace at `Cargo.toml` defines the crate graph:

```text
tauri/
├── crates/
│   ├── tauri/                          ← THE main runtime crate (downstream apps depend on this)
│   ├── tauri-runtime/                  ← Trait-only "what is a runtime"
│   ├── tauri-runtime-wry/              ← Default impl: WRY + TAO
│   ├── tauri-build/                    ← build.rs helpers
│   ├── tauri-codegen/                  ← Compile-time codegen (asset embedding, config parse)
│   ├── tauri-macros/                   ← `#[command]`, `generate_handler!`, `generate_context!`
│   ├── tauri-utils/                    ← Shared utilities, config types, ACL types, platform detect
│   ├── tauri-plugin/                   ← Plugin author SDK
│   ├── tauri-cli/                      ← The `tauri` Rust binary (clap)
│   ├── tauri-bundler/                  ← Cross-platform bundler (DMG / NSIS / MSI / DEB / RPM / AppImage)
│   ├── tauri-driver/                   ← WebDriver server for E2E tests
│   ├── tauri-macos-sign/               ← macOS keychain + codesign helpers
│   ├── tauri-schema-generator/         ← JSON schema generation for tauri.conf.json
│   ├── tauri-schema-worker/            ← Cloudflare Worker that serves schemas (docs site)
│   └── tests/
│       ├── acl/                        ← ACL fixture-based tests
│       ├── app-updater/                ← Updater integration tests
│       └── restart/                    ← Process relaunch tests
├── packages/
│   ├── api/                            ← @tauri-apps/api (TS bindings)
│   └── cli/                            ← @tauri-apps/cli (napi-rs wrapper)
├── examples/                           ← Reference apps (helloworld, api, isolation, multiwindow, ...)
├── bench/                              ← Performance benchmarks
├── audits/                             ← cargo-vet supply-chain audits
└── supply-chain/                       ← cargo-vet config
```

### 2.3 Crate-by-crate

#### `tauri` (core)

The crate users write `tauri = "2"` in their `Cargo.toml` to depend on. Re-exports `tauri-macros`, `tauri-utils`, and the runtime traits. Modules:

| Module | Responsibility |
|--------|---------------|
| `app` | `App`, `AppHandle`, `Builder`. The lifecycle root. |
| `manager` | The internal state manager that ties windows, webviews, plugins, IPC, and resources together. |
| `webview` | `Webview`, `WebviewBuilder`. |
| `window` | `Window`, `WindowBuilder`. (Desktop window vs webview is separated for multi-webview windows.) |
| `webviewWindow` | The convenience type that combines the two for the common case. |
| `event` | Pub/sub event system. |
| `ipc` | IPC core: `Channel`, `Invoke`, `RuntimeAuthority`, command resolution. |
| `menu` | (desktop-only) Menus and submenus. |
| `tray` | (desktop-only, behind `tray-icon` feature) Tray icons and tray menus. |
| `path` | Cross-platform path resolution (`AppData`, `Cache`, `Resource`, etc.). |
| `plugin` | The plugin runtime: `Plugin` trait, `Builder`, lifecycle. |
| `protocol` | Custom URI protocol handlers (the `tauri://` and `asset://` schemes). |
| `pattern` | The Brownfield / Isolation patterns. |
| `process` | Restart, exit, current binary path. |
| `resources` | `Resource`, `ResourceTable` for sharing typed handles between commands. |
| `scope` | Filesystem and shell scopes (used by ACL). |
| `state` | `State<T>` extractor for command arguments. |
| `image` | `Image` type and decoding (gated by `image-ico`/`image-png` features). |
| `vibrancy` | macOS vibrancy effect for transparent windows. |
| `async_runtime` | Tokio runtime accessor, used internally and exposed to plugin authors. |

#### `tauri-runtime`

A trait-only crate that defines what a runtime must provide. The actual implementations (currently only `tauri-runtime-wry`) plug in here. The split exists so a third party could swap out WRY for a different webview engine without forking core.

#### `tauri-runtime-wry`

The default runtime. Implements `tauri_runtime::Runtime` by wrapping [WRY](https://github.com/tauri-apps/wry) (the webview library) and [TAO](https://github.com/tauri-apps/tao) (the windowing library, a fork of `winit` with menu/tray support). All the OS-specific webview wiring lives here:

- macOS: WKWebView + Cocoa menus + AppKit windows
- Windows: WebView2 + Win32 windows + WPF tray
- Linux: WebKitGTK + GTK windows + ayatana app indicator
- iOS: WKWebView + UIWindow
- Android: System WebView + Activity

#### `tauri-build`

Provides `tauri_build::build()` to be called from your app's `build.rs`. It:

1. Parses `tauri.conf.json` at build time and emits cargo `rustc-cfg` flags
2. Generates the codegen context (icon embedding, config struct, asset map)
3. On mobile, generates the JNI bindings and Swift bridge
4. Validates the version of `tauri-plugin-*` dependencies match each other

#### `tauri-codegen`

The compile-time half of `tauri-build`. Reads `tauri.conf.json`, hashes assets, and generates the `Context` struct that `tauri::generate_context!()` returns at compile time. This is why your binary embeds your icons, your assets, and your config without any runtime file reads.

#### `tauri-macros`

The procedural macros: `#[tauri::command]`, `tauri::generate_handler![]`, `tauri::generate_context!()`, `#[tauri::mobile_entry_point]`. All Tauri's compile-time magic flows through here.

#### `tauri-utils`

Shared types used by everything else — primarily `Config`, the deserialized `tauri.conf.json` schema. Also `Platform`, `Target` enums; the `acl` submodule with the permission/capability data structures; pattern detection; CSP injection helpers; and the `assets` trait that backs both filesystem assets and embedded ones.

#### `tauri-plugin`

The SDK plugin authors use. Provides the `Builder`, lifecycle hooks, and the helper types for declaring plugin-scoped commands and permissions.

#### `tauri-cli`

The `tauri` binary. A clap-derive Rust app with 17 top-level subcommands (see User Guide §4 for the user-facing list). Each subcommand lives in its own module under `crates/tauri-cli/src/`:

| Module | Subcommand |
|--------|-----------|
| `init` | `tauri init` — scaffold a new project. |
| `dev` | `tauri dev` — debug build + run with hot-reload. |
| `build` | `tauri build` — release build + bundle. |
| `bundle` | `tauri bundle` — re-bundle without rebuilding. |
| `add` / `remove` | `tauri add`/`tauri remove` — plugin install/uninstall. |
| `plugin` | `tauri plugin new/init/android/ios` — author a plugin. |
| `mobile` | `tauri ios`, `tauri android` — mobile lifecycle. |
| `migrate` | `tauri migrate` — v1 → v2 upgrade. |
| `info` | `tauri info` — diagnostics dump. |
| `icon` | `tauri icon` — multi-platform icon generation. |
| `signer` | `tauri signer generate/sign` — updater signing. |
| `acl` | `tauri permission`, `tauri capability` — manage the ACL. |
| `completions` | `tauri completions` — shell completions. |
| `inspect` | `tauri inspect` — print derived bundler values. |
| `interface` | (internal) Interface trait for `dev`/`build` to abstract over Rust vs other interfaces. |
| `helpers` | (internal) Shared helpers: `app_paths`, `cargo`, `npm`, `prompts`, `template`, `framework`. |

The CLI entry point is `crates/tauri-cli/src/main.rs`, which detects whether it was invoked as `cargo-tauri tauri ...` or directly and forwards to `tauri_cli::run()` in `lib.rs`.

#### `tauri-bundler`

The cross-platform bundler. Each format lives in `crates/tauri-bundler/src/bundle/`:

| Module | Output |
|--------|--------|
| `bundle/macos/app` | `.app` directory bundle. |
| `bundle/macos/dmg` | `.dmg` disk image (calls `hdiutil`). |
| `bundle/macos/sign` | macOS code signing (calls `codesign`). |
| `bundle/macos/notarize` | Notarization via `xcrun notarytool`. |
| `bundle/windows/nsis` | NSIS installer (downloads NSIS, runs `makensis`). |
| `bundle/windows/msi` | WiX installer (downloads WiX Toolset, runs `candle` + `light`). |
| `bundle/linux/deb` | Debian package (no external tool needed). |
| `bundle/linux/rpm` | RPM package (uses `rpmbuild` if present; falls back to in-process). |
| `bundle/linux/appimage` | AppImage (downloads `appimagetool`). |
| `bundle/linux/freedesktop` | `.desktop` file generation. |
| `bundle/updater` | `.tar.gz` + `.sig` for the updater. |
| `bundle/kmp` | Kotlin Multiplatform XCFramework (mobile plugin packaging). |

Bundlers download their tools on demand (NSIS, WiX, appimagetool) and cache them under the user's cache directory. The downloads can be redirected to a mirror via `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE`.

#### `tauri-driver`

A WebDriver server that lets you E2E-test Tauri apps using standard Selenium / WebDriver clients. Spawns the app, attaches a WebDriver session per webview. Documented in the `crates/tauri-driver/README.md`.

#### `tauri-macos-sign`

Helpers for macOS keychain operations: importing certificates, signing arbitrary files, managing the build-time keychain. Used by the bundler's macOS code signing.

#### `tauri-schema-generator` and `tauri-schema-worker`

The schema generator produces a JSON Schema document from the `tauri-utils::Config` Rust type via `schemars`. It's invoked at compile time of the CLI to emit `crates/tauri-cli/config.schema.json`. The schema worker is a Cloudflare Worker that serves the schemas to the docs site so editors can fetch them at runtime.

#### `packages/api`

The TypeScript bindings users `import` from `@tauri-apps/api/...`. Build artifacts go to `packages/api/dist/` via Rollup. The package exposes:

| Module | Purpose |
|--------|---------|
| `@tauri-apps/api/core` | `invoke`, `Channel`, `convertFileSrc`, `transformCallback`. |
| `@tauri-apps/api/event` | `listen`, `emit`, `once`, `TauriEvent` enum, targeted variants. |
| `@tauri-apps/api/window` | `getCurrentWindow`, `Window`, `LogicalSize`, `PhysicalSize`, monitor info. |
| `@tauri-apps/api/webview` | `Webview`, `getCurrentWebview`. |
| `@tauri-apps/api/webviewWindow` | `WebviewWindow` (the combined type). |
| `@tauri-apps/api/menu` | Menu / Submenu / MenuItem builders. |
| `@tauri-apps/api/tray` | `TrayIcon`. |
| `@tauri-apps/api/path` | `appDataDir`, `appConfigDir`, `resolveResource`, etc. |
| `@tauri-apps/api/dpi` | `LogicalPosition`, `PhysicalPosition`, `LogicalSize`, `PhysicalSize`. |
| `@tauri-apps/api/image` | `Image` factory + serialization helpers. |
| `@tauri-apps/api/app` | App version, name, hide, show, exit. |
| `@tauri-apps/api/mocks` | `mockIPC`, `mockWindows` for unit-testing frontends without a real Tauri runtime. |

#### `packages/cli`

The npm distribution of the CLI. Wraps the Rust `tauri-cli` binary using [napi-rs](https://github.com/napi-rs/napi-rs) and ships precompiled binaries per `(os, arch)` combo under `packages/cli/npm/<target-triple>/`. The N-API binding is in `packages/cli/src/`.

### 2.4 The IPC bridge

The IPC is the crown jewel — every command call, event, channel message, and plugin invocation goes through it.

```text
[ Webview JS ]
      │
      │ window.__TAURI_INTERNALS__.invoke('cmd_name', args)
      v
[ JS shim that POSTs to ipc:// or postMessage to the host ]
      │
      v
[ tauri-runtime-wry IPC handler ]                ← OS-specific intercept
      │
      v
[ Manager::run_invoke_handler ]
      │
      v
[ RuntimeAuthority::resolve_access ]              ← ACL check
      │  YES → continue
      │  NO  → reject with "command not allowed by ACL"
      v
[ Command resolver ]                              ← parameter extraction
      │
      v
[ Your #[tauri::command] fn ]
      │
      v
[ Serialize result ]
      │
      v
[ Webview JS Promise resolves with result ]
```

Source layout under `crates/tauri/src/ipc/`:

| File | Role |
|------|------|
| `mod.rs` | Public API: `Invoke`, `InvokeBody`, `InvokeResponseBody`. |
| `authority.rs` | `RuntimeAuthority` — owns the resolved capability set and answers "may window X call command Y on plugin Z?". |
| `capability_builder.rs` | Programmatic capability construction (used by `add_capability`). |
| `command.rs` | Command resolver — extracts parameters from the IPC payload. |
| `channel.rs` | `Channel<T>` for streaming responses back to JS. |
| `format_callback.rs` | Legacy callback formatter for early IPC API. |
| `protocol.rs` | The `ipc://` custom URI protocol. |

The IPC payload is JSON by default but can be raw bytes via `InvokeBody::Raw` for performance-sensitive commands (file uploads, image encoding).

### 2.5 The plugin runtime

A plugin is a struct that implements `tauri::plugin::Plugin`:

```rust
pub trait Plugin<R: Runtime>: Send {
    fn name(&self) -> &'static str;
    fn initialize(&mut self, app: &AppHandle<R>, config: serde_json::Value) -> Result<()>;
    fn extend_api(&mut self, invoke: Invoke<R>) -> bool;
    fn on_event(&mut self, app: &AppHandle<R>, event: &RunEvent);
    fn on_navigation(&mut self, window: &Window<R>, url: &Url) -> bool;
    fn on_webview_ready(&mut self, window: Window<R>);
    fn on_drop(&mut self, app: AppHandle<R>);
    fn js_init_script(&self) -> Option<String>;
}
```

Most plugins use `tauri_plugin::Builder` to assemble the trait impl declaratively. The builder also handles the permission file discovery (looking for `permissions/*.toml`) and the JS init script injection.

### 2.6 The build pipeline (compile time)

When you run `cargo build` (directly or via `tauri build`), this happens:

```text
1. cargo runs build.rs in your src-tauri/
   └─> tauri_build::build()
       ├─> Reads tauri.conf.json
       ├─> Validates plugin version compatibility (across tauri/tauri-plugin-*)
       ├─> Emits cargo:rustc-cfg=mobile|desktop|tauri_*=value
       ├─> On mobile: generates JNI/Swift bindings
       └─> Triggers codegen for the Context struct

2. cargo compiles your code
   └─> tauri::generate_context!() macro
       ├─> Calls tauri_codegen::context()
       ├─> Embeds icons, assets, config struct
       └─> Returns a Context<R> at runtime

3. cargo links the binary

4. (only via `tauri build`) tauri_bundler::bundle_project()
   ├─> Detects target triple
   ├─> Picks bundle formats from config + flags
   ├─> Per format: download tools, build artifact, sign
   └─> Emits target/release/bundle/<format>/<files>
```

### 2.7 Frontend asset serving

By default, Tauri uses the `tauri://` custom protocol (or `asset://` for filesystem assets):

- Webview navigates to `tauri://localhost`
- The `tauri-runtime-wry` IPC handler intercepts and serves the embedded asset
- No localhost HTTP server runs (that's the `tauri-plugin-localhost` opt-in)

This is why your bundle is small and there's no port to clash with. It's also why you have to declare `connect-src` in CSP to include `ipc:` and `http://ipc.localhost` (the IPC reply channel).

---

## 3. Requirements and Installation

### 3.1 Build host requirements

| Component | Required Version | Notes |
|-----------|------------------|-------|
| Rust | **1.77.2** or newer | Workspace `rust-version`. Older versions fail at compile time. |
| Cargo | matches Rust | rustup-managed. |
| Node.js | 18+ | Only if using the npm path or a JS frontend. |
| pnpm / npm / yarn / bun | latest | Package manager for `@tauri-apps/cli` and `@tauri-apps/api`. |
| Xcode CLI Tools | 14+ | macOS only, required for `tauri build`. |
| Visual Studio Build Tools | 2019+ | Windows only, "Desktop development with C++" workload. |
| WebView2 Runtime | 95+ | Windows only, preinstalled on Win11. |
| `webkit2gtk-4.1-dev` | 4.1+ | Linux only (Tauri v2). v1 wants 4.0. |
| `libxdo-dev` | any | Linux only, optional, for menu Cut/Copy/Paste. |
| Android Studio + SDK 36 + NDK 29.0.13846066 | exact | Android targets only. CLI installs them on prompt. |
| Xcode | 15+ | iOS targets only, macOS host. |
| `ldid` | any | macOS bundler dependency for codesign. |

### 3.2 Building Tauri itself (contributor / fork)

```sh
git clone https://github.com/tauri-apps/tauri
cd tauri
# Install pnpm
corepack enable
# Build the workspace
cargo check --workspace --all-features
# Build npm packages
pnpm install
pnpm run build
# Run a smoke test
cd examples/api/src-tauri
cargo tauri dev
```

The workspace patches `schemars` and re-points all internal dependencies to local paths via `[patch.crates-io]` in the root `Cargo.toml`:

```toml
[patch.crates-io]
schemars_derive = { git = 'https://github.com/tauri-apps/schemars.git', branch = 'feat/preserve-description-newlines' }
tauri = { path = "./crates/tauri" }
tauri-plugin = { path = "./crates/tauri-plugin" }
tauri-utils = { path = "./crates/tauri-utils" }
```

### 3.3 Release profiles

The workspace ships two release profiles:

```toml
[profile.release]
panic = "abort"           # No unwinding → smaller binary
codegen-units = 1         # Single codegen unit → better LLVM optimization
lto = true                # Link-time optimization
incremental = false       # Disable incremental for max optimization
opt-level = "s"           # Optimize for size
strip = true              # Strip symbols

[profile.release-size-optimized]
inherits = "release"
codegen-units = 1
lto = true
incremental = false
opt-level = "s"
```

User apps inherit `[profile.release]` defaults but can override in their own `Cargo.toml`. For maximum size optimization use `opt-level = "z"` (smaller, slower) and add `strip = "symbols"`.

---

## 4. Configuration Mastery — `tauri.conf.json`

The configuration file is the source of truth for what your app is. It's parsed at compile time (frozen into the binary) and at run time (for non-build settings). Every field maps to a Rust type in `tauri_utils::config::Config`.

### 4.1 Top-level keys

| Key | Type | Purpose |
|-----|------|---------|
| `$schema` | string | URL or path to the JSON schema. Enables editor completions. |
| `productName` | string | Human display name. |
| `version` | string \| object | App version. Either a SemVer string or `{ major, minor, patch, ... }`. |
| `identifier` | string | Reverse-DNS bundle ID. Cannot be `com.tauri.dev` in production. |
| `mainBinaryName` | string | Override the binary name (defaults to `productName` slugified). |
| `app` | object | Runtime app configuration (windows, security, tray). |
| `build` | object | Build-time configuration (dev URL, frontend dist, before commands). |
| `bundle` | object | Bundling configuration (per-format settings, icons, signing). |
| `plugins` | object | Per-plugin configuration. |

### 4.2 `build` block

| Field | Type | Default | Purpose |
|-------|------|---------|---------|
| `frontendDist` | string \| string[] | `null` | Path(s) to built frontend, relative to `tauri.conf.json`. |
| `devUrl` | string | `null` | Dev server URL loaded by `tauri dev`. |
| `beforeDevCommand` | string \| object | `null` | Shell command before `tauri dev`. Object form: `{ cwd, script, wait }`. |
| `beforeBuildCommand` | string | `null` | Shell command before `tauri build` compiles Rust. |
| `beforeBundleCommand` | string | `null` | Shell command before bundling step. |
| `runner` | string | `cargo` | Override the cargo binary (e.g. `cross`). |
| `features` | string[] | `[]` | Cargo features to enable for `tauri dev` and `tauri build`. |
| `removeUnusedCommands` | boolean | `false` | Strip uncalled IPC commands at build time (smaller binaries). |

### 4.3 `app` block

| Field | Type | Default | Purpose |
|-------|------|---------|---------|
| `windows` | array | `[default]` | Window definitions. |
| `security.csp` | string \| object | `null` | Content Security Policy. Object form per-directive. |
| `security.devCsp` | string | `null` | CSP override for `tauri dev`. |
| `security.freezePrototype` | boolean | `false` | Freeze JS prototypes to block prototype pollution. |
| `security.dangerousDisableAssetCspModification` | boolean | `false` | **DO NOT USE** unless you know what you're doing. |
| `security.pattern.use` | `"brownfield"` \| `"isolation"` | `brownfield` | Security pattern selection. |
| `security.pattern.options` | object | depends | Pattern-specific options (e.g. `dir` for isolation). |
| `security.assetProtocol.enable` | boolean | `false` | Enable the `asset://` custom protocol. |
| `security.assetProtocol.scope` | object | empty | Glob allow/deny lists for asset access. |
| `security.headers` | object | `{}` | Per-route HTTP headers injected by the IPC server. |
| `security.capabilities` | array | `[]` | Inline capabilities (alternative to `capabilities/*.json` files). |
| `withGlobalTauri` | boolean | `false` | Expose `window.__TAURI__` for vanilla JS. |
| `macOSPrivateApi` | boolean | `false` | Enable transparent windows + fullscreenEnabled on macOS. |
| `trayIcon` | object | `null` | Static tray icon definition. Auto-enables `tray-icon` cargo feature. |
| `enableGTKAppId` | boolean | `false` | Use `identifier` as the GTK app ID on Linux. |

### 4.4 `app.windows[]` schema

Each window object:

| Field | Type | Default | Purpose |
|-------|------|---------|---------|
| `label` | string | `"main"` | Unique window ID. **Critical** — used in capability targeting and Rust APIs. |
| `url` | string | `"index.html"` | URL or path loaded into the webview. |
| `title` | string | `productName` | Initial window title. |
| `width` / `height` | number | `800` / `600` | Initial size. |
| `x` / `y` | number | (centered) | Initial position. |
| `minWidth` / `minHeight` | number | none | Minimum size constraints. |
| `maxWidth` / `maxHeight` | number | none | Maximum size constraints. |
| `resizable` | boolean | `true` | Allow user resize. |
| `maximizable` / `minimizable` / `closable` | boolean | `true` | Window button enables. |
| `fullscreen` | boolean | `false` | Start fullscreen. |
| `transparent` | boolean | `false` | Transparent window. Requires `macos-private-api` on macOS. |
| `decorations` | boolean | `true` | Show OS chrome. `false` = chromeless window. |
| `alwaysOnTop` | boolean | `false` | Stay above other windows. |
| `skipTaskbar` | boolean | `false` | Hide from taskbar/dock. |
| `theme` | `"Light"` \| `"Dark"` \| `null` | system | Force a theme. |
| `titleBarStyle` | `"Visible"` \| `"Transparent"` \| `"Overlay"` | `Visible` | macOS-only. |
| `hiddenTitle` | boolean | `false` | macOS hide title text but keep buttons. |
| `acceptFirstMouse` | boolean | `false` | macOS accept clicks that bring window to front. |
| `tabbingIdentifier` | string | none | macOS tab grouping ID. |
| `additionalBrowserArgs` | string | none | Windows: WebView2 command-line flags. |
| `windowEffects` | object | none | macOS vibrancy / Windows acrylic. |
| `incognito` | boolean | `false` | Don't persist cookies/storage. |
| `parent` | string | none | Parent window label (modal child). |
| `proxyUrl` | string | none | HTTP/SOCKS proxy URL for the webview. |
| `zoomHotkeysEnabled` | boolean | `false` | Allow Ctrl+/Ctrl-/Ctrl0 zoom. |
| `useHttpsScheme` | boolean | `false` | Serve via `https://tauri.localhost` (better for cookie compat). |
| `browserExtensionsEnabled` | boolean | `false` | Allow installed browser extensions (Windows WebView2). |
| `devtools` | boolean | (debug=`true`) | Enable webview devtools. |
| `userAgent` | string | (default) | Override the webview User-Agent. |
| `dragDropEnabled` | boolean | `true` | Receive `tauri://drag-*` events. |
| `visible` | boolean | `true` | Start visible. Set `false` to show after data loads. |
| `focus` | boolean | `true` | Start focused. |
| `center` | boolean | `false` | Ignore `x`/`y`, center on screen. |

### 4.5 `bundle` block

| Field | Type | Default | Purpose |
|-------|------|---------|---------|
| `active` | boolean | `true` | Enable bundling. |
| `targets` | array \| `"all"` | `"all"` | Restrict bundle formats. |
| `icon` | string[] | `[]` | Paths to icon files. Required for production. |
| `category` | string | `null` | macOS LSApplicationCategoryType. |
| `copyright` | string | `null` | Copyright string in installers. |
| `licenseFile` | string | `null` | Path to LICENSE for installer EULA pages. |
| `shortDescription` | string | `null` | Short description for installers. |
| `longDescription` | string | `null` | Long description for installers. |
| `homepage` | string | `null` | Homepage URL. |
| `publisher` | string | `null` | Publisher name (Windows installers). |
| `resources` | array \| object | `null` | Files to copy into bundle. |
| `externalBin` | string[] | `null` | Sidecar binaries to bundle. |
| `fileAssociations` | array | `null` | Register file extensions with the OS. |
| `protocol` | array | `null` | Register URL schemes (alternative to deep-link plugin for static schemes). |
| `createUpdaterArtifacts` | boolean \| `"v1Compatible"` | `false` | Generate updater `.tar.gz` + `.sig`. |
| `linux` | object | `{}` | Linux-specific bundle settings (Deb, Rpm, AppImage subblocks). |
| `macOS` | object | `{}` | macOS-specific (frameworks, signing, notarization, dmg config). |
| `windows` | object | `{}` | Windows-specific (NSIS, WiX, certificate). |
| `iOS` | object | `{}` | iOS-specific (development team, min version). |
| `android` | object | `{}` | Android-specific (min SDK, version code, signing). |

### 4.6 `bundle.macOS` deep-dive

| Field | Type | Purpose |
|-------|------|---------|
| `frameworks` | string[] | Embed frameworks in `.app/Contents/Frameworks/`. |
| `minimumSystemVersion` | string | `LSMinimumSystemVersion`. Default `10.15`. |
| `exceptionDomain` | string | App Transport Security exception. |
| `signingIdentity` | string | Codesign identity. Use `"-"` for ad-hoc signing. |
| `hardenedRuntime` | boolean | Enable hardened runtime (required for notarization). |
| `entitlements` | string | Path to `.entitlements` plist. |
| `providerShortName` | string | Apple Provider short name (for `notarytool`). |
| `dmg` | object | DMG-specific: `background`, `windowSize`, `windowPosition`, `appPosition`, `applicationFolderPosition`. |

Notarization environment variables:

| Variable | Required for notarization |
|----------|--------------------------|
| `APPLE_ID` | yes (Apple ID email) |
| `APPLE_PASSWORD` | yes (app-specific password) |
| `APPLE_TEAM_ID` | yes (10-char team ID) |
| `APPLE_API_KEY` | alternative to `APPLE_PASSWORD` (App Store Connect API key) |
| `APPLE_API_ISSUER` | with `APPLE_API_KEY` |
| `APPLE_API_KEY_PATH` | with `APPLE_API_KEY` |

### 4.7 `bundle.windows` deep-dive

| Field | Purpose |
|-------|---------|
| `digestAlgorithm` | `"sha256"` (recommended) or `"sha1"`. |
| `certificateThumbprint` | Cert thumbprint in the Windows cert store. |
| `timestampUrl` | Timestamp server URL for code signing. |
| `tsp` | Use RFC 3161 timestamp (`true`) vs Authenticode (`false`). |
| `webviewInstallMode` | `"downloadBootstrapper"`, `"embedBootstrapper"`, `"offlineInstaller"`, `"fixedRuntime"`, `"skip"`. |
| `wix` | WiX-specific config: `language`, `template`, `fragmentPaths`, `componentRefs`, `featureRefs`, `mergeRefs`, `enableElevatedUpdateTask`, `bannerPath`, `dialogImagePath`, `upgradeCode`. |
| `nsis` | NSIS-specific: `template`, `installerIcon`, `installMode`, `languages`, `displayLanguageSelector`, `compression`, `customLanguageFiles`, `headerImage`, `sidebarImage`, `installerHooks`. |
| `signCommand` | Override the default `signtool` command. |

### 4.8 `bundle.linux` deep-dive

```json
{
  "bundle": {
    "linux": {
      "deb": {
        "depends": ["libwebkit2gtk-4.1-0", "libgtk-3-0"],
        "section": "utils",
        "priority": "optional",
        "files": { "/etc/myapp/config": "../etc/config" },
        "desktopTemplate": "path/to/template.desktop"
      },
      "rpm": {
        "depends": ["webkit2gtk4.1"],
        "release": "1",
        "epoch": 0,
        "files": { "/etc/myapp/config": "../etc/config" },
        "desktopTemplate": "path/to/template.desktop"
      },
      "appimage": {
        "bundleMediaFramework": false,
        "files": {}
      }
    }
  }
}
```

### 4.9 `plugins` block

Each plugin defines its own schema under `plugins.<name>`:

```json
{
  "plugins": {
    "fs": {
      "scope": ["$APPCONFIG/**", "$APPDATA/**"],
      "requireLiteralLeadingDot": false
    },
    "shell": {
      "open": true,
      "scope": [
        { "name": "git", "cmd": "git", "args": [{ "validator": "^[\\w-]+$" }] }
      ]
    },
    "updater": {
      "active": true,
      "endpoints": ["https://releases.example.com/{{target}}/{{current_version}}/manifest.json"],
      "dialog": false,
      "pubkey": "<your-base64-pubkey>",
      "windows": { "installMode": "passive" }
    },
    "cli": {
      "description": "My App",
      "args": [{ "name": "verbose", "short": "v" }],
      "subcommands": {
        "update": { "description": "Update", "args": [] }
      }
    }
  }
}
```

The schema for each plugin lives in that plugin's own crate (`tauri-plugin-fs/permissions/schemas/`, etc.). Use `$schema` in your config to get editor completions.

### 4.10 Config merging

Configs merge in this order (later wins):

1. Base `tauri.conf.json` (or `Tauri.toml` with `config-toml` feature, or `tauri.conf.json5` with `config-json5`)
2. Platform-specific file (`tauri.linux.conf.json`, `tauri.macos.conf.json`, `tauri.windows.conf.json`, `tauri.android.conf.json`, `tauri.ios.conf.json`)
3. `--config <FILE>` arguments (one or more, merged in order)
4. `--config '<JSON>'` inline arguments
5. `TAURI_CONFIG` environment variable (inline JSON)

The merge is **JSON merge patch** semantics — objects merge recursively, arrays are replaced wholesale.

> [!IMPORTANT]
> Array fields are **replaced**, not concatenated. If you want to add to a window list in a platform override, you must include the full base array plus your additions.

> [!IMPORTANT]
> The `identifier` field is baked into filesystem paths, bundle IDs, and OS registrations at first release. Changing it after distribution creates a new app identity — users will see a second app instead of an update. Treat it as immutable once shipped.

---

## 5. The Permission and Capability System (ACL)

### 5.1 Why it exists

Tauri v1's allowlist was an opt-in flag per built-in API. It had no scope (file APIs were on or off), no per-window granularity, and didn't extend to plugins. v2 replaces it with a structured ACL designed to:

1. **Default-deny everything.** A fresh app with no capabilities can call zero IPC commands.
2. **Scope per command.** Each command, in core or plugin, gets its own permission name.
3. **Bind to windows.** A capability lists which window labels it applies to.
4. **Carry a payload.** Permissions can include allow/deny scope objects (file paths, URL globs, command patterns).
5. **Compose.** Plugins ship default permission bundles; you mix them.

### 5.2 Permission

A permission is a TOML or JSON file (TOML is the convention) inside a plugin's `permissions/` directory:

```toml
"$schema" = "schemas/schema.json"

[default]
description = "Default permissions for the fs plugin (read-only access to AppData)"
permissions = [
  "allow-read-text-file",
  "allow-read-binary-file",
  "allow-exists",
  "allow-stat"
]

[[permission]]
identifier = "allow-read-text-file"
description = "Allows reading text files"
commands.allow = ["read_text_file"]

[[permission]]
identifier = "allow-write-text-file"
description = "Allows writing text files"
commands.allow = ["write_text_file"]
```

| Field | Purpose |
|-------|---------|
| `[default]` | The bundle other capabilities reference as `<plugin>:default`. |
| `[[permission]]` | A single permission. |
| `identifier` | Local name (referenced as `<plugin>:<identifier>` in capabilities). |
| `commands.allow` | Commands this permission allows. |
| `commands.deny` | Commands this permission explicitly denies (deny wins over allow). |
| `scope` | Optional payload schema (per-plugin meaning). |
| `platforms` | Restrict to specific OS. |

The schema is published at `crates/tauri-cli/permission.schema.json`.

### 5.3 Capability

A capability is a JSON file in `src-tauri/capabilities/`:

```json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "Capabilities for the main window",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    "fs:allow-write-text-file",
    {
      "identifier": "fs:scope",
      "allow": [{ "path": "$APPCONFIG/*.json" }],
      "deny": [{ "path": "$APPCONFIG/secrets.json" }]
    }
  ],
  "platforms": ["linux", "macOS", "windows"],
  "local": true
}
```

| Field | Purpose |
|-------|---------|
| `identifier` | Unique name in the project. |
| `windows` / `webviews` | Glob lists of labels. `["*"]` matches all. |
| `permissions` | List of permission references (string) or scope objects. |
| `platforms` | Restrict by OS. |
| `local` | If `false`, applies to remote URLs too. Default `true`. |
| `remote` | Inverse of `local` for explicit remote scoping. |
| `context` | `"local"`, `"remote"`, or both — alternative to `local`/`remote`. |

### 5.4 Capability resolution

At build time, `tauri-build` walks `src-tauri/capabilities/` and merges every JSON file. The merged set is fed to `tauri-codegen`, which:

1. Validates every permission identifier exists
2. Resolves `<plugin>:default` to the plugin's `[default]` bundle
3. Computes a per-window allowlist
4. Generates a Rust `RuntimeAuthority` populated at `app.run()` time

At runtime, every IPC call passes through `RuntimeAuthority::resolve_access(window_label, command, payload_scope)`. If the command isn't in the window's allowlist, the call is rejected with the "command not allowed by ACL" error before any handler runs.

### 5.5 Inline capabilities (config-only)

For small apps you can avoid `capabilities/*.json` files and inline them in `tauri.conf.json`:

```json
{
  "app": {
    "security": {
      "capabilities": [
        {
          "identifier": "main",
          "windows": ["main"],
          "permissions": ["core:default", "fs:default"]
        }
      ]
    }
  }
}
```

### 5.6 Runtime capabilities (`dynamic-acl` feature)

With the default `dynamic-acl` feature, plugins or your code can register capabilities at runtime:

```rust
use tauri::Manager;

app.add_capability(serde_json::json!({
    "identifier": "user-granted-fs",
    "windows": ["main"],
    "permissions": [
        {
            "identifier": "fs:allow-read-text-file",
            "allow": [{ "path": user_picked_dir }]
        }
    ]
}))?;
```

This is how the dialog plugin "expands" the FS scope when a user picks a file — the picked path is added to a dynamically registered capability.

### 5.7 Schemas

Tauri's build pipeline regenerates ACL schemas every compile under `src-tauri/gen/schemas/`:

- `desktop-schema.json` — for desktop capabilities
- `mobile-schema.json` — for mobile capabilities
- `linux-schema.json`, `macOS-schema.json`, `windows-schema.json`, `android-schema.json`, `iOS-schema.json` — per-platform

Reference these in your capability files' `$schema` field for editor completions.

### 5.8 `tauri permission` and `tauri capability` CLI

| Command | Action |
|---------|--------|
| `tauri permission new <id>` | Create a new permission TOML (in your plugin's `permissions/` directory). |
| `tauri permission add <perm-id> <capability-id>` | Append a permission to a capability file. |
| `tauri permission rm <perm-id> <capability-id>` | Remove a permission from a capability file. |
| `tauri permission ls` | List all known permissions in the project. |
| `tauri capability new <id>` | Create a new capability JSON file. |

---

## 6. The Bundler

### 6.1 Bundler invocation

The bundler is invoked as part of `tauri build` or standalone via `tauri bundle`. Its entry point is `tauri_bundler::bundle_project()` in `crates/tauri-bundler/src/bundle.rs`.

```rust
let settings = SettingsBuilder::new()
    .project_out_directory("target/release")
    .package_settings(...)
    .bundle_settings(...)
    .build()?;
let outputs = tauri_bundler::bundle_project(&settings)?;
```

The CLI does this on your behalf.

### 6.2 Per-format pipeline

Each format's logic lives in `crates/tauri-bundler/src/bundle/<platform>/<format>/`:

**macOS DMG** — Builds the `.app` first (under `bundle/macos/app.rs`), then creates a DMG via `hdiutil` with a styled background, icon positions, and Applications shortcut.

**macOS notarization** — `bundle/macos/sign.rs` invokes `codesign` with the user's identity. `bundle/macos/notarize.rs` calls `xcrun notarytool` and (unless `--skip-stapling`) waits for the ticket and runs `xcrun stapler staple`.

**Windows NSIS** — Downloads NSIS to the user's cache dir, generates `installer.nsi` from a Handlebars template (`bundle/windows/nsis/installer.nsi`), runs `makensis.exe`, then signs the resulting `.exe` if a signing config is present.

**Windows MSI** — Downloads WiX Toolset, generates a WXS file from `bundle/windows/msi/main.wxs` template, runs `candle.exe` then `light.exe`, then signs.

**Linux Deb** — Builds the directory layout under `target/release/bundle/deb/<app>_<ver>_amd64/`, writes `DEBIAN/control`, then assembles the `.deb` archive in pure Rust (no external `dpkg-deb`).

**Linux RPM** — Uses the `rpm` crate to build the package in pure Rust if `rpmbuild` isn't available.

**Linux AppImage** — Downloads `appimagetool`, builds the AppDir, runs `appimagetool` to produce the squashfs-based AppImage.

**Updater** — Bundles the platform's primary artifact format into a `.tar.gz` (Linux/macOS) or `.zip` (Windows), then writes a `.sig` file using the configured signing key.

### 6.3 Bundle output paths

```text
src-tauri/target/release/bundle/
├── deb/<app>_<version>_<arch>.deb
├── rpm/<app>-<version>.<arch>.rpm
├── appimage/<app>_<version>_<arch>.AppImage
├── macos/<app>.app
├── dmg/<app>_<version>_<arch>.dmg
├── nsis/<app>_<version>_<arch>-setup.exe
├── msi/<app>_<version>_<arch>.msi
├── updater/<app>.app.tar.gz   (and .sig)
└── kmp/<app>.xcframework      (mobile plugin packaging)
```

### 6.4 Signing matrix

| Bundle | Signs with | Env var(s) |
|--------|-----------|-----------|
| macOS `.app` | `codesign` + Developer ID cert | `APPLE_ID`, `APPLE_PASSWORD`, `APPLE_TEAM_ID` |
| macOS `.dmg` | `codesign` + Developer ID cert | same |
| Windows `.exe`/`.msi` | `signtool.exe` + cert from cert store | `certificateThumbprint` in config |
| Linux `.deb`/`.rpm`/`.AppImage` | (none by default — repo signing happens elsewhere) | - |
| Updater `.tar.gz`/`.zip` | Tauri's own Ed25519 keypair | `TAURI_SIGNING_PRIVATE_KEY`, `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` |

---

## 7. The Updater

### 7.1 Architecture

```text
[ Your app starts ]
       │
       v
[ JS calls check() ]
       │
       v
[ tauri-plugin-updater fetches manifest URL ]
       │
       v
[ Manifest has { version, signature, url } per platform/arch ]
       │  (version > current)
       v
[ Plugin downloads update artifact ]
       │
       v
[ Plugin verifies signature against embedded pubkey ]
       │  (verified)
       v
[ Plugin extracts and replaces app contents ]
       │
       v
[ Plugin restarts app ]
```

### 7.2 Manifest URL templating

The endpoint URL supports placeholders:

| Placeholder | Replaced with |
|-------------|---------------|
| `{{target}}` | `darwin-aarch64`, `darwin-x86_64`, `windows-x86_64`, `linux-x86_64`, etc. |
| `{{current_version}}` | The app's current version. |
| `{{arch}}` | `aarch64`, `x86_64`, etc. |

Example:
```
https://releases.example.com/{{target}}/{{current_version}}/manifest.json
```

### 7.3 Manifest schema

```json
{
  "version": "1.2.0",
  "notes": "Release notes...",
  "pub_date": "2024-01-15T12:00:00Z",
  "platforms": {
    "darwin-aarch64": {
      "signature": "<base64 .sig contents>",
      "url": "https://example.com/downloads/MyApp-1.2.0-aarch64.app.tar.gz"
    },
    "darwin-x86_64": { ... },
    "windows-x86_64": {
      "signature": "...",
      "url": "https://example.com/downloads/MyApp-1.2.0-x86_64.msi.zip"
    },
    "linux-x86_64": {
      "signature": "...",
      "url": "https://example.com/downloads/MyApp-1.2.0-amd64.AppImage.tar.gz"
    }
  }
}
```

### 7.4 Signing keys

`tauri signer generate` produces an Ed25519 keypair using the `minisign` format. The private key file is encrypted with a password (or unencrypted if `-p ""`).

The public key is embedded in your binary via `tauri.conf.json > plugins.updater.pubkey`. The plugin uses it to verify every update artifact.

> [!WARNING]
> The signature verification is the **only** integrity check for updates. Network-level HTTPS is not enough — anyone who breaches your update server can serve malicious binaries unless they also have your private key. **Treat the private key like a code-signing certificate.**

### 7.5 Operational best practices

1. Store `TAURI_SIGNING_PRIVATE_KEY` in CI secrets, never in the repo.
2. Use a static-hosting CDN for update artifacts (S3 + CloudFront, R2, etc.).
3. Use immutable filenames per release (don't overwrite).
4. Use a versioned manifest filename (`manifest-1.2.0.json`) and bump `endpoints` carefully — or use a version-aware proxy.
5. Test updates in a staging environment before pushing the manifest.
6. Have a rollback plan: keep the previous manifest pinned to an earlier version so you can revert by removing the new manifest.

---

## 8. Mobile Architecture

### 8.1 Android

Tauri Android apps are an Android **Library** (in the Tauri sense) wrapped in an Android **Application** generated by the CLI. The Rust code compiles to a `.so` (`cdylib` crate type) that the Android app loads via JNI.

```text
Your App (Kotlin)              ← gen/android/app/src/main/java/<id>/MainActivity.kt
       │
       v
TauriActivity (Java)            ← crates/tauri/mobile/android/src/main/java/app/tauri/
       │
       v
JNI ↔ tauri_android (Rust)      ← crates/tauri-runtime-wry compiled with android target
       │
       v
WryActivity → WebView
```

The CLI uses [cargo-mobile2](https://github.com/tauri-apps/cargo-mobile2) (a fork of cargo-mobile) under the hood to manage the Gradle project lifecycle.

### 8.2 iOS

iOS apps are a Swift app embedding Rust code as a static library (`staticlib` crate type).

```text
Your App (Swift)                ← gen/apple/<id>/<id>App.swift
       │
       v
Tauri (Swift)                   ← crates/tauri/mobile/ios-api/Sources/Tauri/
       │
       v
Static lib link → tauri (Rust)
       │
       v
WKWebView in UIWindow
```

### 8.3 Mobile-aware plugins

Plugins that need native code on mobile ship Kotlin (Android) and Swift (iOS) modules in `android/` and `ios/` subdirectories. The `Plugin` Kotlin class extends `app.tauri.plugin.Plugin`; the Swift class implements the iOS plugin protocol. The Rust side uses `swift-rs` and JNI to call into them.

### 8.4 Mobile build artifacts

| Target | Output |
|--------|--------|
| `tauri android build` (default) | `.apk` + `.aab` in `gen/android/app/build/outputs/`. |
| `tauri android build --apk` | `.apk` only. |
| `tauri android build --aab` | `.aab` only (for Play Store). |
| `tauri ios build` | `.ipa` in `gen/apple/build/`. |
| `tauri ios build --export-method app-store` | App Store-ready `.ipa`. |
| `tauri ios build --export-method ad-hoc` | Ad-hoc distribution `.ipa`. |
| `tauri ios build --export-method development` | Development `.ipa`. |

---

## 9. Security Analysis

### 9.1 Threat model

Tauri's threat model assumes:

- The **OS** is trusted (kernel, webview engine).
- The **Rust binary** is trusted (you wrote it; it was code-signed by you).
- The **frontend code** is **untrusted** in the sense that it could be modified by a compromised dependency or runtime exploit.
- The **network** is hostile (MITM, DNS spoofing).
- The **filesystem outside the app's data dir** is hostile.

The defenses:

1. **Default-deny IPC** via the ACL — a compromised frontend can only call commands explicitly granted to it.
2. **Custom protocol**, not localhost HTTP — no port for other apps to scan, no CORS confusion.
3. **CSP** restricting external resource loads.
4. **Isolation pattern** for sensitive apps — every IPC payload passes through an app-controlled iframe before reaching Rust.
5. **Updater signature verification** — only releases signed by the developer are accepted.
6. **Code-signed bundles** — Gatekeeper / SmartScreen / packagekit verify provenance.

### 9.2 Known unsafes and gotchas

- **`dangerousDisableAssetCspModification: true`** disables Tauri's automatic nonce injection. Only set if you serve a pre-built CSP and know what you're doing.
- **`security.dangerousUseHttpScheme`** uses plain HTTP for the IPC protocol; only allowed for legacy migration.
- **`shell:allow-execute`** with a wildcard `args` regex is a remote code execution waiting to happen if any window can reach it.
- **`http`** plugin without scope = anything the network can reach is reachable from JS. Always scope.
- **`fs`** plugin's `BaseDirectory.Home` (`$HOME/**`) is broader than most apps need. Prefer `BaseDirectory.AppData` or `BaseDirectory.AppConfig`.
- The **isolation pattern** is opt-in and has a small runtime cost (iframe round-trip per IPC). Worth it for security-critical apps.

### 9.3 Reporting vulnerabilities

Per `SECURITY.md`: use **GitHub Private Vulnerability Disclosure**. Do **not** open a public issue, PR, Discord message, or forum post. The Tauri team aims for 90-day coordinated disclosure. Versions `> 1.0` are supported with security patches; `< 1.0` is not.

### 9.4 Active RUSTSEC advisories

As of commit `df3fb97`, the issue tracker has multiple open RUSTSEC advisories tracking transitive dependencies (issues [#15243](https://github.com/tauri-apps/tauri/issues/15243), [#15244](https://github.com/tauri-apps/tauri/issues/15244), [#15245](https://github.com/tauri-apps/tauri/issues/15245), [#15246](https://github.com/tauri-apps/tauri/issues/15246), [#15247](https://github.com/tauri-apps/tauri/issues/15247), [#15248](https://github.com/tauri-apps/tauri/issues/15248), [#15249](https://github.com/tauri-apps/tauri/issues/15249) — name-constraint validation in TLS certificate handling, and a license-file-naming compliance issue). Operators should:

1. Subscribe to the [Tauri release feed](https://github.com/tauri-apps/tauri/releases.atom).
2. Run `cargo audit` in CI on every build.
3. Pin to known-good Tauri minor versions; bump on patch releases promptly.
4. Use `cargo-vet` (Tauri's own choice — see `supply-chain/` and `audits/`) for supply-chain audit metadata.

### 9.5 The supply-chain audit ledger

The repository ships its own `supply-chain/` directory with `cargo-vet` audits. Every direct dependency has been reviewed by a Tauri maintainer and signed off in `audits/`. If you fork Tauri and add dependencies, follow the same process — `cargo vet check` will fail in CI otherwise.

---

## 10. Operations and Maintenance

### 10.1 Running the dev pipeline

The `tauri dev` lifecycle is:

```text
1. Resolve dirs (find tauri.conf.json by walking up from cwd)
2. Load config (base + platform override + --config args)
3. Run beforeDevCommand
4. Wait for devUrl to respond (unless --no-dev-server-wait)
5. Build the Rust binary (cargo build, debug profile)
6. Spawn the binary with TAURI_DEV=true
7. Watch Rust files
8. On change: SIGTERM the binary, rebuild, respawn
```

Logs are colored via `env_logger` with custom action labels (`Compiling`, `Bundling`, etc.). Verbosity controlled by `-v`.

### 10.2 Stopping cleanly in CI

`tauri dev` traps SIGTERM and SIGINT. For containerized CI, use a process supervisor that propagates signals (e.g. `dumb-init`):

```dockerfile
FROM rust:1.77-bullseye
RUN apt-get install -y libwebkit2gtk-4.1-dev
ENTRYPOINT ["dumb-init", "--"]
CMD ["pnpm", "tauri", "build"]
```

### 10.3 Build caching

Cargo cache is the biggest win. In CI:

```yaml
# GitHub Actions example
- uses: actions/cache@v4
  with:
    path: |
      ~/.cargo/bin/
      ~/.cargo/registry/index/
      ~/.cargo/registry/cache/
      ~/.cargo/git/db/
      src-tauri/target/
    key: ${{ runner.os }}-cargo-${{ hashFiles('src-tauri/Cargo.lock') }}
```

Hot incremental builds drop from 2–5 min to 10–30 sec.

> [!TIP]
> Cache `~/.cargo/registry/` and `src-tauri/target/` separately in CI. The registry cache is shared across all Rust projects; the target cache is project-specific. A warm target cache with `incremental = true` (dev profile only) cuts rebuild time to seconds.

### 10.4 Logs and diagnostics

| Source | How to capture |
|--------|---------------|
| Rust app logs | Use `tauri-plugin-log` with file + stdout sinks. |
| CLI logs | `RUST_LOG=tauri_cli=debug` plus `-vv`. |
| Webview console | DevTools or `tauri-plugin-log`'s webview sink. |
| Bundler errors | `RUST_LOG=tauri_bundler=debug`. |
| WRY/TAO errors | `RUST_LOG=wry=debug,tao=debug`. |

For production telemetry, integrate a crash reporter via the `tauri-plugin-sentry` community plugin or roll your own using `std::panic::set_hook`.

> [!NOTE]
> The release profile sets `panic = "abort"`, which means panics terminate the process immediately — `catch_unwind` will not work. If you need panic recovery for specific subsystems, run them in a separate thread with `panic = "unwind"` configured via a custom profile.

### 10.5 Backup and config drift

`tauri.conf.json` is committed to your repo. The `Cargo.lock` should be too — pinning Tauri's dependency tree avoids surprise upgrades. The `pnpm-lock.yaml` (or `package-lock.json`, `yarn.lock`) similarly pins JS deps.

Capability files in `src-tauri/capabilities/` are the security configuration — review them in PR like any other code.

### 10.6 Performance tuning

- Set `bundle.removeUnusedCommands = true` to strip unused IPC commands and shrink binaries.
- Disable Cargo features you don't need: `tauri = { version = "2", default-features = false, features = ["wry"] }` if you don't need x11 or dbus.
- Use `release-size-optimized` profile for size-critical builds; switch to default `release` for speed-critical.
- For frontend, use Vite/Rollup tree-shaking and code-splitting. Webview asset size matters because everything is embedded.
- Enable `compression` feature (default) for smaller bundle sizes.

### 10.7 Upgrade procedure

```sh
# 1. Read the Tauri release notes.
# 2. Upgrade the CLI:
cargo install tauri-cli --version "^2.X.Y" --locked --force
# or
pnpm add -D @tauri-apps/cli@^2.X.Y

# 3. Upgrade the Rust crates:
cd src-tauri && cargo upgrade --workspace tauri tauri-build tauri-plugin

# 4. Upgrade the JS API:
pnpm add @tauri-apps/api@^2.X.Y

# 5. Upgrade plugins:
pnpm add @tauri-apps/plugin-fs@^2.X.Y @tauri-apps/plugin-http@^2.X.Y ...

# 6. Run `tauri info` and confirm versions match across Rust and JS.
# 7. Rebuild and smoke-test.
```

---

## 11. Internal Modules — Deep Dive

This section indexes every important module in `crates/tauri/src/` for contributors and code-spelunkers.

### 11.1 `app/`

| File | Purpose |
|------|---------|
| `app.rs` | `App`, `AppHandle`, `Builder`, lifecycle. The fan-out hub. |
| `plugin.rs` | The plugin event registration in the builder. |

The `App::run()` blocking call drives the event loop on the main thread. `AppHandle` is the cheap `Send + Sync` clone you pass into commands. `Builder` is the public API surface for everything: `.plugin()`, `.invoke_handler()`, `.manage()`, `.menu()`, `.system_tray()`, `.setup()`, `.on_window_event()`, etc.

### 11.2 `manager/`

The internal manager owns the runtime state: window list, webview list, plugin list, IPC authority, event listeners, resource table. It's the dispatch hub for everything that isn't a direct plugin call.

### 11.3 `ipc/`

Already covered in §2.4. Key point: `RuntimeAuthority::resolve_access` is the choke point for ACL enforcement.

### 11.4 `webview/` and `window/`

`Window` is the OS window (TAO `Window`). `Webview` is the embedded webview (WRY `WebView`). `WebviewWindow` is the convenience type combining both for the common 1:1 case. The split exists for multi-webview windows (split-pane apps).

### 11.5 `event/`

Pub/sub. The `Listener` trait is implemented by `App`, `AppHandle`, `Window`, `Webview`. Events are routed through the manager based on the `EventTarget` (`Any`, `App`, specific window/webview/label).

### 11.6 `path/`

Cross-platform path resolution. `BaseDirectory::AppData` resolves to:

| Platform | Path |
|----------|------|
| Linux | `$XDG_DATA_HOME/<identifier>` or `$HOME/.local/share/<identifier>` |
| macOS | `$HOME/Library/Application Support/<identifier>` |
| Windows | `{FOLDERID_RoamingAppData}\<identifier>` |
| Android | `/data/data/<identifier>/files` |
| iOS | `<App Sandbox>/Library/Application Support/<identifier>` |

### 11.7 `protocol/`

Custom URI handlers. Tauri ships:

- `tauri://localhost` — main asset protocol
- `asset://localhost/<path>` — filesystem assets (gated by `protocol-asset` feature + `assetProtocol` config)
- `ipc://localhost` — IPC fallback protocol on some platforms

You can register your own via `Builder::register_uri_scheme_protocol`.

### 11.8 `pattern/`

Implements the brownfield (default) and isolation patterns. The brownfield pattern just injects the IPC bootstrap script. The isolation pattern wraps every IPC payload in a `postMessage` round-trip through an app-controlled iframe.

### 11.9 `plugin/`

The plugin runtime — distinct from `tauri-plugin` (the SDK). This is the loader, lifecycle dispatcher, and registry that the manager consults during IPC routing.

### 11.10 `state/` and `resources/`

`State<T>` is a command parameter extractor for shared state managed via `Builder::manage(value)`. `ResourceTable` is the typed handle table — plugins use it to give JS opaque IDs that map to Rust resources (file handles, network sockets, etc.) without exposing internals.

### 11.11 `scope/`

Defines `FsScope`, `ShellScope`, etc. These types implement the scope evaluation logic that ACL permissions use. Each plugin that supports scoping ships its scope type here.

### 11.12 `process/`

`current_binary()`, `restart()`, `exit()`. The restart logic spawns a new copy of the binary with the same args and exits the current one. On macOS it uses `NSWorkspace`; on Linux `execv`; on Windows `CreateProcess`.

### 11.13 `tray/` (feature `tray-icon`)

System tray icon and menu. Cross-platform via TAO. macOS uses `NSStatusItem`; Windows uses `Shell_NotifyIcon`; Linux uses `ayatana-appindicator3` (with X11) or the new XDG StatusNotifier protocol.

### 11.14 `vibrancy/`

macOS-only blur effect for transparent windows. Wraps `NSVisualEffectView`.

### 11.15 `image/`

`Image` type used by tray icons, menu icon items, and notifications. Decoders gated by `image-ico`, `image-png` features for size.

### 11.16 `async_runtime/`

Tokio runtime accessor. Plugins that need async dispatch get a handle here. The runtime is shared across the whole app.

### 11.17 `error.rs`

The crate's `Error` enum and `Result` alias. Used by every other module's public API.

---

## 12. Code Patterns and Conventions

### 12.1 Error handling

Every module returns `crate::Result<T>` (`Result<T, Error>`). Errors use `thiserror::Error` for derivation and propagate via `?`. The CLI wraps errors with context using a custom `Context` trait in `crates/tauri-cli/src/error.rs` (similar to `anyhow::Context`).

### 12.2 Cross-platform `cfg(...)` discipline

Tauri uses the standard Rust `#[cfg(target_os = "...")]` and `#[cfg(any(...))]` attributes plus custom cfgs emitted by `tauri-build`:

| cfg | Set when |
|-----|----------|
| `desktop` | `target_os = "macos"` or `"linux"` or `"windows"` |
| `mobile` | `target_os = "ios"` or `"android"` |

These are emitted from `tauri-build` and used throughout core to gate menu/tray APIs (desktop only) and JNI bridges (mobile only).

### 12.3 Builder pattern everywhere

`Builder` types appear all over: `tauri::Builder`, `WindowBuilder`, `WebviewBuilder`, `WebviewWindowBuilder`, `MenuBuilder`, `SubmenuBuilder`, `MenuItemBuilder`, `TrayIconBuilder`, `tauri_plugin::Builder`. The convention is `.field(value)` chaining ending in `.build()` or `.run()`.

### 12.4 Macro-driven codegen

Three macros define the user-facing magic:

- `#[tauri::command]` — wraps a regular Rust fn into an `Invoke` handler
- `tauri::generate_handler![fn1, fn2, ...]` — produces the dispatch table
- `tauri::generate_context!()` — embeds the config + assets + icons at compile time

All three live in `tauri-macros` and call into `tauri-codegen`.

### 12.5 Re-exports

`tauri` re-exports types from its dependencies for ergonomic access:

```rust
pub use http;        // entire http crate
pub use url::Url;
pub use tauri_macros::{command, generate_handler, include_image};
pub use tauri_utils as utils;
```

User code often reaches for `tauri::http::Method` or `tauri::Url` rather than depending on those crates directly.

### 12.6 Feature flag layering

`tauri` has 25+ Cargo features. Conventions:

- Defaults are conservative for desktop (you opt out, not in).
- "Allowlist" features (now mostly removed — replaced by ACL) used to gate built-in commands.
- Features like `tray-icon`, `macos-private-api`, `isolation` are auto-enabled by `tauri-build` if the corresponding config option is set.

### 12.7 Workspace crate naming

All Tauri-published crates start with `tauri-` (`tauri-build`, `tauri-runtime`, `tauri-bundler`, etc.). The exception is the bare `tauri` crate. Plugins live in a separate workspace (`tauri-apps/plugins-workspace`) and are named `tauri-plugin-<name>`.

---

## 13. Testing

### 13.1 Test categories

| Category | Location | Run with |
|----------|----------|----------|
| Unit | inline `#[cfg(test)]` modules | `cargo test --workspace` |
| Integration | `crates/tests/` | `cargo test -p tauri-tests-restart` etc. |
| ACL fixtures | `crates/tests/acl/fixtures/` | `cargo test -p tauri-tests-acl` |
| Bundler snapshots | `crates/tauri-cli/tests/fixtures/` | `cargo test -p tauri-cli` |
| App-updater E2E | `crates/tests/app-updater/` | `cargo test -p tauri-tests-app-updater` |
| JS / TS unit | `packages/api/__tests__/` | `pnpm test` |
| CLI Node tests | `packages/cli/__tests__/` | `pnpm --filter @tauri-apps/cli test` |
| WebDriver E2E | (use `tauri-driver`) | external Selenium/WebDriver client |

### 13.2 The `test` feature

Set `tauri = { version = "2", features = ["test"] }` as a dev-dependency to access:

- `tauri::test::mock_app()` — build a fake `App` for unit tests
- `tauri::test::MockRuntime` — runs without a real webview
- `tauri::test::mock_invoke_handler` — assert command dispatch

```rust
#[cfg(test)]
mod tests {
    use tauri::test::{mock_app, mock_builder};

    #[test]
    fn greet_works() {
        let app = mock_builder()
            .invoke_handler(tauri::generate_handler![crate::greet])
            .build(mock_context(noop_assets()))
            .unwrap();
        // ...
    }
}
```

### 13.3 Bench

`bench/` contains performance tests for cold-start time, memory, IPC throughput, and bundle size. Run with `cargo bench --workspace`.

---

## 14. Migrating from v1 to v2 — Operator Checklist

### 14.1 Pre-migration

- [ ] Tag the repo at the v1 head (`git tag v1-final`).
- [ ] Read the official [v1 → v2 migration guide](https://v2.tauri.app/start/migrate/from-tauri-1/).
- [ ] Confirm Rust toolchain is **1.77.2 or newer**.
- [ ] Audit your plugin list: are any third-party plugins not yet ported to v2?
- [ ] Decide which mobile targets (if any) to add — they're a v2-exclusive feature.

### 14.2 Run the migration

- [ ] `cargo install tauri-cli --version "^2.0.0" --locked`
- [ ] `cargo tauri migrate` from the project root.
- [ ] Review the diff. Pay special attention to `Cargo.toml`, `package.json`, `tauri.conf.json`, and every source file that imports from `@tauri-apps/api/...`.

### 14.3 Manual fixups

- [ ] Replace every old-style allowlist with an explicit capability file. Run `tauri permission ls` and confirm every command your app calls has a matching permission granted.
- [ ] Replace `appWindow` with `getCurrentWindow()` in JS.
- [ ] Update plugin imports: `@tauri-apps/api/dialog` → `@tauri-apps/plugin-dialog`, etc.
- [ ] Add `tauri-plugin-*` Rust dependencies for every plugin via `tauri add <name>`.
- [ ] If using the updater, generate a new keypair (the v1 keypair format is incompatible).
- [ ] Update CI configuration: `tauri build` is unchanged but the env var requirements may have changed.
- [ ] Run `cargo update` and `pnpm install --frozen-lockfile=false` to refresh lockfiles.

### 14.4 Test matrix

- [ ] `tauri dev` works on every platform you target.
- [ ] `tauri build` produces signed bundles on every platform.
- [ ] Updater can fetch and apply an update from a staging server.
- [ ] Every IPC command in the app fires successfully (no ACL rejections in the console).
- [ ] No deprecated API calls in the JS console.

### 14.5 Operational rollout

- [ ] Ship a "migration release" to internal users first.
- [ ] Watch crash reports for the first week.
- [ ] Ship to public after one week of clean telemetry.

> [!WARNING]
> The v1 → v2 migration is the **largest breaking change** in Tauri's history. Even with `tauri migrate`, expect 1–3 days of cleanup work for a non-trivial app.

---

## 15. Contributing

### 15.1 Where to send what

| What you want to do | Where it goes |
|---------------------|---------------|
| Bug in `tauri` core | [tauri-apps/tauri](https://github.com/tauri-apps/tauri) issues |
| Bug in a plugin (`fs`, `http`, etc.) | [tauri-apps/plugins-workspace](https://github.com/tauri-apps/plugins-workspace) issues |
| Bug in the docs site | [tauri-apps/tauri-docs](https://github.com/tauri-apps/tauri-docs) issues |
| Bug in `create-tauri-app` | [tauri-apps/create-tauri-app](https://github.com/tauri-apps/create-tauri-app) issues |
| Bug in WRY / TAO | [tauri-apps/wry](https://github.com/tauri-apps/wry) / [tauri-apps/tao](https://github.com/tauri-apps/tao) |
| Security vulnerability | GitHub Private Vulnerability Disclosure |
| Feature discussion | [Discord](https://discord.com/invite/tauri) `#feature-requests` |

### 15.2 Contribution flow

1. Open an issue first (or comment on an existing one) so the team can confirm the approach.
2. Fork, branch from `dev`.
3. Run `cargo fmt && cargo clippy --workspace --all-features -- -D warnings`.
4. Add changeset via `pnpm changeset` (the repo uses [Changesets](https://github.com/changesets/changesets) for versioning).
5. Open a PR against `dev`.
6. CI runs `cargo test --workspace`, `pnpm test`, `pnpm run build`, and the supply-chain audit.
7. A maintainer reviews, requests changes if needed, and merges.

### 15.3 Maintainer notes

- The `dev` branch is the integration target.
- Releases are cut from `release/v2.X` branches via the Changesets bot.
- Versioning follows SemVer; breaking changes wait for a major bump.
- Patch releases ship within a week of identification for security issues.
- The project follows the Programme governance of The Commons Conservancy (see `governance/` if present in your fork).

---

## 16. Improvements and Tech Debt

Read in conjunction with the open issue tracker. Specifics observed during this review:

- **Dependency RUSTSEC drift** — multiple open advisories ([#15243](https://github.com/tauri-apps/tauri/issues/15243)–[#15249](https://github.com/tauri-apps/tauri/issues/15249)) need either dependency bumps or `[patch.crates-io]` entries. The supply-chain story is excellent (cargo-vet is in use), but the cadence of advisory triage could be tightened.

- **The `#2486` Windows Defender false-positive** — a five-year-old open issue that is largely outside Tauri's control, but the workaround docs (sign with EV cert) deserve more visibility in the install flow rather than only being mentioned in the FAQ.

- **`tauri-bundler` external tool downloads** — NSIS, WiX, appimagetool are downloaded on first use. This is a supply-chain risk if the upstream URLs are compromised. Consider distributing checksummed binaries via the Tauri release channel itself.

- **Schema regeneration friction** — `src-tauri/gen/schemas/` is regenerated at compile time but is .gitignored. This means devs without a working build can't get IDE completions on capability files. Consider committing a snapshot or adding a `tauri capability schema` CLI command.

- **macOS notarization parallelism** — `tauri build` notarizes serially per format. Building both DMG and APP triggers two notarization runs. Could be parallelized.

- **Mobile DX** — `tauri android init` will install SDK + NDK on prompt, but the prompt fires in the middle of a long-running build. Consider an explicit `tauri doctor` command that prepares the environment up-front.

- **The `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE`** — useful for users behind GitHub-blocking firewalls, but the format is not well-documented in either the CLI help or `tauri.conf.json`. A small docs improvement.

- **Unified plugin permission discovery** — currently each plugin's permissions are loaded by `tauri-build` from convention. A `tauri permission discover` command that lists all available permissions across installed plugins would help users without opening every plugin's source.

- **Updater rollback** — there's no built-in "downgrade" path. If a release goes wrong, users have to reinstall the previous version manually. A `--target-version` flag for `update.downloadAndInstall()` would help.

- **No explicit `tauri doctor`** — `tauri info` is close but doesn't run any active checks. A doctor command that validates all toolchains, signing keys, and plugin compatibility could catch bad-state environments early.

---

## 17. Troubleshooting

### 17.1 Build failures

**`error[E0433]: failed to resolve: use of undeclared crate or module` after upgrading Tauri**

Run `cargo update` to refresh the lockfile. Tauri minor releases often bump internal crate versions, and a stale `Cargo.lock` will attempt to resolve old versions.

**`Library not found for -lWkWebView` on macOS**

The macOS SDK is missing or Xcode CLI tools are not installed. Run `xcode-select --install` and ensure Xcode.app is fully downloaded (not just the CLI tools stub).

**`cannot find -lwebkit2gtk-4.1` on Linux**

Install `libwebkit2gtk-4.1-dev` (Debian/Ubuntu) or `webkit2gtk4.1-devel` (Fedora). Tauri v2 requires the 4.1 API; the old `webkit2gtk-4.0` package from v1 will not work.

> [!WARNING]
> On Ubuntu 22.04, `libwebkit2gtk-4.1-dev` is available but may not match the runtime version on the target system. If your build host runs a newer WebKitGTK than your target, the app may crash at launch with symbol errors. Pin the build-host WebKitGTK version or build inside a container matching the target distro.

**`error: failed to run custom build command for tauri` with `TAURI_CONFIG` set**

The `TAURI_CONFIG` environment variable must contain valid JSON. A common mistake is setting it to a file path instead of inline JSON. Use `--config path/to/override.json` for file-based overrides.

### 17.2 Runtime errors

**"runtime already initialized"**

This panic fires if `tauri::Builder::build()` or `.run()` is called more than once. The Tokio runtime is global — you cannot build two Tauri apps in the same process. In tests, use `tauri::test::mock_app()` which shares the runtime.

**"state not found for type `MyState`"**

You called `app.state::<MyState>()` or used `State<MyState>` in a command, but never registered it with `.manage(MyState::new())` on the Builder. Every `State<T>` extractor requires a corresponding `.manage()` call.

**"command not allowed by ACL"**

The IPC call was rejected by `RuntimeAuthority`. The window making the call does not have a capability granting the required permission. Check `src-tauri/capabilities/*.json` and ensure the command's permission (e.g., `fs:allow-read-text-file`) is listed under a capability that targets the calling window's label.

> [!CAUTION]
> Do not "fix" ACL rejections by granting `"*"` to all windows. This disables the security model entirely. Instead, create narrowly-scoped capabilities per window label, granting only the specific permissions each window needs.

### 17.3 Bundler failures

**Windows NSIS: "makensis: command not found"**

The NSIS tools download failed or the cache was corrupted. Delete `~/.cache/tauri/` (or `%LOCALAPPDATA%\tauri\` on Windows) and rebuild. The CLI will re-download NSIS.

**macOS notarization: "The signature of the binary is invalid"**

The signing identity in `bundle.macOS.signingIdentity` does not match an installed certificate. Run `security find-identity -v -p codesigning` to list available identities. For CI, ensure the certificate is imported into the keychain before the build step.

**AppImage: "FUSE is not installed"**

AppImage requires FUSE to run. Install `fuse` or `libfuse2` on the target system. Alternatively, extract with `--appimage-extract` for FUSE-less environments.

### 17.4 Mobile build failures

**Android: "SDK location not found"**

Set `ANDROID_HOME` to your Android SDK directory (e.g., `~/Android/Sdk` or `%LOCALAPPDATA%\Android\Sdk`). The CLI also accepts `ANDROID_SDK_ROOT`.

**iOS: "No signing certificate found"**

Run `tauri ios build` with `--export-method development` for local testing. Production builds require a valid Apple Developer certificate configured in Xcode.

### 17.5 Performance issues

**Large binary size (>20 MB)**

Check that the release profile uses `opt-level = "s"` or `"z"`, `lto = true`, `strip = true`, and `codegen-units = 1`. Disable unused Cargo features (e.g., `image-ico`, `image-png` if you don't use programmatic images). Enable the `compression` feature (default on) for embedded asset compression.

**Slow cold start on Windows**

WebView2 initialization on first launch can take 1–3 seconds while the runtime initializes. Subsequent launches are faster. Use `visible: false` in the window config and show the window in `setup()` after initialization to avoid a blank window flash.

---

## 18. Quick Reference

### 18.1 Filesystem layout of a built app

| File | Where |
|------|-------|
| `src-tauri/Cargo.toml` | Rust dependencies. |
| `src-tauri/Cargo.lock` | Locked Rust versions. **Commit it.** |
| `src-tauri/tauri.conf.json` | App configuration. |
| `src-tauri/build.rs` | Build script. |
| `src-tauri/src/main.rs` | Binary entry point. |
| `src-tauri/src/lib.rs` | App library. |
| `src-tauri/icons/` | Generated icons. |
| `src-tauri/capabilities/*.json` | ACL capabilities. |
| `src-tauri/gen/` | Generated code (mobile, schemas). **gitignored**. |
| `src-tauri/target/` | Cargo build output. **gitignored**. |
| `src-tauri/target/release/bundle/<format>/` | Built bundles. |

### 18.2 Important env vars

(See User Guide §21.3 for the full list.)

### 18.3 Important config keys

- `identifier` — never change after release
- `version` — drives bundling and updater
- `build.frontendDist` / `build.devUrl` — wire the frontend
- `app.security.csp` — CSP
- `app.security.pattern.use` — `brownfield` or `isolation`
- `bundle.targets` — restrict formats
- `bundle.createUpdaterArtifacts` — produce updater files
- `plugins.updater.endpoints` — updater manifest URL
- `plugins.updater.pubkey` — updater public key

### 18.4 Important commands cheat sheet

| Command | When to use |
|---------|-------------|
| `tauri dev` | Daily development. |
| `tauri build` | Every release. |
| `tauri info` | First step of every bug report. |
| `tauri add <plugin>` | Adding new functionality. |
| `tauri permission ls` | Auditing what your app can do. |
| `tauri signer generate` | Once, when setting up the updater. |
| `tauri migrate` | One time, when upgrading from v1. |
| `tauri icon` | When you change branding. |
| `tauri inspect wix-upgrade-code` | When debugging Windows MSI upgrades. |

### 18.5 External resources

- Source: <https://github.com/tauri-apps/tauri>
- WRY (webview): <https://github.com/tauri-apps/wry>
- TAO (windowing): <https://github.com/tauri-apps/tao>
- Plugins workspace: <https://github.com/tauri-apps/plugins-workspace>
- Docs site: <https://v2.tauri.app/>
- Awesome list: <https://github.com/tauri-apps/awesome-tauri>
- Discord: <https://discord.com/invite/tauri>
- Funding: <https://opencollective.com/tauri>
- Schema reference: <https://schema.tauri.app/config/v2>

---

_Generated May 2026 from tauri-apps/tauri at df3fb97. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
