# Tauri 2 + React + Vite (Rust Desktop App) — AI Agent Guidelines & Architecture Rules

> Desktop app rules for Tauri 2: React + Vite + TypeScript UI, typed Rust commands, least-privilege capabilities, SQLite in Rust, updater and signed CI builds.
> Technologies: Tauri, Rust, React, Vite, TypeScript, SQLite, sqlx, Tokio

---

## AGENTS.md
```markdown
# Project Architecture & Guidelines (Tauri 2 + React + Vite)

## 1. System Architecture
- **Shell**: Tauri 2 (system WebView, Rust core). Desktop targets: macOS, Windows, Linux.
- **UI**: React + Vite + TypeScript (strict) in `src/`; TanStack Query for calling Rust commands; Tailwind CSS.
- **Core**: Rust 2024 edition in `src-tauri/`. All file system, network, database and OS work happens here, never in the WebView.
- **Data**: local SQLite owned by Rust (`sqlx` with the `sqlite` and `migrate` features) in the app data directory (`app.path().app_data_dir()`).
- **Updates**: `tauri-plugin-updater` with signed release artifacts.

## 2. Project Layout
- `src-tauri/src/lib.rs`: `run()` builds the app: plugins, `.manage(state)`, `invoke_handler(tauri::generate_handler![...])`.
- `src-tauri/src/commands/<feature>.rs`: `#[tauri::command]` functions, thin wrappers over `src-tauri/src/core/`.
- `src-tauri/src/core/`: plain Rust domain logic with unit tests; no Tauri types.
- `src-tauri/src/error.rs`: `AppError` (`thiserror`) with a `serde::Serialize` impl so commands can return `Result<T, AppError>`.
- `src-tauri/capabilities/*.json`: per-window permission sets.
- `src/lib/ipc.ts`: the only file that calls `invoke`; exports typed functions per command.

## 3. Commands and IPC
- Commands are `async` when they do I/O and return `Result<T, AppError>`; arguments and results derive `Serialize`/`Deserialize` with `#[serde(rename_all = "camelCase")]`.
- Shared state goes through `app.manage(...)` and `State<'_, T>`; wrap mutable state in `tokio::sync::Mutex` or `RwLock`, never `static mut`.
- Long-running work streams progress with `tauri::ipc::Channel<T>` or `app.emit(...)`; never block the main thread.
- Keep TypeScript types in sync with Rust: generate bindings (for example with `tauri-specta`) or keep `src/lib/ipc.ts` as the single hand-written mirror reviewed with each command change.
- The frontend never builds file paths or SQL; it calls a command with intent-level arguments.

## 4. Security (least privilege)
- Every window gets an explicit capability file listing only the permissions it uses (`core:window:default`, `updater:default`, ...). No wildcard permissions.
- Keep the default CSP in `tauri.conf.json` strict; load no remote scripts.
- Validate every command argument in Rust; treat the WebView as untrusted input.
- Secrets live in the OS keychain (via a keyring plugin or crate), not in local storage or SQLite.

## 5. Agent Loop (run after every change)
1. `cargo check --manifest-path src-tauri/Cargo.toml`.
2. `cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings`.
3. `cargo test --manifest-path src-tauri/Cargo.toml`.
4. `pnpm typecheck && pnpm test` for the UI.
5. When adding a command: register it in `generate_handler!`, add its permission to the capability file, add the typed wrapper in `ipc.ts`. Missing any of the three is the most common Tauri bug.
- No `unwrap()`/`expect()` in commands; a panic in a command kills the app.

## 6. Testing
- Unit-test `core/` as plain Rust. Command wrappers stay thin enough not to need their own tests.
- Mock `invoke` in Vitest (`@tauri-apps/api/mocks` `mockIPC`) for UI tests.
- Smoke-test the packaged app on each OS in CI before a release.

## 7. Build and Release
- `pnpm tauri build` per target in a GitHub Actions matrix (`tauri-apps/tauri-action`).
- Sign and notarise (Apple), sign (Windows), and sign updater artifacts with the Tauri signing key stored as a CI secret.
- Bump the version in `tauri.conf.json` and `Cargo.toml` together.
```

---

## CLAUDE.md
```markdown
# CLAUDE.md — Tauri 2 + React + Vite

@AGENTS.md

## Commands
- `pnpm tauri dev` - run the app with hot reload
- `cargo check --manifest-path src-tauri/Cargo.toml` - fast Rust feedback
- `cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings`
- `cargo test --manifest-path src-tauri/Cargo.toml`
- `pnpm typecheck`, `pnpm test` - UI checks
- `pnpm tauri build` - production bundle for the current OS

## Non-negotiables
- New command = handler registration + capability permission + typed wrapper in `src/lib/ipc.ts`.
- File, network and database access only in Rust.
- No wildcard permissions; no `unwrap()` in commands.
```

---

## .cursor/rules/stack.mdc
```markdown
---
description: Tauri 2 + React + Vite desktop app rules
globs: ["src-tauri/**/*.rs", "src-tauri/capabilities/*.json", "src/**/*.{ts,tsx}"]
alwaysApply: false
---

# Tauri 2 + React + Vite

- All OS, file, network and SQLite work in Rust commands under `src-tauri/src/commands/`; domain logic in `core/`.
- Commands return `Result<T, AppError>`; `AppError` implements `serde::Serialize`.
- State via `app.manage` + `State<'_, T>`; streaming via `ipc::Channel` or `emit`.
- `src/lib/ipc.ts` is the only caller of `invoke`; keep its types in sync with Rust.
- Least-privilege capability files per window; no wildcards; strict CSP.
- New command: register in `generate_handler!`, add permission, add typed wrapper.
```

---

## Architecture Overview & Best Practices
## Architecture Overview

**A desktop app with a web UI and a Rust core.** Tauri 2 renders a React + Vite frontend in the system WebView and exposes typed Rust commands for everything that touches the operating system, the file system or the local SQLite database. The result is a small, signed, auto-updating binary for macOS, Windows and Linux.

### Why it suits AI coding agents

- **A hard boundary.** The UI asks for intents, Rust does the work. An agent can change either side and the compiler plus TypeScript catch most contract breaks.
- **Security is declarative.** Capability files list exactly which commands each window may call, so an agent adding a feature also has to add the permission, which reviewers see in the diff.
- **Plain Rust core.** Domain logic in `core/` has no Tauri types and is unit-tested like any other crate.

### In the directory

[Hoppscotch](/project/hoppscotch) ships a Tauri desktop app next to its web client, [VoiceStudio](/project/voicestudio) uses Tauri alongside Electron, and [RustDesk](/project/rustdesk) shows how far a Rust core can go on the desktop. For server-side Rust web stacks, see [Building web apps in Rust in 2026](/insights/rust-web-apps-ai-agents-2026).