Use Cases (Aktualisiert: 2.6.2026)

Tauri-Desktop-Apps mit Claude Code: Vite, Rust Commands und Berechtigungen

Praxisguide fur Tauri v2 mit Claude Code: Vite, Rust commands, invoke, Dateirechte, Tests und Review-Prompts.

Tauri-Desktop-Apps mit Claude Code: Vite, Rust Commands und Berechtigungen

Tauri macht aus einer Weboberflache eine Desktop-App fur Windows, macOS und Linux. Die UI kann mit React, Svelte oder einem anderen Frontend-Stack gebaut werden, wahrend native Arbeit in Rust landet. Genau diese Trennung passt gut zu Claude Code: Eine Aufgabe kann nur die UI, nur einen Rust command, nur eine capability oder nur einen Test betreffen.

Dieser Artikel nutzt Tauri v2 und eine kleine lokale Notizen-App als Beispiel. Wir behandeln, wann Tauri sinnvoll ist, wie der Start mit Vite + React oder Svelte funktioniert, wie Rust commands geschrieben werden, wie der Frontend-Aufruf mit invoke aussieht, wie Dateisystemberechtigungen eingegrenzt werden und wie Claude Code als Reviewer fur Berechtigungen eingesetzt wird.

Geprufte offizielle Quellen sind Tauri Create a Project, Calling Rust from the Frontend, Tauri Capabilities, File System plugin, Vite Getting Started, Cargo test und Claude Code setup. Fur Rust-Grundlagen passt Rust-Entwicklung mit Claude Code. Fur den Vergleich mit Electron lies Electron Desktop App Development.

Wann Tauri passt

Tauri passt, wenn die App lokale Fahigkeiten braucht, aber die Produktivitat einer Web-UI behalten soll. Gute Beispiele sind private Notizen, ein CSV-Konverter fur interne Dateien, ein Log-Viewer, ein Entwicklerwerkzeug oder eine Offline-App fur Inventur und Checklisten.

Tauri ist nicht automatisch die beste Wahl, nur weil es leichtgewichtig klingt. Du ubernimmst Rust-Toolchain, Bundling, Code Signing, Installer, OS-Unterschiede, App-Identifier und Update-Strategie. Wenn das Produkt im Kern eine Web-App mit Login und Server-APIs ist, ist eine normale Web-App oder PWA oft einfacher.

Ein guter Startprompt fur Claude Code lautet:

Wir bauen eine lokale Notizen-App mit Tauri v2.
Implementiere noch nichts.
Teile die Arbeit in React UI, Rust commands, capabilities und build/test.
Dateizugriff darf nur innerhalb des app data Verzeichnisses stattfinden.
Schlage keine beliebigen Dateipfade oder breiten Dateirechte vor.

Damit wird erst der Sicherheitsrahmen geklart, bevor Code entsteht.

Architekturgrenze

Das Frontend sollte das Betriebssystem nicht direkt beruhren. Es ruft Rust commands mit invoke auf. Rust validiert die Eingabe, arbeitet mit lokalen Ressourcen und gibt ein typisiertes Ergebnis zuruck.

flowchart LR
  UI["React oder Svelte UI"] --> Invoke["invoke from @tauri-apps/api/core"]
  Invoke --> Command["Rust command"]
  Command --> Guard["Pfad-Allowlist und Validierung"]
  Guard --> AppData["app data Verzeichnis"]
  Command --> Result["typisiertes Ergebnis"]
  Result --> UI
  Capability["Tauri capability"] --> UI

Wichtig: Tauri capabilities beschranken den Zugriff des Frontends auf Tauri-APIs und Plugins. Sie machen eigene Rust commands nicht automatisch sicher. Wenn ein Rust command einen beliebigen Pfad akzeptiert und schreibt, bleibt das gefahrlich. Deshalb mussen capabilities und Rust-seitige Pfadprufung gemeinsam reviewed werden.

Start mit React oder Svelte

