tauri-expert · diff

v4.4.2 to v4.4.2

294 added, 64 removed. Audit A to A.

---
name: tauri-expert
- description: "Expert skill for Tauri (v2) development, Rust backend, IPC, and security / Panduan ahli untuk pengembangan Tauri v2, Rust backend, IPC, dan keamanan."
+ 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 Expert (Tauri 2.12+ Desktop & Mobile Architecture)
[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)
---
<a name="english"></a>
## English
### Orchestration & Integration
Connects and orchestrates with relevant domain skills:
- - `rust-programming-expert` — Writing memory-safe, asynchronous Rust commands and state backends.
+ - `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.
### Description
- This skill provides best practice guidance for building cross-platform desktop and mobile applications using Tauri (specifically Tauri v2). It covers frontend integration with the Rust backend, Inter-Process Communication (IPC), state management, and security.
+ 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<T>`** 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 & IPC (Inter-Process Communication)
- - Use `tauri::command` to define Rust functions callable from the frontend.
- - Invoke Rust functions from the frontend using `@tauri-apps/api/core` (`invoke`).
- - Utilize Tauri's event system (`emit` from Rust, `listen` on frontend) for real-time or async updates.
- - Keep heavy processing and filesystem access in Rust (backend), while the frontend focuses purely on UI.
-
- #### 2. State Management (Rust Backend)
- - Use `tauri::State` to store and manage global state in the Rust backend.
- - Use `std::sync::Mutex` or `tokio::sync::Mutex` (if async) for mutable state.
- - Initialize managed state during application setup: `tauri::Builder::default().manage(MyState::default())`.
+ #### 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<T>`** 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<u8>)`.
- #### 3. Security & Capabilities
- - Enforce strict context isolation.
- - In Tauri v2, use the **Capabilities** system to restrict API access per window or plugin.
- - Never expose dangerous filesystem, shell, or network APIs globally. Only grant allowlist access to specific, required paths or commands.
+ #### 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`.
- #### 4. Performance & Concurrency
- - Use `async fn` for `tauri::command` when handling heavy I/O to avoid blocking the main thread.
- - Use `serde` (`Serialize`, `Deserialize`) efficiently for data structures sent between Rust and the frontend.
+ #### 3. State Management & Concurrency
+ - Store global application state in the Rust backend using `tauri::State<T>`.
+ - Use `tokio::sync::Mutex` or `std::sync::RwLock` for mutable state.
+ - Initialize managed state during application setup: `tauri::Builder::default().manage(AppState::new())`.
- #### 5. Plugin Management (Tauri v2)
- - Use official Tauri plugins (e.g., `tauri-plugin-fs`, `tauri-plugin-store`, `tauri-plugin-dialog`) instead of writing custom I/O modules when available.
- - Initialize plugins in `main.rs` or `lib.rs`: `app.plugin(tauri_plugin_dialog::init())`.
+ #### 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.
- #### 6. Cross-Platform & Build
- - Avoid hardcoded paths. Use the `app_handle.path()` resolver for AppData, Cache, and other system directories.
- - Ensure dependencies in `Cargo.toml` support cross-compilation (Windows, macOS, Linux, iOS, Android).
+ #### 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
- Active when:
- - Building, debugging, or refactoring Tauri applications.
- - Writing IPC systems between the frontend (React/Svelte/Vue) and the Rust backend.
- - Configuring security (`tauri.conf.json`, capabilities, permissions).
+ 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 Definition (`src-tauri/src/lib.rs`)
+ #### 1. Rust Backend Command & Streaming Handler (`src-tauri/src/lib.rs`)
```rust
- use serde::{Serialize, Deserialize};
+ 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<MetricsPayload, String> {
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<ProgressPayload>,
+ ) -> Result<String, String> {
+ 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()
- .invoke_handler(tauri::generate_handler![fetch_system_metrics])
+ .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 (`src/lib/metrics.ts`)
+ #### 2. Frontend TypeScript Invocation with Channel (`src/lib/streaming.ts`)
```typescript
- import { invoke } from '@tauri-apps/api/core';
+ 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<MetricsPayload> {
return await invoke<MetricsPayload>('fetch_system_metrics');
}
+
+ // Tauri v2.12+ Typed Channel streaming consumption
+ export async function runStreamingTask(
+ taskName: string,
+ onProgressUpdate: (progress: ProgressPayload) => void
+ ): Promise<string> {
+ const channel = new Channel<ProgressPayload>();
+
+ channel.onmessage = (payload) => {
+ onProgressUpdate(payload);
+ };
+
+ return await invoke<string>('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"
+ ]
+ }
+ ```
+
---
<a name="bahasa-indonesia"></a>
## Bahasa Indonesia
### Integrasi Orkestrasi
Terhubung dan mengorkestrasi skill domain yang relevan:
- - `rust-programming-expert` — Penulisan perintah Rust asinkron yang aman memori dan backend state.
+ - `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
- Skill ini memberikan panduan praktik terbaik (best practices) untuk membangun aplikasi desktop dan mobile lintas platform menggunakan Tauri (terutama Tauri v2). Skill ini mencakup integrasi frontend dengan backend Rust, Inter-Process Communication (IPC), manajemen state, serta keamanan.
+ 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<T>`** 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 & IPC (Inter-Process Communication)
- - Gunakan `tauri::command` untuk mendefinisikan fungsi Rust yang bisa dipanggil dari frontend.
- - Panggil fungsi Rust dari frontend menggunakan `@tauri-apps/api/core` (`invoke`).
- - Manfaatkan sistem *event* Tauri (`emit` dari Rust, `listen` di frontend) untuk pembaruan *real-time* atau asinkron dari backend.
- - Lakukan operasi berat di sisi Rust (backend), sedangkan frontend hanya fokus pada UI.
-
- #### 2. Manajemen State (Rust Backend)
- - Gunakan `tauri::State` untuk menyimpan dan mengelola state global di backend Rust.
- - Gunakan `std::sync::Mutex` or `tokio::sync::Mutex` (jika asinkron) untuk state yang bisa berubah (mutable state).
- - Lakukan inisialisasi state saat setup aplikasi: `tauri::Builder::default().manage(MyState::default())`.
+ #### 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<T>`** 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<u8>)`.
- #### 3. Keamanan (Security & Capabilities)
- - Terapkan isolasi konteks secara ketat.
- - Di Tauri v2, gunakan sistem **Capabilities** untuk membatasi akses API per jendela/plugin.
- - Jangan mengekspos API filesystem, shell, atau jaringan yang berbahaya secara global. Hanya beri akses (`allowlist`) ke path atau perintah (commands) yang spesifik dan dibutuhkan.
+ #### 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`.
- #### 4. Kinerja & Asinkronisitas
- - Gunakan `async fn` pada `tauri::command` untuk I/O yang berat agar tidak memblokir *main thread*.
- - Gunakan `serde` (`Serialize`, `Deserialize`) secara efisien untuk mengirim struktur data kompleks antara Rust dan frontend.
+ #### 3. Manajemen State & Konkurensi
+ - Simpan state global aplikasi di backend Rust menggunakan `tauri::State<T>`.
+ - 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())`.
- #### 5. Pengelolaan Plugin (Tauri v2)
- - Gunakan ekosistem plugin Tauri (seperti `tauri-plugin-fs`, `tauri-plugin-store`, `tauri-plugin-dialog`) alih-alih membuat modul I/O sendiri jika sudah tersedia.
- - Inisialisasi plugin di file `main.rs` atau `lib.rs`: `app.plugin(tauri_plugin_dialog::init())`.
+ #### 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*).
- #### 6. Lintas Platform (Cross-Platform) & Build
- - Hindari *hardcode* path sistem operasi. Gunakan `app_handle.path()` resolver untuk direktori AppData, Cache, dsb.
- - Pastikan dependencies di `Cargo.toml` mendukung *cross-compilation* (Windows, macOS, Linux, iOS, Android).
+ #### 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 melakukan *refactoring* pada aplikasi Tauri.
- - Menulis sistem IPC antara frontend (React/Svelte/Vue) dan backend Rust.
- - Mengonfigurasi keamanan (`tauri.conf.json`, capabilities, permission).
+ - 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<MetricsPayload, String> {
+ 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<ProgressPayload>,
+ ) -> Result<String, String> {
+ 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<MetricsPayload> {
+ return await invoke<MetricsPayload>('fetch_system_metrics');
+ }
+
+ // Konsumsi streaming Channel terketik pada Tauri v2.12+
+ export async function runStreamingTask(
+ taskName: string,
+ onProgressUpdate: (progress: ProgressPayload) => void
+ ): Promise<string> {
+ const channel = new Channel<ProgressPayload>();
+
+ channel.onmessage = (payload) => {
+ onProgressUpdate(payload);
+ };
+
+ return await invoke<string>('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"
+ ]
+ }
+ ```