Desktop application specialist for the Tauri + SolidJS stack — project scaffolding, IPC design, security configuration, build/distribution, and platform integration. Triggers: 'Tauri app', 'SolidJS desktop', 'Tauri invoke', 'tauri.conf.json', 'src-tauri', 'desktop app with SolidJS', 'Tauri commands', 'Tauri allowlist', 'Tauri v2 capabilities'. Do NOT use for web-only SolidJS apps (no Tauri), Electron apps, React Native/Flutter mobile apps, or pure Rust CLI tools without a frontend.
Domain skill for building cross-platform desktop applications using the Tauri framework (Rust backend) with a SolidJS frontend. Covers project structure, IPC contract design, security model configuration, state management across the Rust–JS boundary, build pipelines, and platform-specific distribution (macOS, Windows, Linux).
Use this skill when:
misc-helpersrc-tauri/ exist? Is there a tauri.conf.json (v1) or tauri.conf.json with capabilities (v2)?@tauri-apps/api ^1.x, tauri crate ^1.x) vs v2 (@tauri-apps/api ^2.x, tauri crate ^2.x). The security model, plugin system, and IPC patterns differ significantly.solid-js), bundler (vite with vite-plugin-solid), router (@solidjs/router).my-app/
├── src/ # SolidJS frontend
│ ├── index.tsx # Entry point, render(<App />)
│ ├── App.tsx # Root component with router
│ ├── routes/ # Page components
│ ├── components/ # Shared UI components
│ ├── lib/
│ │ ├── tauri.ts # Typed wrappers around invoke() calls
│ │ └── store.ts # SolidJS stores for app state
│ └── index.html # HTML shell (loaded by WebView)
├── src-tauri/
│ ├── Cargo.toml # Rust dependencies
│ ├── tauri.conf.json # Tauri configuration (window, security, bundle)
│ ├── capabilities/ # (v2) Permission/capability definitions
│ ├── src/
│ │ ├── main.rs # Tauri builder setup
│ │ ├── commands/ # #[tauri::command] handlers
│ │ ├── state.rs # Managed state (app_handle.manage())
│ │ └── lib.rs # Module declarations
│ └── icons/ # App icons for all platforms
├── vite.config.ts # Vite + solid plugin + Tauri dev server config
└── package.json
Tauri Commands (frontend → backend):
#[tauri::command]:
#[tauri::command]
async fn read_file(path: String) -> Result<String, String> {
std::fs::read_to_string(&path).map_err(|e| e.to_string())
}
.invoke_handler(tauri::generate_handler![read_file])const content = await invoke<string>('read_file', { path: '/tmp/data.txt' })src/lib/tauri.ts to centralize invoke calls and provide TypeScript types.Event System (bidirectional):
app_handle.emit_all("event-name", payload) or window-scoped window.emit("event-name", payload)emit('event-name', payload) with app.listen_global("event-name", handler) in Rustlisten<PayloadType>('event-name', (event) => { ... }) — always unlisten on component cleanup.Tauri v1 (allowlist):
tauri.conf.json → tauri.allowlist, enable only the APIs the app needs:
{ "fs": { "scope": ["$APPDATA/*"], "readFile": true, "writeFile": true },
"dialog": { "open": true, "save": true },
"shell": { "open": true } }
tauri.conf.json → tauri.security.csp:
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"Tauri v2 (capabilities):
src-tauri/capabilities/*.json:
{ "identifier": "main-window", "windows": ["main"],
"permissions": ["fs:read", "fs:write", "dialog:open"] }
fs:* or shell:* — always scope to the minimum required.HashRouter from @solidjs/router (not Router) — there is no server to handle history-mode fallback in a desktop WebView.fs API or custom commands. Never use browser fetch('file://...').tauri.conf.json → tauri.menu or programmatically in Rust. Connect menu events to frontend via the event system.createStore/createSignal for UI state. For state that must persist or be shared with Rust, sync through Tauri commands or the event system. Avoid duplicating state across boundaries.npm run tauri dev — starts Vite dev server + Tauri with hot reload.npm run tauri build — produces platform-specific bundles:
.dmg and .app bundle.msi and .exe (NSIS installer).deb, .AppImage, .rpmAPPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORD, APPLE_ID, APPLE_PASSWORD env vars for notarization.TAURI_SIGNING_PRIVATE_KEY for Authenticode signing.tauri.conf.json → tauri.updater with endpoint URL, public key. The updater checks for new versions on app launch or on a timer.invoke() when the frontend needs a response. Use events only for push notifications (backend → frontend) or fire-and-forget.async to avoid blocking the main thread. Only use sync commands for trivial operations (<1ms).invoke() with raw string command names scattered throughout the frontend. Centralize in a typed module.Every response must include the applicable sections:
Project Scaffold — directory structure with file purposes annotated, if scaffolding a new project or adding a major feature.Configuration Files — complete tauri.conf.json snippets (or capability definitions for v2) with security settings annotated.IPC Contract — Rust command definitions paired with their TypeScript invoke wrappers. Include types for both sides.Build Configuration — build commands, signing setup, and updater configuration if distribution is involved.Platform Notes (if relevant) — any platform-specific behavior, workarounds, or testing instructions."all": true in the allowlist (v1) or "*" permissions (v2). This defeats Tauri's security model and gives the WebView access to the full file system, shell, and network.async commands and spawn blocking work with tauri::async_runtime::spawn_blocking.shell:execute when the app only needs shell:open (open URLs in default browser). Review each permission's actual scope.invoke('my_command', { arg: val }) directly in components instead of through typed wrappers. This leads to typo bugs and makes refactoring IPC contracts painful.Router instead of HashRouter. The app will break on refresh because there's no server to handle the fallback.fastapi-patterns — for building backend APIs that a Tauri app might connect tobigquery-skill — for analytics dashboards built as Tauri desktop appsmisc-helper — for quick utility tasks during developmentCargo.toml for the tauri crate version and package.json for @tauri-apps/api version. v1 and v2 have incompatible APIs — confirm before writing code.webkit2gtk on Linux), (3) Vite dev server configuration matches tauri.conf.json → build.devPath.generate_handler![] and the argument names match exactly between Rust and TypeScript (Tauri uses snake_case → camelCase conversion).