Fur ein neues Projekt ist create-tauri-app der sauberste Weg. Wahle React oder Svelte und TypeScript.

npm create tauri-app@latest taskdesk
cd taskdesk
npm install
npm run tauri dev

Bei einer bestehenden Vite-App initialisierst du erst das Frontend und danach Tauri. Die Vite-Dokumentation nennt npm create vite@latest; aktuelle Vite-Versionen verlangen Node.js 20.19+ oder 22.12+.

node --version
npm create vite@latest taskdesk -- --template react-ts
cd taskdesk
npm install
npm install -D @tauri-apps/cli@latest
npm install @tauri-apps/api@latest
npx tauri init
npx tauri dev

Fur Svelte wechselst du nur das Template:

npm create vite@latest taskdesk -- --template svelte-ts

Nach der Erstellung sollte Claude Code zuerst lesen:

Lies package.json, tauri.conf.json und src-tauri/src/lib.rs.
Fasse Projektstruktur und Scripts zusammen.
Ander noch keine Dateien.

So nutzt du die aktuelle Template-Struktur statt Annahmen aus alten Artikeln.

Minimale Tauri-Konfiguration

tauri.conf.json verbindet Vite dev server, Produktionsbuild, Fenster und capability.

{
  "$schema": "https://schema.tauri.app/config/2",
  "productName": "TaskDesk",
  "version": "0.1.0",
  "identifier": "com.example.taskdesk",
  "build": {
    "beforeDevCommand": "npm run dev",
    "devUrl": "http://localhost:5173",
    "beforeBuildCommand": "npm run build",
    "frontendDist": "../dist"
  },
  "app": {
    "windows": [
      {
        "title": "TaskDesk",
        "width": 1000,
        "height": 700
      }
    ],
    "security": {
      "capabilities": ["main-capability"]
    }
  },
  "bundle": {
    "active": true,
    "targets": "all"
  }
}

Reviewe immer identifier, devUrl und frontendDist. Diese drei Felder verursachen oft lange Fehlersuchen, wenn Claude Code spater am falschen Ort repariert.

Rust commands

Die Notizen-App braucht Lesen, Schreiben und Auflisten. Das folgende Beispiel zeigt vor allem die Sicherheitsregel: keine absoluten Pfade, kein .., kein Ausbruch aus app data.

// src-tauri/src/note_commands.rs
use serde::Serialize;
use std::{
    fs,
    path::{Component, Path, PathBuf},
};
use tauri::{AppHandle, Manager};

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NoteFile {
    name: String,
    path: String,
    bytes: u64,
    is_dir: bool,
}

fn reject_unsafe_relative(path: &Path) -> Result<(), String> {
    for component in path.components() {
        match component {
            Component::Normal(_) | Component::CurDir => {}
            _ => return Err("use a relative path inside app data".to_string()),
        }
    }
    Ok(())
}

fn app_data_root(app: &AppHandle) -> Result<PathBuf, String> {
    let root = app
        .path()
        .app_data_dir()
        .map_err(|error| format!("failed to get app data dir: {error}"))?;
    fs::create_dir_all(&root).map_err(|error| format!("failed to create app data dir: {error}"))?;
    root.canonicalize()
        .map_err(|error| format!("failed to resolve app data dir: {error}"))
}

fn existing_path(app: &AppHandle, relative: &str) -> Result<PathBuf, String> {
    let root = app_data_root(app)?;
    let requested = Path::new(relative);
    reject_unsafe_relative(requested)?;
    let full = root
        .join(requested)
        .canonicalize()
        .map_err(|error| format!("path does not exist: {error}"))?;

    if !full.starts_with(&root) {
        return Err("path escapes app data".to_string());
    }

    Ok(full)
}

#[tauri::command]
pub fn read_note(app: AppHandle, path: String) -> Result<String, String> {
    let safe_path = existing_path(&app, &path)?;
    fs::read_to_string(safe_path).map_err(|error| format!("failed to read note: {error}"))
}

