Local-First Desktop Tool

This walkthrough traces a small Tauri app that stores local data and exports files.

Flow

User clicks "Save"
  -> React form validates obvious UI errors
  -> `saveRecord()` frontend wrapper calls `invoke`
  -> Rust command validates payload and app state
  -> Rust writes through SQLite/store/filesystem layer
  -> Command returns serializable response
  -> UI updates local view

Files

src/records/RecordForm.tsx
src/tauri-api.ts
src-tauri/src/commands.rs
src-tauri/src/state.rs
src-tauri/capabilities/default.json
src-tauri/tauri.conf.json

Native Boundary

The frontend can ask to save a record. It cannot choose arbitrary filesystem paths, spawn processes, or write directly to app data.

Rust owns:

  • Payload validation beyond UI convenience checks.
  • App data directory selection.
  • Database or store migration rules.
  • Export path validation.
  • Error messages safe for the UI.

Capability Review

Start with core:default. Add filesystem, SQL, store, dialog, or opener permissions only if the implementation actually uses official plugins for those operations.

If the app exports files, prefer a Rust command that receives a user-approved path from a dialog flow rather than a renderer-controlled arbitrary path.

Test Walkthrough

frontend test: form calls wrapper with expected payload
mockIPC test: wrapper uses expected command and handles errors
Rust test: invalid payload rejected
Rust test: export path validation rejects traversal or empty path
release smoke: save, restart, load, export

Release Walkthrough

build release artifact
launch without dev server
create record
restart app
verify record persists
export file
inspect logs
record signing and WebView2 state