---
name: tauri-expert
description: "Expert guide for Tauri 2.x / 2.12+ cross-platform desktop and mobile development: Rust 1.90+ MSRV, IPC Channel streaming, on_permission_request handlers, App-Bound domains, and Capabilities security / Panduan ahli pengembangan desktop dan mobile Tauri 2.x / 2.12+."
author: "Roedy Rustam"
version: "4.4.2"
---
# Tauri Expert (Tauri 2.12+ Desktop & Mobile Architecture)
[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)
---
## English
### Orchestration & Integration
Connects and orchestrates with relevant domain skills:
- `rust-programming-expert` — Writing memory-safe, asynchronous Rust commands, Tokio tasks, and state backends.
- `senior-frontend` — Building lightweight React 19 / Vite UI frontends for Tauri.
- `design-system-architect` — Desktop UI design systems, custom window titlebars, and native OS theming.
- `ui-ux-pro-max` — Desktop window ergonomics, system tray menus, and native keyboard shortcuts.
- `visual-ui-auditor` — Multimodal vision auditing of rendered desktop window frames and dialogs.
- `anti-slop` — Anti-slop desktop UI craft, zero fake browser window frames and zero placeholder commands.
- `production-ready-hardener` — Auditing binary sizes, code signing, and release pipeline hardening.
- `desktop-electron-expert` & `browser-extension-mv3-pro` — Migrating legacy Electron apps to Tauri v2 and bridging native desktop binaries with Manifest V3 browser companion extensions.
### Description
Production-grade guidance for building cross-platform desktop (Windows 10/11, macOS, Linux) and mobile (iOS 16+, Android 10+) applications using **Tauri 2.x (specifically v2.12+)**. Covers Rust 1.90+ MSRV requirements, **`tauri::ipc::Channel`** for ordered bidirectional streaming, **`on_permission_request`** programmatic security gates, **App-Bound Domain navigation limiting**, portable application directory overrides (`appDirectoriesOverride`), Rust-side `convert_file_src`, and the fine-grained **Capabilities & Permissions** system.
### Instructions & Best Practices
#### 1. Architecture & Streaming IPC (`tauri::ipc::Channel`)
- Use `#[tauri::command]` for discrete request-response commands.
- For streaming long-running jobs (AI token generation, file download progress, database indexing), use **`tauri::ipc::Channel`** passed as a command argument:
- Channels deliver messages strictly in order directly to the caller.
- Keeps the global event bus (`emit` / `listen`) uncluttered and avoids global memory leaks.
- For raw binary streams without JSON serialization overhead, send `tauri::ipc::InvokeResponseBody::Raw(Vec)`.
#### 2. Native Webview Security Gates (Tauri 2.12+)
- **Permission Request Handler (`on_permission_request`)**: Intercept native webview permission requests (Camera, Microphone, Geolocation, Notifications) in Rust using `WebviewWindowBuilder::on_permission_request` or `WebviewBuilder::on_permission_request`. Evaluate app state before granting or denying.
- **App-Bound Domains**: Lock webview navigation strictly to trusted external domains using `limit_navigations_to_app_bound_domains` in Rust or `limitNavigationsToAppBoundDomains` in `tauri.conf.json`.
- **Context Isolation & Capabilities**: Never expose unrestricted system APIs. Define least-privilege capability files in `src-tauri/capabilities/*.json`.
#### 3. State Management & Concurrency
- Store global application state in the Rust backend using `tauri::State`.
- Use `tokio::sync::Mutex` or `std::sync::RwLock` for mutable state.
- Initialize managed state during application setup: `tauri::Builder::default().manage(AppState::new())`.
#### 4. Portable Application Support (`appDirectoriesOverride`)
- In Tauri 2.12+, configure `app > appDirectoriesOverride` in `tauri.conf.json` or mutate `Context::config_mut` at runtime to direct app data, configs, and cache relative to the executable (`$EXE_DIR`). This enables portable zero-installer distribution.
- Use `app_handle.path()` resolver APIs instead of hardcoded paths across platforms.
#### 5. Plugin Ecosystem & Native Integration
- Rely on official Tauri plugins (`@tauri-apps/plugin-fs`, `@tauri-apps/plugin-store`, `@tauri-apps/plugin-dialog`, `@tauri-apps/plugin-notification`).
- For app exit, use the core-level `exit` from `@tauri-apps/api/app` backed by permission `core:app:allow-exit` without requiring third-party process plugins.
### Trigger Conditions
Activate this skill when:
- Building, debugging, or refactoring desktop or mobile applications with Tauri v2.
- Implementing streaming IPC pipelines with `tauri::ipc::Channel`.
- Configuring webview permissions, App-Bound domains, or capabilities (`src-tauri/capabilities/`).
- Setting up portable application directories or cross-platform builds.
---
### Implementation Examples
#### 1. Rust Backend Command & Streaming Handler (`src-tauri/src/lib.rs`)
```rust
use serde::{Deserialize, Serialize};
use tauri::{
ipc::Channel,
Manager, WebviewUrl, WebviewWindowBuilder,
};
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProgressPayload {
pub step: u32,
pub total: u32,
pub message: String,
}
#[derive(Serialize, Deserialize)]
pub struct MetricsPayload {
pub cpu_usage: f32,
pub memory_used_mb: u64,
}
#[tauri::command]
pub async fn fetch_system_metrics() -> Result {
Ok(MetricsPayload {
cpu_usage: 12.4,
memory_used_mb: 2048,
})
}
// Tauri v2.12+ Streaming IPC Channel pattern
#[tauri::command]
pub async fn start_streaming_task(
task_name: String,
on_progress: Channel,
) -> Result {
let total_steps = 5;
for step in 1..=total_steps {
tokio::time::sleep(tokio::time::Duration::from_millis(150)).await;
let payload = ProgressPayload {
step,
total: total_steps,
message: format!("Processing {} (step {}/{})", task_name, step, total_steps),
};
on_progress.send(payload).map_err(|err| err.to_string())?;
}
Ok(format!("Task '{}' completed successfully", task_name))
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
// Tauri 2.12+ Window creation with Permission Request Handler
let main_window = WebviewWindowBuilder::new(
app,
"main",
WebviewUrl::App("index.html".into()),
)
.title("Sovereign Tauri Desktop")
.inner_size(1024.0, 768.0)
.on_permission_request(|_webview, request| {
println!("Webview requested permission: {:?}", request.permission());
tauri::webview::PermissionState::Granted
})
.build()?;
#[cfg(debug_assertions)]
main_window.open_devtools();
Ok(())
})
.invoke_handler(tauri::generate_handler![
fetch_system_metrics,
start_streaming_task
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
#### 2. Frontend TypeScript Invocation with Channel (`src/lib/streaming.ts`)
```typescript
import { Channel, invoke } from '@tauri-apps/api/core';
export interface ProgressPayload {
step: number;
total: number;
message: string;
}
export interface MetricsPayload {
cpu_usage: number;
memory_used_mb: number;
}
export async function getSystemMetrics(): Promise {
return await invoke('fetch_system_metrics');
}
// Tauri v2.12+ Typed Channel streaming consumption
export async function runStreamingTask(
taskName: string,
onProgressUpdate: (progress: ProgressPayload) => void
): Promise {
const channel = new Channel();
channel.onmessage = (payload) => {
onProgressUpdate(payload);
};
return await invoke('start_streaming_task', {
taskName,
onProgress: channel,
});
}
```
#### 3. Tauri v2 Granular Capability Definition (`src-tauri/capabilities/default.json`)
```json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Default capability envelope for main application window",
"windows": ["main"],
"permissions": [
"core:default",
"core:app:allow-exit",
"core:window:allow-set-title",
"core:window:allow-set-fullscreen"
]
}
```
### Sovereign Execution, Token Efficiency & Invariants
| Dimension | Sovereign Mandate & Deterministic Contract |
| :--- | :--- |
| **Frontier Model Cascade** | Medium / High (`GPT-5.6 Sol Medium`, `Gemini 4 Argon`, `Claude Sonnet 5.5 High`, `Grok 4.7 High Fast`) |
| **Availability Fallback** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Zero unhandled missing-model errors) |
| **KV-Cache & Token Protocol** | Pin static instructions & schemas at prompt prefix (`kv-cache-prefix-optimizer`); pass surgical `CONTEXT_MAP.md` slices (<250 tokens/call) during Swarm Fan-Out |
| **SaaT CLI & MCP Dispatch** | CLI: `vibes skill exec tauri-expert ` / MCP: `vibes_skill_dispatch`, `vibes_ast_query`, `vibes_compile_cache`, `vibes_optimize_budget`, `vibes_verify_gate` |
| **NFR & Anti-Slop Invariants** | P99 API Gateway Latency < 85ms, OpenAPI 3.1 / TypeSpec strict schema parity, RFC 9457 Problem Details error contracts, Idempotency-Key enforcement; 100% complete zero-placeholder output verified via `node scripts/check-anti-slop.js --strict` |
---
---
## Bahasa Indonesia
### Integrasi Orkestrasi
Terhubung dan mengorkestrasi skill domain yang relevan:
- `rust-programming-expert` — Penulisan perintah Rust asinkron yang aman memori, task Tokio, dan backend state.
- `senior-frontend` — Pembuatan frontend UI React 19 / Vite yang ringan untuk Tauri.
- `design-system-architect` — Sistem desain UI desktop, titlebar jendela kustom, dan theming OS native.
- `ui-ux-pro-max` — Ergonomi antarmuka desktop, menu system tray, dan pintasan keyboard native.
- `visual-ui-auditor` — Audit visual multimodal untuk bingkai jendela desktop dan dialog yang dirender.
- `anti-slop` — Penegakan UI desktop anti-slop, nol bingkai browser palsu, dan nol perintah placeholder.
- `production-ready-hardener` — Audit ukuran biner, code signing, dan pipeline rilis siap produksi.
### Deskripsi
Panduan produksi untuk membangun aplikasi desktop (Windows 10/11, macOS, Linux) dan mobile (iOS 16+, Android 10+) berperforma tinggi menggunakan **Tauri 2.x (terutama v2.12+)**. Mencakup standar MSRV Rust 1.90+, **`tauri::ipc::Channel`** untuk streaming bidirectional terurut, **`on_permission_request`** untuk gerbang keamanan native, pembatasan navigasi **App-Bound Domains**, override direktori aplikasi portabel (`appDirectoriesOverride`), konversi file lokal di sisi Rust via `convert_file_src`, serta sistem **Capabilities & Permissions**.
### Instruksi & Praktik Terbaik
#### 1. Arsitektur & Streaming IPC (`tauri::ipc::Channel`)
- Gunakan `#[tauri::command]` untuk pola permintaan-respons standar (*request-response*).
- Untuk tugas jangka panjang yang membutuhkan streaming progres (seperti unduhan file, inferensi AI token per token, atau indeks database), gunakan **`tauri::ipc::Channel`** yang dilewatkan sebagai parameter command:
- Channel menjamin urutan pesan langsung ke pemanggil spesifik.
- Menghindari penumpukan listener pada event bus global (`emit` / `listen`) yang rawan kebocoran memori.
- Untuk streaming data biner mentah tanpa overhead serialisasi JSON, gunakan `tauri::ipc::InvokeResponseBody::Raw(Vec)`.
#### 2. Gerbang Keamanan Webview Native (Tauri 2.12+)
- **Permission Request Handler (`on_permission_request`)**: Tangani permintaan izin webview native (Kamera, Mikrofon, Lokasi, Notifikasi) langsung di Rust menggunakan `WebviewWindowBuilder::on_permission_request`. Evaluasi status aplikasi sebelum memberikan izin (`Granted`) atau menolak (`Denied`).
- **App-Bound Domains**: Kunci navigasi webview agar hanya mengakses domain tepercaya melalui `limit_navigations_to_app_bound_domains` di Rust atau `limitNavigationsToAppBoundDomains` di `tauri.conf.json`.
- **Isolasi Konteks & Capabilities**: Jangan pernah mengekspos API sistem tanpa batas. Tentukan kapabilitas berbasis hak istimewa terkecil (*least-privilege*) di `src-tauri/capabilities/*.json`.
#### 3. Manajemen State & Konkurensi
- Simpan state global aplikasi di backend Rust menggunakan `tauri::State`.
- Gunakan `tokio::sync::Mutex` atau `std::sync::RwLock` untuk state yang dapat berubah (*mutable state*).
- Daftarkan state saat inisialisasi aplikasi: `tauri::Builder::default().manage(AppState::new())`.
#### 4. Aplikasi Portabel & Override Direktori (`appDirectoriesOverride`)
- Pada Tauri 2.12+, gunakan konfigurasi `app > appDirectoriesOverride` di `tauri.conf.json` atau ubah `Context::config_mut` saat runtime agar data aplikasi, konfigurasi, dan cache disimpan relatif terhadap direktori biner eksekusi (`$EXE_DIR`). Pola ini memungkinkan distribusi aplikasi portabel tanpa installer.
- Gunakan resolver `app_handle.path()` alih-alih jalur sistem operasi yang ditulis statis (*hardcoded*).
#### 5. Ekosistem Plugin & Integrasi Native
- Manfaatkan plugin resmi Tauri (`@tauri-apps/plugin-fs`, `@tauri-apps/plugin-store`, `@tauri-apps/plugin-dialog`, `@tauri-apps/plugin-notification`).
- Untuk keluar dari aplikasi, gunakan fungsi bawaan `exit` dari `@tauri-apps/api/app` yang didukung oleh izin `core:app:allow-exit` tanpa perlu plugin pihak ketiga.
### Kondisi Pemicu
Aktif saat:
- Membangun, men-debug, atau merefaktor aplikasi desktop atau mobile dengan Tauri v2.
- Mengimplementasikan pipeline streaming IPC dengan `tauri::ipc::Channel`.
- Mengonfigurasi perizinan webview, App-Bound domains, atau file kapabilitas (`src-tauri/capabilities/`).
- Menyiapkan direktori aplikasi portabel atau kompilasi lintas platform.
---
### Contoh Implementasi
#### 1. Definisi Command & Streaming Handler Rust (`src-tauri/src/lib.rs`)
```rust
use serde::{Deserialize, Serialize};
use tauri::{
ipc::Channel,
Manager, WebviewUrl, WebviewWindowBuilder,
};
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProgressPayload {
pub step: u32,
pub total: u32,
pub message: String,
}
#[derive(Serialize, Deserialize)]
pub struct MetricsPayload {
pub cpu_usage: f32,
pub memory_used_mb: u64,
}
#[tauri::command]
pub async fn fetch_system_metrics() -> Result {
Ok(MetricsPayload {
cpu_usage: 12.4,
memory_used_mb: 2048,
})
}
// Pola Streaming IPC Channel Tauri v2.12+
#[tauri::command]
pub async fn start_streaming_task(
task_name: String,
on_progress: Channel,
) -> Result {
let total_steps = 5;
for step in 1..=total_steps {
tokio::time::sleep(tokio::time::Duration::from_millis(150)).await;
let payload = ProgressPayload {
step,
total: total_steps,
message: format!("Memproses {} (tahap {}/{})", task_name, step, total_steps),
};
on_progress.send(payload).map_err(|err| err.to_string())?;
}
Ok(format!("Tugas '{}' selesai dengan sukses", task_name))
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
// Inisialisasi jendela Tauri 2.12+ dengan Permission Request Handler
let main_window = WebviewWindowBuilder::new(
app,
"main",
WebviewUrl::App("index.html".into()),
)
.title("Sovereign Tauri Desktop")
.inner_size(1024.0, 768.0)
.on_permission_request(|_webview, request| {
println!("Izin webview diminta: {:?}", request.permission());
tauri::webview::PermissionState::Granted
})
.build()?;
#[cfg(debug_assertions)]
main_window.open_devtools();
Ok(())
})
.invoke_handler(tauri::generate_handler![
fetch_system_metrics,
start_streaming_task
])
.run(tauri::generate_context!())
.expect("terjadi kesalahan saat menjalankan aplikasi tauri");
}
```
#### 2. Pemanggilan Frontend TypeScript dengan Channel (`src/lib/streaming.ts`)
```typescript
import { Channel, invoke } from '@tauri-apps/api/core';
export interface ProgressPayload {
step: number;
total: number;
message: string;
}
export interface MetricsPayload {
cpu_usage: number;
memory_used_mb: number;
}
export async function getSystemMetrics(): Promise {
return await invoke('fetch_system_metrics');
}
// Konsumsi streaming Channel terketik pada Tauri v2.12+
export async function runStreamingTask(
taskName: string,
onProgressUpdate: (progress: ProgressPayload) => void
): Promise {
const channel = new Channel();
channel.onmessage = (payload) => {
onProgressUpdate(payload);
};
return await invoke('start_streaming_task', {
taskName,
onProgress: channel,
});
}
```
#### 3. Definisi Kapabilitas Rinci Tauri v2 (`src-tauri/capabilities/default.json`)
```json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Ambang kapabilitas default untuk jendela aplikasi utama",
"windows": ["main"],
"permissions": [
"core:default",
"core:app:allow-exit",
"core:window:allow-set-title",
"core:window:allow-set-fullscreen"
]
}
```
### Matriks Eksekusi Berdaulat, Efisiensi Token & Invarian
| Dimensi | Mandat Berdaulat & Kontrak Deterministik |
| :--- | :--- |
| **Kaskade Model Frontier** | Medium / High (`GPT-5.6 Sol Medium`, `Gemini 4 Argon`, `Claude Sonnet 5.5 High`, `Grok 4.7 High Fast`) |
| **Invarian Fallback Ketersediaan** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Bebas gagal saat model belum tersedia/kuota penuh) |
| **Protokol Hemat Token & KV-Cache** | Kunci instruksi & skema statis di prefix awal (`kv-cache-prefix-optimizer`); kirim potongan `CONTEXT_MAP.md` (<250 token/panggilan) saat Fan-Out |
| **Eksekusi SaaT CLI & MCP** | CLI: `vibes skill exec tauri-expert ` / MCP: `vibes_skill_dispatch`, `vibes_ast_query`, `vibes_compile_cache`, `vibes_optimize_budget`, `vibes_verify_gate` |
| **Invarian NFR & Anti-Slop** | Latensi P99 API < 85ms, paritas skema ketat OpenAPI 3.1 / TypeSpec, kontrak error RFC 9457 Problem Details, penegakan Idempotency-Key; kode 100% tuntas tanpa placeholder diverifikasi melalui `node scripts/check-anti-slop.js --strict` |