Registriere den command in lib.rs:

// src-tauri/src/lib.rs
mod note_commands;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            note_commands::read_note
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

Aufruf aus TypeScript

Ein kleiner Wrapper verhindert wiederholte command-Namen in Komponenten.

// src/lib/notesApi.ts
import { invoke } from "@tauri-apps/api/core";

export type NoteFile = {
  name: string;
  path: string;
  bytes: number;
  isDir: boolean;
};

export const notesApi = {
  read(path: string) {
    return invoke<string>("read_note", { path });
  },
  write(path: string, content: string) {
    return invoke<void>("write_note", { path, content });
  },
  list(dir = ".") {
    return invoke<NoteFile[]>("list_notes", { dir });
  },
};

Eine erste React-Komponente kann klein bleiben:

import { useState } from "react";
import { notesApi } from "./lib/notesApi";

export default function App() {
  const [content, setContent] = useState("");
  const [message, setMessage] = useState("Ready");

  async function saveNote() {
    await notesApi.write("daily-note.txt", content);
    setMessage("Saved");
  }

  return (
    <main>
      <textarea value={content} onChange={(event) => setContent(event.target.value)} />
      <button onClick={saveNote}>Save</button>
      <p>{message}</p>
    </main>
  );
}

Berechtigungen und capabilities

Wenn das Frontend das File System plugin direkt nutzt, muss die capability eng bleiben.

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "Main window permissions for TaskDesk.",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    "fs:allow-app-read-recursive",
    "fs:allow-app-write-recursive"
  ]
}

In diesem Beispiel laufen Dateizugriffe uber Rust commands. Es gibt daher keinen Grund, dem Frontend breite Rechte auf Downloads oder Documents zu geben. Nutze Claude Code als Reviewer:

Nur Review, keine Anderungen.
Prufe src-tauri/capabilities und src-tauri/src/note_commands.rs.
Liste alle vom Frontend erreichbaren APIs und Rust commands.
Erklare fur jede API, welche Dateipfade erreichbar sind.
Markiere absolute Pfade, .., breite Wildcards und Schreiben ausserhalb von app data.
Schlage das kleinste passende Permission-Set vor.

Build, Tests und Einsatzfalle

Trenne die Checks:

npm run build
cd src-tauri
cargo test
cd ..
npm run tauri build

npm run build pruft Vite, cargo test pruft Rust, npm run tauri build pruft das Desktop-Bundle. Pfadregeln konnen als kleine Rust-Tests abgesichert werden.

#[cfg(test)]
mod tests {
    use super::reject_unsafe_relative;
    use std::path::Path;

    #[test]
    fn rejects_parent_directory() {
        assert!(reject_unsafe_relative(Path::new("../secret.txt")).is_err());
    }
}

Praktische Einsatzfalle sind lokale Notizen, CSV/Markdown-Konverter, Entwickler-Logviewer und Offline-Tools fur Inventur oder Checklisten. Typische Fehler sind zu breite Dateirechte, Vermischung von capability und Rust-Sicherheit, zu grosse Claude-Code-Aufgaben, Entwicklungswerte in Produktion und Tests auf nur einem Betriebssystem.

CTA und getestetes Ergebnis

Wenn daraus ein Team-Workflow werden soll, nutze ClaudeCodeLab products and templates oder Claude Code training and consultation. Fur Teams mussen CLAUDE.md, Review-Prompts, capability-Regeln und Build-Kommandos zum echten Repository passen.

Im praktischen Durchlauf war die stabilste Reihenfolge: erst Rust-Pfadgrenze festlegen, dann den TypeScript-invoke-Wrapper bauen, danach capabilities reviewen. Mit der UI anzufangen wirkt schneller, erzeugt aber oft Nacharbeit, sobald Speicherort und Berechtigungen klar werden.

#Claude Code #Tauri #Rust #desktop apps #frontend
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.