Canonical reference for how Cmdr keeps prod, dev, per-worktree dev, and concurrent E2E shards from colliding on disk, ports, Keychain, clipboard, fixtures, or process names.
One env var (CMDR_INSTANCE_ID) drives every per-instance suffix. Read AGENTS.md for repo-wide
rules. This doc is the canonical reference — the design has fully landed in code.
CMDR_INSTANCE_ID is a short ASCII string the wrapper or the E2E checker sets at launch. Everything else (bundle
identifier, data dir, Keychain service name, MCP ports, Vite port, clipboard backend, fixture root, Dock label) is a
pure function of that string. Prod leaves it unset and ends up byte-identical to before instance isolation existed.
| Scenario | CMDR_INSTANCE_ID value |
Bundle identifier | Data dir | macOS Keychain SERVICE_NAME |
Dock label / productName |
|---|---|---|---|---|---|
| Prod | (unset) | com.veszelovszki.cmdr |
~/Library/Application Support/com.veszelovszki.cmdr/ |
Cmdr |
Cmdr |
pnpm dev |
dev |
com.veszelovszki.cmdr-dev |
~/.../com.veszelovszki.cmdr-dev/ |
Cmdr-dev |
Cmdr (dev) |
pnpm dev --worktree foo |
dev-foo |
com.veszelovszki.cmdr-dev-foo |
~/.../com.veszelovszki.cmdr-dev-foo/ |
Cmdr-dev-foo |
Cmdr (dev-foo) |
E2E nonmtp shard (PID N) |
e2e-nonmtp1-N |
com.veszelovszki.cmdr-e2e-nonmtp1-N |
/tmp/cmdr-e2e-data-e2e-nonmtp1-N/ |
Cmdr-e2e-nonmtp1-N |
Cmdr (E2E nonmtp1) |
E2E MTP shard (PID N) |
e2e-mtp-N |
com.veszelovszki.cmdr-e2e-mtp-N |
/tmp/cmdr-e2e-data-e2e-mtp-N/ |
Cmdr-e2e-mtp-N |
Cmdr (E2E mtp) |
Slug rules for --worktree: lowercase ASCII [a-z0-9-]+, max 32 chars, runs of - collapsed, leading/trailing -
trimmed. Rejection happens in Node before any Rust process spawns. Source of truth: sanitizeWorktreeSlug in
apps/desktop/scripts/instance-id.js.
| Resource | Per-instance behavior | Authoritative file |
|---|---|---|
| Data dir | CMDR_DATA_DIR env wins; otherwise Tauri's app_data_dir() resolves from the identifier in the generated config. Wrapper sets both so they agree. |
config.rs, instance-id.js |
tauri-plugin-store (settings.json) |
Identifier-driven via BaseDirectory::AppData. No code override needed; the identifier change in the generated config redirects the file automatically. |
lib.rs |
tauri-plugin-window-state |
Same identifier-driven redirect. | (same) |
| macOS Keychain | SERVICE_NAME becomes Cmdr-<instance> when the env is set; Cmdr otherwise. Cached once via OnceLock. |
keychain_macos.rs |
| Secret store backend | Wrapper exports CMDR_SECRET_STORE=file for any non-prod instance, so dev and per-worktree dev never trigger the Keychain password dialog. E2E forces the same path via is_e2e_mode(). |
tauri-wrapper.js, secrets/mod.rs |
| Cmdr MCP HTTP port | Ephemeral by default (developer.mcpPort = 0). Server binds 127.0.0.1:0, writes the actual port to <data_dir>/mcp.port atomically. CMDR_MCP_PORT still pins. |
mcp/server.rs, mcp/port_file.rs |
| Tauri MCP bridge port | Wrapper allocates via net.createServer().listen(0), exports CMDR_MCP_BRIDGE_PORT, writes <data_dir>/tauri-mcp.port BEFORE Tauri launches. Plugin forced to 127.0.0.1 (was 0.0.0.0, a LAN exposure: load-bearing security fix). |
tauri-wrapper.js, lib.rs |
| Vite dev port | Wrapper allocates ephemeral, exports CMDR_VITE_PORT, writes build.devUrl into the generated config so the Tauri webview points at the same number Vite binds. |
tauri-wrapper.js, vite.config.js |
| Updater endpoint | Non-prod gets https://localhost.invalid/no-updater in the generated config so dev or E2E never phones home accidentally. |
instance-id.js |
| Clipboard (NSPasteboard) | Compiled out for E2E via #[cfg(feature = "playwright-e2e")]: mock module replaces the real one. Process-local store, never touches the user's pasteboard. CMDR_CLIPBOARD_BACKEND=mock env override delegates to the same store from prod-feature builds. |
clipboard/mod.rs |
| Fixture root (macOS E2E) | /tmp/cmdr-e2e-fixtures-<instance>-<timestamp>/. Bulk .dat files hardlinked from /tmp/cmdr-e2e-fixtures-cache/ (built once via tmp-dir + content-hash verify + atomic rename). Text files are full copies because tests mutate them. |
e2e-shared/fixtures.ts |
| Fixture root (Linux E2E) | Stays at /tmp/cmdr-e2e-<timestamp>/, no cache. Single shard, low benefit. |
(same) |
| Playwright socket | Per-shard at /tmp/tauri-playwright-<instance>.sock via CMDR_PLAYWRIGHT_SOCKET. Plugin falls back to /tmp/tauri-playwright.sock when unset (manual / Linux paths). |
lib.rs |
apps/desktop/scripts/tauri-wrapper.js is the single composition point. The split between it and
apps/desktop/scripts/instance-id.js exists so the pure helpers (slug sanitization, instance derivation, config payload
builder, port-file write protocol) are unit-testable via Vitest without spawning Tauri.
The launch sequence:
- Parse
--worktree <slug>from argv (before the--separator, so Tauri flags after--stay intact). - Resolve
CMDR_INSTANCE_ID: existing env wins, then sanitized--worktreeslug, thendevin dev mode, else unset for prod. - In dev: allocate an ephemeral port for the Tauri MCP bridge (
CMDR_MCP_BRIDGE_PORT) and one for Vite (CMDR_VITE_PORT). - Compute identifier, data dir, productName, generated config payload via
deriveInstance. - Write the generated
tauri.instance.jsonto$TMPDIR/cmdr-tauri-instance-<rand>/and pass-c <abs-path>to Tauri. - Write
<data_dir>/tauri-mcp.portatomically (tempfile + fsync + rename) so external readers can discover the bridge port before Tauri's own bind completes. - Export
CMDR_DATA_DIR(when unset) andCMDR_SECRET_STORE=file(when unset). - Spawn
pnpm exec tauri .... On exit / SIGINT / SIGTERM, remove the tmp config dir and the tauri-mcp port file (best-effort;/tmpself-prunes on macOS anyway).
The generated config never lands in the repo: it lives under $TMPDIR so a crashed wrapper can't pollute tracked space.
# Terminal A (worktree A):
cd ~/projects-git/vdavid/cmdr/.claude/worktrees/feature-a
pnpm dev --worktree a
# Terminal B (worktree B, simultaneously):
cd ~/projects-git/vdavid/cmdr/.claude/worktrees/feature-b
pnpm dev --worktree bEach session gets:
- A unique bundle identifier (
com.veszelovszki.cmdr-dev-avs-dev-b), sotauri-plugin-storeandtauri-plugin-window-stateland in~/Library/Application Support/com.veszelovszki.cmdr-dev-{a,b}/. - A unique Dock label (
Cmdr (dev-a)vsCmdr (dev-b)). - An ephemeral Vite port (no
EADDRINUSEon1420). - Ephemeral Cmdr MCP and Tauri MCP bridge ports, discovered via
<data_dir>/mcp.portand<data_dir>/tauri-mcp.port. - An isolated Keychain service namespace (
Cmdr-dev-avsCmdr-dev-b), though both default to the file backend in dev so the Keychain isn't hit anyway. - An isolated file-backed secret store (under the per-instance data dir).
Quitting one session has no effect on the other.
The Playwright checker (scripts/check/checks/desktop-svelte-e2e-playwright.go) runs the suite as multiple parallel
shards: one MTP shard (serialized) plus two non-MTP shards split by --shard X/2. Per shard the checker stamps:
CMDR_INSTANCE_ID=e2e-<kind>-<pid>(for example,e2e-mtp-12345,e2e-nonmtp1-12345).CMDR_DATA_DIR=/tmp/cmdr-e2e-data-<instance>/.CMDR_MCP_PORT=<9429 + offset>(for the Cmdr MCP server: pinned per-shard so test fixtures don't have to read the port file).CMDR_MCP_BRIDGE_PORT=<ephemeral>for the Tauri MCP bridge.CMDR_PLAYWRIGHT_SOCKET=/tmp/tauri-playwright-<instance>.sock.CMDR_E2E_START_PATH=/tmp/cmdr-e2e-fixtures-<instance>-<ts>/(created with hardlinks from the shared cache).CMDR_E2E_MODE=1,CMDR_MOCK_FDA=granted.
Two concurrent ./scripts/check.sh --check desktop-e2e-playwright runs from two worktrees never collide on data dir,
ports, sockets, fixture roots, Keychain, or processes (the Dock label Cmdr (E2E <kind>) lets pgrep -f 'Cmdr (E2E '
target only the right ones).
The MTP shard always runs alone because the virtual MTP backing dir (/tmp/cmdr-mtp-e2e-fixtures/) is shared by every
Tauri instance (the virtual device is wired into the same path globally). Running MTP specs from two shards at once
would corrupt that backing dir.
Which port is the Cmdr MCP server on right now?
# In-process (FE only): the get_mcp_port IPC reads MCP_ACTUAL_PORT directly.
# Out-of-process (CLI, agent helpers):
cat ~/Library/Application\ Support/com.veszelovszki.cmdr-dev/mcp.portFor a worktree session, swap cmdr-dev for cmdr-dev-<your-slug>. For an E2E shard, the path is under
/tmp/cmdr-e2e-data-<instance>/.
The scripts/mcp-call.sh helper auto-discovers: when CMDR_INSTANCE_ID is set, it resolves the data dir and reads the
port file. CMDR_MCP_PORT still pins.
CMDR_INSTANCE_ID=dev-a ./scripts/mcp-call.sh --list-toolsWhere's my data dir?
The wrapper logs it at startup: Using CMDR_DATA_DIR: .... The same path is what CMDR_DATA_DIR exports and what
Tauri's app_data_dir() returns. crash-report.json, settings.json, window-state.json, mcp.port,
tauri-mcp.port, the log dir, and the file-backed secret store all live under it.
Why is the Dock showing Cmdr (dev-a) and not Cmdr?
You started the app with an instance ID set (either via --worktree a or because CMDR_INSTANCE_ID was already in your
env). productName mirrors the bundle identifier so cleanup scripts and Dock interactions can target the right process.
The Tauri MCP bridge isn't responding on 9223.
There's no longer a fixed port. The wrapper allocates an ephemeral one per instance and writes
<data_dir>/tauri-mcp.port. Read that file. The plugin now binds 127.0.0.1 only (was 0.0.0.0, a LAN exposure we
fixed at the same time).
| File | Owner | Lifetime | Purpose |
|---|---|---|---|
$TMPDIR/cmdr-tauri-instance-<rand>/tauri.instance.json |
wrapper | per-launch (cleaned on exit / SIGINT) | Tauri -c override for identifier, productName, devUrl, updater endpoint |
<data_dir>/mcp.port |
Rust | server lifetime (best-effort delete on shutdown) | actual bound port of the Cmdr MCP HTTP server |
<data_dir>/tauri-mcp.port |
wrapper | per-launch (best-effort delete on exit) | wrapper-allocated port the Tauri MCP bridge will bind |
/tmp/cmdr-e2e-fixtures-cache/ |
Node | persistent (rebuild on file-shape change) | shared hardlink source for E2E bulk .dat fixtures |
/tmp/cmdr-e2e-fixtures-<instance>-<ts>/ |
Node | per-run | per-shard fixture root |
/tmp/cmdr-e2e-data-<instance>/ |
Go checker | per-run | per-shard data dir |
/tmp/tauri-playwright-<instance>.sock |
Tauri plugin | per-run | per-shard Playwright IPC socket |
CMDR_DATA_DIRis authoritative for data-dir paths. If set, the backend uses it as-is.CMDR_INSTANCE_IDis authoritative for Keychain service name, clipboard backend selection (when the Cargo feature isn't already on), and the Dock label. It does NOT participate in data-dir resolution.- MCP port read precedence (external clients):
CMDR_MCP_PORTenv →<data_dir>/mcp.port→ typed error. Never silently fall back to a legacy hardcoded default; that hides bugs. - MCP port write precedence: even when
CMDR_MCP_PORTis set, the server still writes the bound port to the file so external readers don't have to special-case the pinned variant. - Wrapper always sets both
CMDR_DATA_DIRandCMDR_INSTANCE_IDso Tauri'sapp_data_dir()and ourCMDR_DATA_DIRagree.
Two patterns coexist on purpose:
- Cargo feature (
#[cfg(feature = "playwright-e2e")]) when the mock would otherwise compile in heavy platform deps (objc2,security-framework, OS FFI). Compile-time switch keeps prod binaries lean and removes whole code paths. Example: the clipboard mock. - Runtime env var when the mock is an alternative implementation of an existing function and the prod path is light.
Examples:
CMDR_MOCK_FDAgates a few syscalls;CMDR_E2E_MODE=1toggles soft hooks (title-bar stripe);CMDR_CLIPBOARD_BACKEND=mocklets prod-feature dev builds delegate to the shared store for ad-hoc debugging.
Both patterns can coexist on one subsystem (the clipboard does: feature flag for the E2E build, env for the manual
override on a prod-feature build). Each subsystem's CLAUDE.md documents which hooks it honors.
Two manual tests that prove the primitive holds. Re-run these after any change to the wrapper, the E2E checker, or any of the per-resource derivation paths. Future regressions are caught by re-running them.
- Open two terminal shells, each in a different worktree (typically
~/projects-git/vdavid/cmdr/.claude/worktrees/<name>/). - In shell A:
pnpm dev --worktree a. - In shell B:
pnpm dev --worktree b. - Both windows open with no
EADDRINUSEerrors on the wrapper or Vite. Dock showsCmdr (dev-a)andCmdr (dev-b). - Change a setting in window A (for example, toggle hidden files). Quit window A. Confirm
~/Library/Application Support/com.veszelovszki.cmdr-dev-a/settings.jsonexists with the change persisted, and~/Library/Application Support/com.veszelovszki.cmdr-dev-b/settings.jsonis untouched. - Window B is still running and responsive. The B session's MCP port (in
~/Library/Application Support/com.veszelovszki.cmdr-dev-b/mcp.port) is reachable. lsof -i -P | grep Cmdr | grep LISTEN: every line shows127.0.0.1(no*:bind), and the port numbers per session don't overlap.
- Open two shells, each in a different worktree.
- In each shell:
./scripts/check.sh --check desktop-e2e-playwright. - Mid-run, in a third shell:
ls /tmp/cmdr-e2e-fixtures-*shows distinct<instance>-<ts>dirs for each shard from each run, plus one shared/tmp/cmdr-e2e-fixtures-cache/.ls /tmp/cmdr-e2e-data-*shows distinct data dirs per shard.ls /tmp/tauri-playwright-*.sockshows distinct sockets per shard.lsof -i -P | grep Cmdr | grep LISTEN: every line bound to127.0.0.1, distinct ports per shard.pgrep -fl 'Cmdr (E2E ': process labels are distinct per shard.
- Open TextEdit before starting the runs with some content in your real clipboard. After both runs finish,
pbpastereturns the same content (the clipboard mock never touched the system pasteboard). - Both runs complete with the same pass/fail result they'd have in isolation.
du -sh /tmp/cmdr-e2e-fixtures-cache: roughly 170 MB, paid once across both runs (hardlinks).
docs/tooling/mcp.md: MCP server overview, port discovery for external clients, action-tool ack contract.AGENTS.md§ Debugging / § MCP / § Worktrees: repo-wide cross-references.apps/desktop/CLAUDE.md: desktop app overview,--worktreeflag.apps/desktop/src-tauri/src/mcp/CLAUDE.md: server lifecycle and port-file protocol.apps/desktop/src-tauri/src/secrets/CLAUDE.md: Keychain service-name suffix.apps/desktop/src-tauri/src/clipboard/CLAUDE.md: mock backend.apps/desktop/test/CLAUDE.md: fixture cache, per-instance root.scripts/check/CLAUDE.md§ Self-contained E2E checks: per-shard env composition.