Desktop Client
The Diminuendo desktop client (@igentai/dim-desktop) is a native application built on Tauri v2, and it exists for a reason the web client cannot satisfy: the WebSocket connection belongs in a process that does not pause when a tab loses focus, does not contend with JavaScript’s single-threaded event loop for I/O scheduling, and has direct access to the local filesystem for Chronicle workspace synchronization. The Tauri Rust backend holds the WebSocket via tokio-tungstenite, runs a dedicated tokio task for event ingestion, and exposes the connection to the React frontend through IPC — the same React frontend, the same Zustand stores, the same 70+ components. Only the transport layer differs.
Tech Stack
| Technology | Version | Role |
|---|---|---|
| Tauri | v2 (@tauri-apps/cli ^2.2) | Native desktop shell with Rust backend |
| @tauri-apps/api | ^2.2 | Frontend IPC bindings (invoke, listen) |
| React | 19 | Component model and rendering |
| TypeScript | 5.x | Compile-time type safety |
| Tailwind CSS | v4 | Utility-first styling |
| Effect | ^3.12 | Typed effects for the Gateway Adapter abstraction |
| Zustand | 5 | Selector-based state management |
| Diminuendo Rust SDK | local | WebSocket client in the Rust backend process |
Architecture
The desktop client inverts the web client’s connection topology. Where the browser holds the WebSocket directly, the desktop client delegates that responsibility to a Rust process that operates outside the webview’s lifecycle. The React frontend communicates with this Rust backend through Tauri’s IPC mechanism:invoke() for commands (frontend-to-backend), listen() for events (backend-to-frontend).
tokio task that reads from the Rust SDK’s mpsc::UnboundedReceiver<ServerEvent> and emits each event to the frontend via Tauri’s event system. The frontend’s TauriGatewayAdapter listens for these events and forwards them into the shared Zustand stores — the same stores that the web client uses, consuming the same GatewayEvent type through the same GatewayAdapter interface.
The TauriGatewayAdapter
TheTauriGatewayAdapter implements the same GatewayAdapter interface as the web client’s WebGatewayAdapter, but bridges to the Rust backend using Tauri’s IPC primitives rather than wrapping the TypeScript SDK:
- Commands are sent via
invoke()from@tauri-apps/api/core. Eachinvokecall maps to a#[tauri::command]function in the Rust backend that calls the corresponding method on the Rust SDK’sDiminuendoClient. - Events are received via
listen()from@tauri-apps/api/event. The Rust backend’s event handler emitsgateway-eventTauri events, which the adapter consumes and forwards to the Effect stream.
The Rust backend holds the
DiminuendoClient in Tauri’s managed state, typically wrapped in Arc<Mutex<...>>. Each #[tauri::command] handler retrieves the client from state and calls the corresponding SDK method. The event receiver task calls app_handle.emit("gateway-event", &event) to serialize and deliver each event to the webview.Data Flow
Command Path (Frontend to Gateway)
User Action
The user submits a message through the chat input. The form handler calls
sendMessage() from the useChat() hook.Adapter Dispatch
useChat() calls adapter.runTurn() on the TauriGatewayAdapter, which calls invoke("run_turn", { sessionId, text }).Tauri IPC
Tauri’s IPC bridge serializes the arguments as JSON and delivers them across the process boundary to the Rust backend.
Rust Command
The
#[tauri::command] run_turn handler retrieves the DiminuendoClient from Tauri’s managed state and calls client.run_turn().Event Path (Gateway to Frontend)
Gateway Event
The gateway publishes an event to the session topic. The event traverses the WebSocket to the Rust backend.
Rust SDK Reception
The Rust SDK’s background
tokio task receives the event from the WebSocket, deserializes it via serde into a ServerEvent enum variant, and sends it through the mpsc channel.Event Emission
The event receiver task calls
app_handle.emit("gateway-event", &event), which serializes the event and delivers it to the frontend webview via Tauri’s event system.Adapter Stream
The
TauriGatewayAdapter’s listen("gateway-event") callback receives the event and pushes it into the Effect Stream.Stream.Advantages over the Web Client
Lower Latency
The Rust backend holds the WebSocket connection natively via
tokio-tungstenite. There is no browser WebSocket implementation overhead, no JavaScript event loop contention for I/O processing, and no garbage collection pauses during high-throughput event streams. The tokio runtime schedules WebSocket reads on a dedicated async task, decoupled from the webview’s rendering cadence.Native Window Management
Full control over window chrome, title bar, resizing behavior, and multi-window support via Tauri’s window management API. The application can implement custom title bars, frameless windows, and platform-specific window behaviors that the browser sandbox prohibits.
System Tray
Background presence via system tray icon with notification badges. The application can run minimized to the tray and surface notifications when agent turns complete or questions are requested — the connection remains alive because it lives in the Rust process, not in a tab that the user might close.
Local File Access
Direct filesystem access for Chronicle local-sync integration. The desktop client can synchronize agent workspace files to local directories, enabling users to open and edit files in their preferred IDE while the agent is actively working. This is architecturally impossible in a browser.
Chronicle Local-Sync Integration
The desktop client integrates with Chronicle’s local-sync mode, which replaces FUSE or NFS-based filesystem abstractions with real APFS files synchronized bidirectionally via platform-native FSEvents. Agent workspace files appear as ordinary files in Finder and in editors like VS Code — no kernel extension, no mount point, no virtual filesystem.Tauri IPC Commands
Four IPC commands provide the local-sync interface from the React frontend:| Command | Description |
|---|---|
start_sync | Begin synchronizing a session’s workspace to a local directory |
stop_sync | Stop synchronization for a session |
sync_status | Query the current sync state (active, paused, error) |
resolve_conflict | Resolve a file conflict when both local and remote changes exist |
Sync Architecture
Local-sync consists of three cooperating components running in the Rust backend:LocalMaterializer
Writes files from the upstream replication stream to the local filesystem. Implements echo suppression to prevent its own writes from triggering the watcher and creating infinite sync loops.
FsWatcher
Monitors the local directory for changes using
notify::RecommendedWatcher (FSEvents on macOS). Changes are debounced via notify-debouncer-full and filtered to exclude editor temp files, .git directories, and other noise.WriteJournal
Tracks all changes — both local and upstream — in a journal for replication. Provides the mechanism for conflict detection when both the user and the agent modify the same file within the same debounce window.
LocalMaterializer writes a file, the FsWatcher will detect that write as a local change. Without suppression, this would create an infinite sync loop. The materializer registers each write in a short-lived “echo set,” and the watcher ignores change events for paths currently in that set.
Conflict resolution surfaces to the user when both the agent (upstream) and the user (local) modify the same file within the debounce window. The desktop client presents options to keep the local version, accept the upstream version, or merge manually.
Chronicle local-sync uses the
notify crate (v7) with RecommendedWatcher for FSEvents on macOS. The local-sync Cargo feature flag must be enabled at build time. This is the default for desktop development builds.Development
Start the desktop client in development mode:local-sync feature enabled by default in development.
Build for Production
- macOS:
.dmgand.appbundle - Windows:
.msiinstaller and.exe - Linux:
.deb,.rpm, and.AppImage
Prerequisites
- Rust toolchain (stable)
- Tauri CLI:
cargo install tauri-cli - Platform-specific dependencies (see Tauri prerequisites)
- A running Diminuendo gateway instance for development