Home

mweinbach / agent-coworker

publicmweinbach/agent-coworker
Overview Code History Branches Pull requestsIssuesInsights
main
HomeOverview Code PRsIssues

feat(desktop): overhaul Rust backend - async I/O, security, graceful shutdown

9 months ago

7ad2863
Authored
mweinbach2/10/2026, 12:47:18 AM
Phase 1 of the desktop audit: comprehensive Rust backend improvements.

**Threading & Async (Findings 1.1-1.3, 2.1-2.4, 3.1-3.3):**
- Replace all synchronous std::fs operations (read_to_string, write,
  remove_file, OpenOptions) with tokio::fs async equivalents to avoid
  blocking the Tauri async runtime's worker threads
- Switch server startup from blocking std::sync::mpsc + recv_timeout
  to tokio::sync::oneshot + tokio::time::timeout for non-blocking wait
- Increase server startup timeout from 6s to 15s for slower machines
- Add new `append_transcript_batch` command that groups events by
  thread_id and writes them in a single file open/write cycle,
  reducing IPC overhead for rapid transcript events

**Concurrency & Safety (Findings 4.1-4.3, 5.1-5.2):**
- Replace std::sync::Mutex with parking_lot::Mutex for ServerManager
  (no poisoning, faster uncontended lock, never held across .await)
- Add StateLock using tokio::sync::Mutex for state file read/write
  serialization (safe to hold across .await points unlike parking_lot)
- Atomic state writes: write to .json.tmp then rename to .json to
  prevent corruption if the app crashes mid-write
- Add structured AppError enum with thiserror derives instead of
  ad-hoc String::from error mapping throughout

**Process Management (Findings 2.1, 2.3, 2.4):**
- Add graceful_kill(): sends SIGTERM first on Unix (via libc), polls
  try_wait() for up to 3 seconds, then falls back to SIGKILL. Prevents
  orphaned bun server processes on restart/quit
- Stderr drain thread now properly terminates when pipe closes (process
  exits), preventing leaked threads

**Security Hardening (Findings 6.1-6.3):**
- Add validate_safe_id() that rejects empty, oversized (>256 chars),
  or path-traversal characters in thread_id and workspace_id inputs.
  Only alphanumeric, hyphens, and underscores are allowed
- Add validate_workspace_path() that verifies the path exists and is
  a directory before spawning a server process
- All transcript commands now validate thread_id before constructing
  file paths, preventing directory traversal attacks

**Frontend binding:**
- Add appendTranscriptBatch() to tauriCommands.ts to expose the new
  batched transcript API to the React frontend

**Dependencies added:**
- thiserror 2: structured error derives
- parking_lot 0.12: fast, non-poisoning mutex
- tokio 1 (sync, time, fs, io-util, process, rt): async runtime
- tracing 0.1: structured logging (warn on canonicalize fallback)
- libc 0.2 (unix only): SIGTERM for graceful process shutdown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Parent5bab7dd

4 files changed
  • apps/desktop/src-tauri/Cargo.lock+6−0
  • apps/desktop/src-tauri/Cargo.toml+7−0
  • apps/desktop/src-tauri/src/lib.rs+341−64
  • apps/desktop/src/lib/tauriCommands.ts+9−0