The app uses MCP Server Tauri to let AI assistants (Claude Code, Cursor) control this app: take screenshots, click buttons, and read front-end logs.
The MCP bridge requires withGlobalTauri: true which exposes window.__TAURI__ to the frontend. This would be a huge
security risk in production (untrusted JS could access system APIs, not good), so we enable it only in development:
- Compile-time exclusion: The MCP plugin is only registered via
#[cfg(debug_assertions)]inlib.rs - Config separation:
"withGlobalTauri": falseintauri.conf.json(production), only overridden viatauri.dev.jsonduring dev - Wrapper script:
apps/desktop/scripts/tauri-wrapper.jsinjects-c src-tauri/tauri.dev.jsononly fordevcommands. (pnpm tauri devcalls the wrapper which adds-c src-tauri/tauri.dev.json, then Tauri merges this withtauri.conf.jsonvia JSON Merge Patch (RFC 7396).))
To avoid security issues in dev mode, always add a condition to disable that functionality in dev mode. This way, malicious websites can't access the system APIs even on your machine.
Cmdr can ship diagnostic bundles (manifest + recent debug-level log tail) to the maintainer when something goes wrong. Privacy posture:
- Flow A — user-initiated. Help > Send error report… or the button on error toasts opens a preview dialog showing exactly what's about to be sent (manifest + first 5 / last 20 redacted log lines). Clicking Send is the consent — no setting required.
- Flow B — auto-send on error. Gated by
updates.errorReports(default off). When enabled, user-visible errors fire a debounced auto-send (60 s window with ±10 s jitter to avoid lock-step reporting under global outages). The toast surfaces the send with View (opens the same preview as Flow A) and Change settings.
Flow A is unconditional. Flow B is opt-in only.
Both flows pass every log line and the manifest through apps/desktop/src-tauri/src/redact/ — see
its CLAUDE.md for the full pattern catalog and the mandatory
snapshot-tested corpus.
- Path-shape preserved.
/Users/john/Documents/budget.pdf→$HOME/Documents/<file>.pdf— extension and known-safe parent dir kept, user-identifying parts redacted. - Allowlist of safe parent dirs.
Documents,Downloads,Desktop,Library,src,Pictures,Movies,Music,Public,AppData,Application Support. Anything else (for example,/Users/john/SecretProjectName/...) collapses the parent to<dir>. - What's redacted: Unix/Linux/Windows home paths,
/Volumes/<label>,/media/<label>, SMB UNC URIs, bare*.localhostnames, MTP device names (in log targets), IPv4/IPv6, email addresses, URL userinfo. - What's NOT redacted: module paths (
cmdr_lib::network::smb_client), filenames inside known-safe dirs (just the user-identifying chunks around them), version strings, and anything that looks path-ish but isn't (Cargo.toml,0.1.2-alpha).
The redactor is also used by the crash reporter, so the same rules apply to crash payloads.
No license key, no email, no device ID is attached. The short ID ERR-XXXXX (5 chars from the same unambiguous alphabet
as license short codes) is the only correlation handle. Users include it in their bug report when they reach out.
Bundles land in the cmdr-error-reports R2 bucket (key: error-reports/{yyyy-mm-dd}/{ERR-XXXXX}-{uuid}.zip). Three
retention layers, in order of aggressiveness:
- 8/6 GB watermark eviction. On every upload (in
event.waitUntil(...), so the client response isn't blocked) the server checks total bytes; if > 8 GB it deletes oldest objects until total ≤ 6 GB. KV-locked to avoid concurrent evictors. - Daily cron sweep.
handleDailyEvictionSweeprecomputes total from R2 ground truth and re-runs eviction. Catches KV drift. - R2 lifecycle rule. 90-day expiration at the bucket level — third safety net if both layers above fail.
Each upload triggers a one-line Discord notification to the private #error-reports channel with a presigned R2 GET URL
embedded. TTL: 7 days (R2's max for presigned URLs). Rationale: only the maintainer has access to the Cmdr Discord
server, and the channel is not shared, so the convenience of a click-to-download link outweighs the theoretical risk of
URL leakage. If access widens later, flip to short-TTL + admin re-mint endpoint (~50 LOC).