# Claude Desktop Cowork Mode - VM Architecture Analysis

> This document contains reverse-engineered code from Claude Desktop's Electron app, documenting how the Cowork mode (Local Agent Mode) interfaces with the `@ant/claude-swift` native addon for VM-based isolation on macOS.

**URL:** https://aaddrick.com/blog/claude-desktop-cowork-mode-vm-architecture-analysis

---

This document contains reverse-engineered code from Claude Desktop's Electron app, documenting how the Cowork mode (Local Agent Mode) interfaces with the `@ant/claude-swift` native addon for VM-based isolation on macOS.

**Sources:**
- `build-reference/app-extracted/.vite/build/index.js` (main process)
- `build-reference/app-extracted/.vite/build/mainView.js` (renderer preload)

---

## Table of Contents

1. [Overview](#overview)
2. [Module Loading](#module-loading)
3. [VM Lifecycle Management](#vm-lifecycle-management)
4. [Process Management](#process-management)
5. [Stdin/Stdout Communication](#stdinstdout-communication)
6. [Path Translation & Mounting](#path-translation--mounting)
7. [IPC Interface (Renderer → Main)](#ipc-interface-renderer--main)
8. [Settings Configuration Architecture](#settings-configuration-architecture)
   - [Two Storage Systems](#two-storage-systems)
   - [User Preferences Schema](#user-preferences-schema)
   - [YukonSilver Config (VM Settings)](#yukonsilver-config-vm-settings)
   - [Enterprise Configuration Override](#enterprise-configuration-override)
   - [Disabling Cowork - Methods Summary](#disabling-cowork---methods-summary)
9. [Security Architecture](#security-architecture)
   - [Hypervisor Isolation (VZVirtualMachine)](#1-hypervisor-isolation-vzvirtualmachine)
   - [Bubblewrap + Seccomp Sandboxing](#2-bubblewrap--seccomp-sandboxing-inside-vm)
   - [Path Traversal Detection & Blocked Extensions](#3-path-traversal-detection--blocked-extensions)
   - [OAuth MITM Proxy](#4-oauth-mitm-proxy-for-token-approval)
   - [Network Egress Allow-List](#5-network-egress-allow-list-alloweddomains)
10. [Symbol Reference](#symbol-reference)
11. [Heartbeat Monitoring](#heartbeat-monitoring)
12. [VM Bundle Management](#vm-bundle-management)
13. [Knowledge Base Integration](#knowledge-base-integration)
14. [File System Watching](#file-system-watching)
15. [ClaudeVM Web IPC Interface](#claudevm-web-ipc-interface)
16. [Idle Session Notifications](#idle-session-notifications)
17. [Telemetry Events Reference](#telemetry-events-reference)
18. [Internal Feature Codenames](#internal-feature-codenames)
19. [VM Image Analysis](#vm-image-analysis)
    - [Download URLs](#download-urls)
    - [Image Structure](#image-structure)
    - [Base System](#base-system)
    - [SDK Daemon](#sdk-daemon)
    - [Sandbox Runtime Package](#sandbox-runtime-package)
    - [Seccomp Filters](#seccomp-filters)
    - [Installed Tools](#installed-tools)
    - [Implications for Linux Port](#implications-for-linux-port)

---

## Overview

Claude Desktop's Cowork mode runs Claude Code CLI inside an isolated Linux VM using Apple's Virtualization Framework (VZVirtualMachine). The architecture provides:

- **Hard isolation**: Code runs in a sandboxed Linux VM, not on the host
- **Explicit mounting**: Only user-selected folders are accessible
- **Path translation**: VM paths like `/sessions/<name>/mnt/<folder>` map to host paths
- **Stdio-based IPC**: Communication via stdin/stdout streams through the native addon

```
┌─────────────────────────────────────────────────────────────────┐
│                        Electron App                              │
│  ┌─────────────┐     ┌──────────────────┐     ┌──────────────┐  │
│  │  Renderer   │────▶│   Main Process   │────▶│@ant/claude-  │  │
│  │  (Web UI)   │ IPC │  (Node.js)       │     │swift (Swift) │  │
│  └─────────────┘     └──────────────────┘     └──────┬───────┘  │
│                                                       │          │
└───────────────────────────────────────────────────────┼──────────┘
                                                        │
                                          ┌─────────────▼─────────────┐
                                          │   VZVirtualMachine        │
                                          │   ┌───────────────────┐   │
                                          │   │    Linux VM       │   │
                                          │   │  ┌─────────────┐  │   │
                                          │   │  │ Claude Code │  │   │
                                          │   │  │    CLI      │  │   │
                                          │   │  └─────────────┘  │   │
                                          │   └───────────────────┘   │
                                          └───────────────────────────┘
```

---

## Module Loading

The `@ant/claude-swift` module is loaded lazily with caching. Three separate cache variables exist for different components.

### Primary Module Loader

```javascript
// Minified: ff, Vw, p0e, li, eXe
// Refactored names shown below

let swiftModuleCache = null;
let swiftModulePromise = null;

/**
 * Loads the @ant/claude-swift module with singleton caching.
 * Returns the cached module or initiates loading.
 */
async function loadSwiftModule() {
  return (
    swiftModuleCache ||
    swiftModulePromise ||
    (logger.info("[SwiftVM] Loading @ant/claude-swift module..."),
    (swiftModulePromise = (async () => {
      try {
        return (
          (swiftModuleCache = (await import("@ant/claude-swift")).default),
          logger.info("[SwiftVM] Module loaded successfully"),
          swiftModuleCache
        );
      } catch (error) {
        return (logger.error("[SwiftVM] Failed to load module: %o", error), null);
      }
    })()),
    swiftModulePromise)
  );
}

/**
 * Gets the VM interface from the loaded module.
 * The module exports { vm, quickAccess, notifications, desktop, api, midnightOwl }
 */
async function getVMInterface() {
  const module = await loadSwiftModule();
  return (module == null ? void 0 : module.vm) ?? null;
}

// Synchronous cached access (returns null if not yet loaded)
getVMInterface.getCached = function () {
  return (swiftModuleCache == null ? void 0 : swiftModuleCache.vm) ?? null;
};

/**
 * Gets the full swift module (for non-VM features like notifications, quickAccess)
 */
async function getSwiftModule() {
  return loadSwiftModule();
}
```

### Platform-Specific Feature Loading

```javascript
// Minified: ii, Unt (notifications), Ht, tit (quick entry)

let notificationsModule = null;

/**
 * Loads swift module for notifications (macOS only)
 */
async function loadNotificationsModule() {
  if (process.platform !== "darwin") return null;
  try {
    return ((notificationsModule = (await import("@ant/claude-swift")).default), notificationsModule);
  } catch (error) {
    return (
      logger.warn("Failed to load claude-swift for notifications: %o", error),
      null
    );
  }
}

let quickEntryModule = null;

/**
 * Initializes Quick Entry (spotlight-like input) on macOS
 * Sets up keyboard shortcuts, API credentials, dictation
 */
async function initializeQuickEntry() {
  if ((await electron.app.whenReady(), !!isQuickEntrySupported()))
    try {
      quickEntryModule = (await import("@ant/claude-swift")).default;

      setupQuickEntryShortcut();
      setupDictationShortcut();

      // Listen for preference changes
      settingsEmitter.on("quickEntryShortcut", () => {
        setupQuickEntryShortcut();
      });

      // Set up event handlers
      quickEntryModule.on("logAnalyticsEvent", ({ eventName, metadata }) => {
        trackEvent(eventName, metadata);
      });

      quickEntryModule.on("quickEntrySubmitted", handleQuickEntrySubmit);

      quickEntryModule.on("navigateToChat", async (chatId) => {
        const { navigateToExistingChat } = await import("./navigation");
        navigateToExistingChat(chatId);
      });

      await configureAPICredentials();
      await configureDictationLanguage();
    } catch (error) {
      logger.warn("Failed to load claude-swift %o", error);
    }
}
```

---

## VM Lifecycle Management

### Main Startup Sequence (4 Steps)

```javascript
// Minified: AXe, Mx, kg, iae, TXe, $Xe
// Refactored names shown below

let vmStartupPromise = null;
const VM_CONNECTION_TIMEOUT_MS = 120000;  // iae
const DEFAULT_VM_RAM_GB = 8;              // TXe
const GUEST_POLL_INTERVAL_MS = 100;       // $Xe

/**
 * Main VM startup orchestration - 4 step process:
 * 1. Download VM bundle + SDK
 * 2. Load Swift VM API
 * 3. Start the VM
 * 4. Wait for guest connection
 *
 * @param options - { memoryGB?: number }
 * @param progressCallback - Progress reporting callback
 */
async function startVMInternal(options, progressCallback) {
  const bundlePath = getVMBundlePath();
  const startTime = Date.now();
  const vmInstanceId = getVMInstanceId();

  logger.info(`[VM:start] Beginning startup, bundlePath=${bundlePath}`);
  logger.info(`[VM:start] Bundle version: ${bundleVersion}`);
  logger.info(`[VM:start] VM instance ID: ${vmInstanceId}`);

  // Step 1/4: Download bundle + SDK
  logger.info("[VM:start] Step 1/4: Downloading bundle + SDK...");
  const step1Start = Date.now();
  const [isFreshDownload, sdkResult] = await Promise.all([
    downloadVMBundle(progressCallback),
    sdkManager.prepareForVM()
  ]);

  if (!sdkResult.ready) {
    throw new Error(sdkResult.error ?? "SDK preparation failed");
  }

  logger.info(`[VM:start] Step 1/4 complete: download=${isFreshDownload}, SDK ready, took ${Date.now() - step1Start}ms`);

  // Step 2/4: Load Swift VM API
  logger.info("[VM:start] Step 2/4: Loading Swift VM API...");
  const step2Start = Date.now();
  const vmInterface = await getVMInterface();

  if (!vmInterface) {
    logger.error("[VM:start] Swift VM addon not available");
    trackEvent("lam_vm_startup_failed", {
      vm_instance_id: vmInstanceId,
      bundle_version: bundleVersion,
      duration_ms: Date.now() - startTime,
      error_type: "swift_addon_unavailable",
      is_fresh_download: isFreshDownload,
      failed_step: "swift_api",
    });
    throw new Error("Swift VM addon not available");
  }

  logger.info(`[VM:start] Step 2/4 complete: Swift API loaded, took ${Date.now() - step2Start}ms`);

  // Initialize callbacks
  await initializeVMEventCallbacks();
  setupHeartbeatMonitor();

  // Register shutdown handler (only once)
  if (!shutdownHandlerRegistered) {
    shutdownHandlerRegistered = true;
    registerShutdownHandler({
      name: "cowork-vm-shutdown",
      fn: async () => {
        const shutdownStart = Date.now();
        const instanceId = getVMInstanceId();
        logger.info(`[VM:shutdown] App quit, stopping VM (instance: ${instanceId})...`);
        stopHeartbeat();
        try {
          await vmInterface.stopVM();
          logger.info(`[VM:shutdown] Completed in ${Date.now() - shutdownStart}ms`);
          trackEvent("lam_vm_shutdown_completed", {
            vm_instance_id: instanceId ?? "unknown",
            bundle_version: bundleVersion,
            duration_ms: Date.now() - shutdownStart,
            trigger: "app_quit",
          });
        } catch (error) {
          logger.error("[VM:shutdown] Failed: %o", error);
          throw error;
        } finally {
          clearVMInstanceId();
        }
      },
    });
  }

  // Step 3/4: Start VM
  logger.info("[VM:start] Step 3/4: Starting VM...");
  const step3Start = Date.now();
  const ramGB = (options == null ? void 0 : options.memoryGB) ?? DEFAULT_VM_RAM_GB;

  await vmInterface.startVM(bundlePath, ramGB);

  logger.info(`[VM:start] Step 3/4 complete: VM start called, took ${Date.now() - step3Start}ms`);
  setVMState(VMState.Booting);

  // Step 4/4: Wait for guest connection
  logger.info("[VM:start] Step 4/4: Waiting for guest connection...");
  const step4Start = Date.now();
  let pollCount = 0;
  let lastLogTime = Date.now();

  while (Date.now() - step2Start < VM_CONNECTION_TIMEOUT_MS) {
    pollCount++;

    if (await isGuestConnected()) {
      const totalDuration = Date.now() - startTime;
      logger.info(
        `[VM:start] Step 4/4 complete: connected after ${pollCount} polls, ${Date.now() - step4Start}ms since boot`
      );

      // Install SDK in guest
      const sdkSubpath = sdkManager.getVMStorageSubpath();
      const sdkVersion = sdkManager.getRequiredVersion();
      logger.info(`[VM:start] Installing SDK: subpath=${sdkSubpath}, version=${sdkVersion}`);
      await vmInterface.installSdk(sdkSubpath, sdkVersion);

      logger.info(`[VM:start] SDK installed, total startup time: ${totalDuration}ms`);
      clearStartupError();
      setVMState(VMState.Ready);

      trackEvent("lam_vm_startup_completed", {
        vm_instance_id: vmInstanceId,
        bundle_version: bundleVersion,
        duration_ms: totalDuration,
        is_fresh_download: isFreshDownload,
      });

      // Setup heartbeat restart handler
      startHeartbeat({
        onRestart: async () => {
          logger.info("[VM:heartbeat] Heartbeat failure detected, restarting VM...");
          try {
            await vmInterface.stopVM();
            clearVMInstanceId();
            await startVM(options);
            logger.info("[VM:heartbeat] VM restart completed successfully");
          } catch (err) {
            logger.error("[VM:heartbeat] VM restart failed:", err);
          }
        },
      });

      return;
    }

    // Log progress every 10 seconds
    if (Date.now() - lastLogTime >= 10000) {
      const elapsed = Date.now() - step4Start;
      logger.info(`[VM:start] Still waiting for guest connection... ${elapsed}ms elapsed, ${pollCount} polls`);
      lastLogTime = Date.now();
    }

    await new Promise((resolve) => setTimeout(resolve, GUEST_POLL_INTERVAL_MS));
  }

  // Timeout
  clearStartupError();
  const totalDuration = Date.now() - startTime;
  const waitDuration = Date.now() - step2Start;

  logger.error(
    `[VM:start] Connection timeout after ${waitDuration}ms waiting for guest (${totalDuration}ms total), ${pollCount} polls`
  );

  const errorMessage = "VM connection timeout";
  setStartupError(errorMessage);
  throw new Error(errorMessage);
}

/**
 * Public wrapper ensuring single concurrent startup
 */
async function startVM(options, progressCallback) {
  if (vmStartupPromise) {
    logger.info("[startVM] VM startup already in progress, waiting...");
    return vmStartupPromise;
  }

  if (await isGuestConnected()) {
    logger.info("[startVM] VM already connected");
    return;
  }

  return (
    (vmStartupPromise = startVMInternal(options, progressCallback)
      .catch((error) => {
        setVMState(VMState.Offline);
        throw error;
      })
      .finally(() => {
        vmStartupPromise = null;
      })),
    vmStartupPromise
  );
}
```

### Guest Connection Check

```javascript
// Minified: h_

/**
 * Checks if the VM guest is connected and ready
 */
async function isGuestConnected() {
  const vmInterface = await getVMInterface();
  return vmInterface ? vmInterface.isGuestConnected() : false;
}
```

---

## Process Management

### VMProcess Class

```javascript
// Minified: tXe, dc, zw, sF
// Refactored names shown below

const activeProcesses = new Map();  // dc
let activeProcessCount = 0;         // zw
const vmEventEmitter = new EventEmitter();  // sF

/**
 * Represents a process running inside the VM.
 * Manages stdin/stdout streams and lifecycle.
 */
class VMProcess extends EventEmitter {
  constructor(processId, processName) {
    super();
    this.id = processId;
    this.name = processName;
    this._killed = false;
    this._exitCode = null;
    this._wasKilled = false;
    this._spawnConfirmed = false;
    this._stdinBuffer = [];
    this._stdin = new PassThrough();
    this._stdout = new PassThrough();
    this._startTime = Date.now();

    activeProcesses.set(processId, this);
    activeProcessCount++;
    logger.info(`[Process:${processId}] Created, name=${processName}, total active=${activeProcessCount}`);
  }

  /**
   * Called when VM confirms the process has spawned.
   * Flushes any buffered stdin data.
   */
  async confirmSpawn() {
    logger.info(
      `[Process:${this.id}] Spawn confirmed, flushing ${this._stdinBuffer.length} buffered stdin chunks`
    );
    await this.flushBufferedStdin();
    this._spawnConfirmed = true;
  }

  /**
   * Flushes buffered stdin to the VM process
   */
  async flushBufferedStdin() {
    const vmInterface = await getVMInterface();
    if (vmInterface) {
      while (this._stdinBuffer.length > 0) {
        const chunk = this._stdinBuffer.shift();
        try {
          await vmInterface.writeStdin(this.id, chunk);
        } catch (error) {
          logger.error(`[Process:${this.id}] failed to flush buffered stdin: %o`, error);
        }
      }
    }
  }

  get stdin() { return this._stdin; }
  get stdout() { return this._stdout; }
  get killed() { return this._killed; }
  get exitCode() { return this._exitCode; }

  /**
   * Push data to stdout stream (called by VM event callback)
   */
  pushStdout(data) {
    this._stdout.push(data);
  }

  /**
   * Mark process as exited with code/signal
   */
  setExited(code, signal) {
    const exitCode = this._wasKilled ? 0 : code;
    const exitSignal = this._wasKilled ? null : signal;

    this._exitCode = exitCode;
    this._killed = exitSignal !== null;
    this._stdout.push(null);  // End stdout stream

    const duration = Date.now() - this._startTime;
    logger.info(`[Process:${this.id}] Exited, code=${exitCode}, signal=${exitSignal}, duration=${duration}ms`);

    trackEvent("lam_vm_process_exited", {
      vm_instance_id: getVMInstanceId() ?? "unknown",
      exit_code: exitCode,
      duration_ms: duration,
      was_killed: this._wasKilled || exitSignal !== null,
    });

    this.emit("exit", exitCode, exitSignal);
  }

  /**
   * Mark process as errored
   */
  setError(error, eventName) {
    logger.error(`[Process:${this.id}] Error: ${error.message}`);
    trackEvent(eventName ?? "lam_vm_runtime_error", {
      vm_instance_id: getVMInstanceId() ?? "unknown",
      error_message: error.message,
    });
    this.emit("error", error);
  }

  /**
   * Kill the process with optional signal
   */
  kill(signal) {
    logger.info(`[CoworkVMProcess:${this.id}] kill called with signal: %s`, signal);

    if (this._killed || this._exitCode !== null) {
      return true;
    }

    this._wasKilled = true;
    getVMInterface().then((vmInterface) => {
      if (vmInterface) {
        vmInterface.kill(this.id, signal).catch((error) => {
          logger.error(`[CoworkVMProcess:${this.id}] kill failed with error: %o`, error);
        });
        this._killed = true;
      }
    });

    return true;
  }

  /**
   * Set up forwarding from stdin stream to VM
   */
  setupStdinForwarding() {
    this._stdin.on("data", (chunk) => {
      const data = chunk.toString();
      const preview = data.length > 100 ? data.substring(0, 100) + "..." : data;

      logger.verbose(`[Process:${this.id}] stdin: ${data.length} bytes: ${preview.replace(/\n/g, "\\n")}`);

      // Buffer stdin until spawn is confirmed
      if (!this._spawnConfirmed) {
        logger.info(`[Process:${this.id}] Buffering stdin (spawn not yet confirmed): ${data.length} bytes`);
        this._stdinBuffer.push(data);
        return;
      }

      getVMInterface().then((vmInterface) => {
        if (vmInterface) {
          vmInterface.writeStdin(this.id, data).catch((error) => {
            logger.error(`[Process:${this.id}] failed to write stdin: %o`, error);
            this.emit("error", error);
          });
        }
      });
    });
  }

  /**
   * Clean up process from active map
   */
  cleanup() {
    activeProcesses.delete(this.id);
    activeProcessCount--;
    logger.info(`[Process:${this.id}] Cleaned up, remaining active=${activeProcessCount}`);
    this.removeAllListeners();
  }
}
```

---

## Stdin/Stdout Communication

### Event Callbacks Setup

```javascript
// Minified: rXe

/**
 * Initializes VM event callbacks for stdout, stderr, exit, error, and network status.
 * These callbacks route VM events to the appropriate VMProcess instances.
 */
async function initializeVMEventCallbacks() {
  logger.info("[Callbacks] Initializing VM event callbacks...");

  const vmInterface = await getVMInterface();
  if (!vmInterface) {
    logger.error("[Callbacks] Swift VM addon not available for callbacks");
    return;
  }

  // Stdout and stderr use the same handler - push to process stdout
  const outputHandler = (processId, data) => {
    const process = activeProcesses.get(processId);
    if (process) {
      process.pushStdout(data);
    }
  };

  vmInterface.setEventCallbacks(
    outputHandler,  // stdout callback
    outputHandler,  // stderr callback (merged with stdout)

    // Exit callback
    (processId, code, signal) => {
      const process = activeProcesses.get(processId);
      if (process) {
        process.setExited(code, signal);
      }
    },

    // Error callback
    (processId, message, eventName) => {
      const process = activeProcesses.get(processId);
      if (process) {
        process.setError(new Error(message));
      }
    },

    // Network status callback
    (status) => {
      logger.info(`[VM] Network status: ${status}`);
      vmEventEmitter.emit("networkStatus", status);
    }
  );

  // Guest connection change handler
  const swiftModule = await getSwiftModule();
  if (swiftModule) {
    swiftModule.on("guestConnectionChanged", (connected) => {
      logger.info(`[VM] Guest connection changed: ${connected}`);
      vmEventEmitter.emit("guestConnectionChanged", connected);

      if (!connected) {
        const processIds = Array.from(activeProcesses.keys());

        if (isGracefulQuit()) {
          logger.info(
            `[VM] Guest disconnected during graceful quit, cleanly exiting ${processIds.length} active processes`
          );
          for (const id of processIds) {
            const process = activeProcesses.get(id);
            if (process && process.exitCode === null && !process.killed) {
              process.setExited(0, null);
            }
          }
          return;
        }

        // Unexpected disconnect - terminate all processes
        logger.info(
          `[VM] Guest disconnected unexpectedly, terminating ${processIds.length} active processes with SIGKILL`
        );
        for (const id of processIds) {
          const process = activeProcesses.get(id);
          if (process && process.exitCode === null && !process.killed) {
            process.setExited(null, "SIGKILL");
          }
        }
      }
    });
  }

  logger.info("[Callbacks] VM event callbacks initialized");
}
```

### Spawn Function Factory

```javascript
// Minified: gXe, yXe

/**
 * Creates a spawn function configured for a specific session.
 *
 * @param config - Session configuration with mounts, process name, etc.
 * @returns A spawn function that creates VMProcess instances
 */
function createSpawnFunction(config) {
  const mountCount = Object.keys(config.additionalMounts).length;
  const mountNames = Object.keys(config.additionalMounts).join(", ");

  logger.info(
    `[Spawn:config] Creating spawn function for process=${config.processName}, isResume=${config.isResume}, mounts=${mountCount} (${mountNames}), allowedDomains=${(config.allowedDomains?.length) ?? 0}`
  );

  return (spawnOptions) => {
    const processId = crypto.randomUUID();

    logger.info(
      `[Spawn:create] id=${processId} name=${config.processName} cmd=${spawnOptions.command} args=${spawnOptions.args.join(" ")} cwd=${spawnOptions.cwd ?? "(none)"}`
    );

    initializeVMEventCallbacks();

    const process = new VMProcess(processId, config.processName);
    process.setupStdinForwarding();

    // Filter sensitive env vars from logging
    const envVars = {};
    let envCount = 0;
    for (const [key, value] of Object.entries(spawnOptions.env)) {
      if (value !== undefined) {
        envVars[key] = value;
        envCount++;
      }
    }

    logger.verbose(`[Spawn:create] id=${processId} env vars=${envCount} (sensitive values filtered)`);

    // Actually spawn in VM (async)
    spawnInVM(process, processId, spawnOptions.command, spawnOptions.args, spawnOptions.cwd, envVars, config)
      .catch((error) => {
        logger.error(`[Spawn:create] id=${processId} Failed to spawn: %o`, error);
        process.emit("error", error);
      });

    return process;
  };
}

/**
 * Spawns a process in the VM via the Swift addon.
 */
async function spawnInVM(process, processId, command, args, cwd, envVars, config) {
  const vmInterface = await getVMInterface();

  if (!vmInterface) {
    logger.error(`[Spawn:vm] id=${processId} Swift VM addon not available`);
    process.setError(
      new Error("Swift VM addon not available"),
      "lam_vm_process_spawn_failed"
    );
    return;
  }

  try {
    // Register OAuth token for MITM proxy if present
    const oauthToken = envVars.CLAUDE_CODE_OAUTH_TOKEN;
    if (oauthToken) {
      await vmInterface.addApprovedOauthToken(oauthToken);
      logger.info(`[Spawn:vm] id=${processId} OAuth token approved with MITM proxy`);
    }

    // Call the Swift addon's spawn method
    await vmInterface.spawn(
      processId,
      config.processName,
      command,
      args,
      cwd,
      Object.keys(envVars).length > 0 ? envVars : undefined,
      config.additionalMounts,
      config.isResume,
      config.allowedDomains,
      config.sharedCwdPath
    );

    await process.confirmSpawn();
    logger.info(`[Spawn:vm] id=${processId} Spawn succeeded`);

    trackEvent("lam_vm_process_spawned", {
      vm_instance_id: getVMInstanceId() ?? "unknown",
      is_resume: config.isResume,
      mount_count: Object.keys(config.additionalMounts).length,
    });

  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    logger.error(`[Spawn:vm] id=${processId} Spawn failed: ${message}`);
    process.setError(
      error instanceof Error ? error : new Error(message),
      "lam_vm_process_spawn_failed"
    );
  }
}
```

---

## Path Translation & Mounting

### VM Path to Host Path Translation

```javascript
// Minified: Og, DXe

/**
 * Translates a VM path to the corresponding host path.
 *
 * VM paths have the format: /sessions/<processName>/mnt/<mountName>/<subpath>
 *
 * @param vmPath - The path inside the VM
 * @param context - Session context with mount information
 * @returns Host path or null if translation fails
 */
function translateVMPathToHost(vmPath, context) {
  const {
    vmProcessName,
    sessionStorageDir,
    userSelectedFolders,
    sharedCwdPath,
  } = context;

  const sessionPrefix = `/sessions/${vmProcessName}/`;

  // Must start with session prefix
  if (!vmPath.startsWith(sessionPrefix)) {
    return null;
  }

  const relativePath = vmPath.slice(sessionPrefix.length);

  // Check for path traversal attempts
  if (isPathTraversal(relativePath)) {
    return null;
  }

  // Handle mount paths: /sessions/<name>/mnt/<mount>/<subpath>
  if (relativePath.startsWith("mnt/")) {
    const afterMnt = relativePath.slice(4);
    const slashIndex = afterMnt.indexOf("/");
    const mountName = slashIndex === -1 ? afterMnt : afterMnt.slice(0, slashIndex);
    const subpath = slashIndex === -1 ? "" : afterMnt.slice(slashIndex + 1);

    // Special mounts
    if (mountName === "outputs") {
      return sharedCwdPath
        ? path.join(os.homedir(), sharedCwdPath, "outputs", subpath)
        : sessionStorageDir
          ? path.join(sessionStorageDir, "outputs", subpath)
          : null;
    }

    if (mountName === "uploads") {
      return sessionStorageDir ? path.join(sessionStorageDir, "uploads", subpath) : null;
    }

    // User-selected folder mounts
    for (const folder of userSelectedFolders ?? []) {
      if (path.basename(folder) === mountName) {
        return path.join(folder, subpath);
      }
    }

    // Knowledge base mounts
    if (mountName === ".knowledge" && context.knowledgeBasePaths) {
      const kbSlash = subpath.indexOf("/");
      const kbName = kbSlash === -1 ? subpath : subpath.slice(0, kbSlash);
      const kbSubpath = kbSlash === -1 ? "" : subpath.slice(kbSlash + 1);
      const kbPath = context.knowledgeBasePaths.get(kbName);
      if (kbPath) {
        return path.join(kbPath, kbSubpath);
      }
    }

    return null;
  }

  // Non-mount paths (e.g., shared working directory)
  return sharedCwdPath ? path.join(os.homedir(), sharedCwdPath, relativePath) : null;
}
```

### Mount Directory Tool

```javascript
// Minified: (part of directory tool implementation)

/**
 * Mounts a host directory into the VM with specified permissions.
 * Called when user grants folder access through the UI.
 */
async function mountDirectory(sessionContext, hostPath) {
  const vmInterface = await getVMInterface();
  const { vmProcessName, sessionId } = sessionContext;

  // Resolve to real path
  const realPath = await fs.realpath(hostPath);

  // Get relative path from home directory
  const relativePath = path.relative(os.homedir(), realPath);

  // Mount name is the folder basename
  const mountName = path.basename(realPath);

  // Mount with read-write permissions
  await vmInterface.mountPath(sessionId, relativePath, mountName, "rw");

  const vmPath = `/sessions/${vmProcessName}/mnt/${mountName}`;

  // Track the folder as user-selected
  sessionContext.addUserSelectedFolder(sessionId, realPath);

  logger.info(`[CoworkDirectoryTool] Mounted directory: ${realPath} -> ${vmPath}`);

  return {
    content: [{
      type: "text",
      text: `Successfully mounted directory.

Host path: ${realPath}
VM path: ${vmPath}

You can now access files in this directory at ${vmPath}`,
    }],
  };
}

/**
 * Enables file deletion for a mounted directory.
 * Requires explicit user permission.
 */
async function enableFileDeletion(sessionContext, mountInfo) {
  const vmInterface = await getVMInterface();

  // Remount with read-write-delete permissions
  await vmInterface.mountPath(
    sessionContext.sessionId,
    mountInfo.subpath,
    mountInfo.name,
    "rwd"  // read, write, delete
  );

  sessionContext.setFileDeleteApprovedForMount(mountInfo.name);

  logger.info(`[CoworkDirectoryTool] Enabled file deletion for mount: ${mountInfo.name}`);
}
```

### Mount Path Patterns

The VM uses these standard mount paths:

| Mount Path | Purpose |
|------------|---------|
| `/sessions/<name>/mnt/<folder>` | User-selected folders |
| `/sessions/<name>/mnt/outputs` | Output files directory |
| `/sessions/<name>/mnt/uploads` | Uploaded files directory |
| `/sessions/<name>/mnt/.projects/<uuid>` | Project contexts |
| `/sessions/<name>/mnt/.plugins/<id>` | Remote plugins |
| `/sessions/<name>/mnt/.local-plugins/<path>` | Local plugins |
| `/sessions/<name>/mnt/.skills` | Skill definitions |
| `/sessions/<name>/mnt/.knowledge/<name>` | Knowledge bases |
| `/sessions/<name>/mnt/.claude` | Claude configuration |

---

## IPC Interface (Renderer → Main)

### LocalAgentModeSessions Object

This object is exposed to the renderer process via Electron's contextBridge. Each method invokes an IPC handler in the main process.

```javascript
// Minified: It (assigned to LocalAgentModeSessions)
// File: mainView.js (renderer preload)

const LocalAgentModeSessions = {
  // ─────────────────────────────────────────────
  // Session Lifecycle
  // ─────────────────────────────────────────────

  /**
   * Start a new Cowork session
   * @param options - Session configuration
   */
  start(options) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_start",
      options
    );
  },

  /**
   * Stop a running session
   * @param sessionId - Session to stop
   */
  stop(sessionId) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_stop",
      sessionId
    );
  },

  /**
   * Archive a session
   * @param sessionId - Session to archive
   * @param options - Archive options
   */
  archive(sessionId, options) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_archive",
      sessionId,
      options
    );
  },

  // ─────────────────────────────────────────────
  // Session Communication
  // ─────────────────────────────────────────────

  /**
   * Send a message to a session
   * @param sessionId - Target session
   * @param message - Message content
   * @param images - Attached images
   * @param mcpConfig - MCP server configuration
   * @param connectors - First-party connectors
   */
  sendMessage(sessionId, message, images, mcpConfig, connectors) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_sendMessage",
      sessionId,
      message,
      images,
      mcpConfig,
      connectors
    );
  },

  /**
   * Respond to a tool permission request
   * @param sessionId - Session ID
   * @param requestId - Permission request ID
   * @param approved - Whether permission was granted
   */
  respondToToolPermission(sessionId, requestId, approved) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_respondToToolPermission",
      sessionId,
      requestId,
      approved
    );
  },

  // ─────────────────────────────────────────────
  // Session Queries
  // ─────────────────────────────────────────────

  /**
   * Get a specific session
   * @param sessionId - Session ID
   * @param options - Query options
   */
  getSession(sessionId, options) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_getSession",
      sessionId,
      options
    );
  },

  /**
   * Get all sessions
   */
  getAll() {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_getAll"
    );
  },

  /**
   * Get session transcript
   * @param sessionId - Session ID
   */
  getTranscript(sessionId) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_getTranscript",
      sessionId
    );
  },

  // ─────────────────────────────────────────────
  // Folder Management
  // ─────────────────────────────────────────────

  /**
   * Get all trusted folders
   */
  getTrustedFolders() {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_getTrustedFolders"
    );
  },

  /**
   * Add a folder to trusted list
   * @param folderPath - Path to trust
   */
  addTrustedFolder(folderPath) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_addTrustedFolder",
      folderPath
    );
  },

  /**
   * Remove a folder from trusted list
   * @param folderPath - Path to untrust
   */
  removeTrustedFolder(folderPath) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_removeTrustedFolder",
      folderPath
    );
  },

  /**
   * Check if a folder is trusted
   * @param folderPath - Path to check
   */
  isFolderTrusted(folderPath) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_isFolderTrusted",
      folderPath
    );
  },

  /**
   * Set folders for draft session
   * @param folders - Array of folder paths
   */
  setDraftSessionFolders(folders) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_setDraftSessionFolders",
      folders
    );
  },

  // ─────────────────────────────────────────────
  // MCP Operations
  // ─────────────────────────────────────────────

  /**
   * Configure MCP servers for a session
   * @param sessionId - Session ID
   * @param mcpServers - Server configuration
   */
  setMcpServers(sessionId, mcpServers) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_setMcpServers",
      sessionId,
      mcpServers
    );
  },

  /**
   * Call an MCP tool
   * @param sessionId - Session ID
   * @param serverName - MCP server name
   * @param toolName - Tool to call
   * @param arguments - Tool arguments
   */
  mcpCallTool(sessionId, serverName, toolName, arguments) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_mcpCallTool",
      sessionId,
      serverName,
      toolName,
      arguments
    );
  },

  /**
   * Read an MCP resource
   * @param sessionId - Session ID
   * @param serverName - MCP server name
   * @param resourceUri - Resource URI
   */
  mcpReadResource(sessionId, serverName, resourceUri) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_mcpReadResource",
      sessionId,
      serverName,
      resourceUri
    );
  },

  /**
   * List MCP resources
   * @param sessionId - Session ID
   * @param serverName - MCP server name
   */
  mcpListResources(sessionId, serverName) {
    return ipcRenderer.invoke(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_mcpListResources",
      sessionId,
      serverName
    );
  },

  // ─────────────────────────────────────────────
  // Event Listeners
  // ─────────────────────────────────────────────

  /**
   * Subscribe to session events
   * @param callback - Event handler
   * @returns Unsubscribe function
   */
  onOnEvent(callback) {
    const handler = (event, data) => callback(data);
    ipcRenderer.on(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_onEvent",
      handler
    );
    return () => {
      ipcRenderer.removeListener(
        "$eipc_message$_..._$_LocalAgentModeSessions_$_onEvent",
        handler
      );
    };
  },

  /**
   * Subscribe to tool permission requests
   * @param callback - Permission request handler
   * @returns Unsubscribe function
   */
  onOnToolPermissionRequest(callback) {
    const handler = (event, data) => callback(data);
    ipcRenderer.on(
      "$eipc_message$_..._$_LocalAgentModeSessions_$_onToolPermissionRequest",
      handler
    );
    return () => {
      ipcRenderer.removeListener(
        "$eipc_message$_..._$_LocalAgentModeSessions_$_onToolPermissionRequest",
        handler
      );
    };
  },

  // ─────────────────────────────────────────────
  // Other Operations
  // ─────────────────────────────────────────────

  getSupportedCommands(sessionId) { /* ... */ },
  setFirstPartyConnectors(sessionId, connectors) { /* ... */ },
  setFocusedSession(sessionId) { /* ... */ },
  respondDirectoryServers(sessionId, servers) { /* ... */ },
  openOutputsDir(sessionId) { /* ... */ },
  shareSession(sessionId) { /* ... */ },
  updateSession(sessionId, updates) { /* ... */ },
};

// Registration function - exposes to renderer
function registerLocalAgentModeSessions(contextBridgeApi) {
  if (isContextBridgeAvailable()) {
    contextBridgeApi["claude.web"] = contextBridgeApi["claude.web"] || {};
    contextBridgeApi["claude.web"].LocalAgentModeSessions = LocalAgentModeSessions;
  }
}
```

---

## Settings Configuration Architecture

This section documents how Claude Desktop stores and manages settings, including how to disable Cowork features.

### Two Storage Systems

Claude Desktop uses two parallel storage systems:

#### 1. claude_desktop_config.json (Main Config)

**Location:** `~/.config/Claude/claude_desktop_config.json` (Linux)

```javascript
// Minified: KDe (schema), Xp (path), GDe (read), U0 (write)
// File: index.js lines 71515-71654

const configFileSchema = {
  claudeAiUrl: string,                    // Backend URL override
  globalShortcut: string,                 // Global keyboard shortcut
  mcpServers: object,                     // MCP server configurations
  features: object,                       // Feature flags
  isHardwareAccelerationDisabled: boolean,
  isUsingBuiltInNodeForMcp: boolean,
  isDxtAutoUpdatesEnabled: boolean,
  dxtMaxTotalSizeMB: number,
  preferences: preferencesSchema,         // User preferences (see below)
};

// Path function
function getConfigFilePath() {  // Minified: Xp
  return path.join(app.getPath("userData"), "claude_desktop_config.json");
}

// Read with caching
function readConfigFile() {  // Minified: GDe, with caching via Un
  const configPath = getConfigFilePath();
  if (!fs.existsSync(configPath)) return {};
  return JSON.parse(fs.readFileSync(configPath, "utf-8"));
}

// Write function
async function writeConfigFile(config) {  // Minified: U0
  const configPath = getConfigFilePath();
  await fs.writeFile(configPath, JSON.stringify(config, null, 2));
}
```

#### 2. electron-store (Runtime Data)

**Location:** `~/.config/Claude/config.json`

```javascript
// Minified: na (instance), z5 (wrapper class)
// File: index.js lines 60177, 71333

class ElectronStoreWrapper {  // Minified: z5
  constructor(options) {
    this.store = new ElectronStore(options);
  }
  get(key) { return this.store.get(key); }
  set(key, value) { this.store.set(key, value); }
}

const electronStore = new ElectronStoreWrapper({ configFileMode: 420 });
```

**Stored data:** Locale, theme, window state, yukon-silver-config

### User Preferences Schema

```javascript
// Minified: zue (schema), JDe (defaults)
// File: index.js lines 20612-20638, 71674-71690

const preferencesSchema = {
  menuBarEnabled: boolean,              // Show menu bar (default: true)
  legacyQuickEntryEnabled: boolean,     // Quick entry feature (default: true)
  chromeExtensionEnabled: boolean,      // Chrome extension (default: true)
  quickEntryShortcut: string,           // "double-tap-option" | "off" | etc.
  quickEntryDictationShortcut: string,  // Dictation shortcut
  plushRaccoonEnabled: boolean,         // Dev-only feature (default: false)
  quietPenguinEnabled: boolean,         // Dev-only feature (default: false)
  louderPenguinEnabled: boolean,        // Dev-only feature (default: false)
  plushRaccoonOption1: string,          // Keyboard accelerator
  plushRaccoonOption2: string,          // Keyboard accelerator
  plushRaccoonOption3: string,          // Keyboard accelerator
  sparkleHedgehogAppearance: string,    // UI appearance (default: "default")
  sparkleHedgehogScale: number,         // UI scale (default: 1)
  chillingSlothLocation: string,        // Worktrees location (default: "default")
  secureVmFeaturesEnabled: boolean,     // COWORK/VM FEATURES (default: true)
  localAgentModeTrustedFolders: string[], // Trusted folders list
};

const defaultPreferences = {
  menuBarEnabled: true,
  legacyQuickEntryEnabled: true,
  chromeExtensionEnabled: true,
  quickEntryShortcut: "double-tap-option",
  quickEntryDictationShortcut: "off",
  plushRaccoonEnabled: false,
  quietPenguinEnabled: false,
  louderPenguinEnabled: false,
  plushRaccoonOption1: "off",
  plushRaccoonOption2: "off",
  plushRaccoonOption3: "off",
  sparkleHedgehogAppearance: "default",
  sparkleHedgehogScale: 1,
  chillingSlothLocation: "default",
  secureVmFeaturesEnabled: true,  // <-- Set to false to disable Cowork
  localAgentModeTrustedFolders: [],
};
```

### Reading & Writing Preferences

```javascript
// Minified: gi (get), Ky (set), rB (apply defaults)
// File: index.js lines 71806-71829

/**
 * Get a preference value with defaults applied
 */
function getPreference(key) {  // Minified: gi
  const prefs = getCachedConfig().preferences ?? {};
  return applyDefaultPreferences(prefs)[key];
}

/**
 * Set a preference value and persist to disk
 */
async function setPreference(key, value) {  // Minified: Ky
  // Run pre-change validator if exists
  const validator = preferenceValidators[key];
  if (validator && (await validator(value)) === false) return;

  // Merge with existing preferences
  const newPrefs = { ...(getCachedConfig().preferences ?? {}), [key]: value };

  // Persist to file
  await setConfigValue("preferences", newPrefs);

  // Emit change event
  settingsEventEmitter.emit(key, value);

  // Notify renderer via IPC
  if (mainWindow?.webContents) {
    AppPreferencesDispatcher
      .getDispatcher(mainWindow.webContents)
      ?.dispatchPreferencesChanged(applyDefaultPreferences(newPrefs));
  }
}
```

### YukonSilver Config (VM Settings)

Controls automatic VM bundle downloading and startup.

```javascript
// Minified: YKe (schema), X4 (get), Uge (set), JKe (store)
// File: index.js lines 129376-129385

const yukonSilverConfigSchema = {
  autoDownloadInBackground: boolean,  // Pre-download VM bundle (default: false)
  autoStartOnUserIntent: boolean,     // Auto-start VM for Cowork
  memoryGB: number,                   // RAM allocation for VM
};

// Storage via electron-store with key "data:yukon-silver-config"
const [getYukonSilverConfig, setYukonSilverConfig, yukonSilverStore] = createConfigStore({
  name: "yukon-silver-config",
  schema: yukonSilverConfigSchema,
  allowStale: true,  // Cache between navigations
});

// Usage check (line 173092)
const shouldAutoDownload = getYukonSilverConfig()?.autoDownloadInBackground ?? false;
```

### Enterprise Configuration Override

Enterprise policies override user preferences.

```javascript
// Minified: XDe (keys), eLe (macOS), rLe (Windows)
// File: index.js lines 71870-72001

/**
 * Settings controllable via enterprise policy
 */
const enterpriseSettingKeys = [
  "isDesktopExtensionEnabled",
  "isDesktopExtensionDirectoryEnabled",
  "isDesktopExtensionSignatureRequired",
  "isLocalDevMcpEnabled",
  "isClaudeCodeForDesktopEnabled",
  "secureVmFeaturesEnabled",           // <-- Can disable Cowork enterprise-wide
  "disableAutoUpdates",
  "autoUpdaterEnforcementHours",
  "isDxtEnabled",
  "isDxtDirectoryEnabled",
  "isDxtSignatureRequired",
];

/**
 * Read enterprise config on macOS
 * Location: ~/Library/Preferences/com.anthropic.Claude.plist
 */
async function readMacOSEnterpriseConfig() {  // Minified: eLe
  // Uses CFPreferences API via native addon
}

/**
 * Read enterprise config on Windows
 * Location: HKLM\SOFTWARE\Policies\Claude
 */
async function readWindowsEnterpriseConfig() {  // Minified: rLe
  // Uses Windows Registry API
}
```

### IPC Interface for Preferences

```javascript
// Minified: qfe
// File: index.js lines 44884-44935, 167168-167170

const AppPreferencesDispatcher = {
  // IPC channel: "claude.settings_$_AppPreferences"

  /**
   * Get all preferences with defaults applied
   */
  getPreferences() {
    return applyDefaultPreferences(getCachedConfig().preferences ?? {});
  },

  /**
   * Set a single preference
   */
  async setPreference(key, value) {
    await setPreference(key, value);
  },

  /**
   * Event: Notify renderer of preference changes
   */
  dispatchPreferencesChanged(preferences) {
    // Sent from main process to renderer
  },
};
```

### Disabling Cowork - Methods Summary

#### Method 1: Config File (Recommended)

Edit `~/.config/Claude/claude_desktop_config.json`:

```json
{
  "preferences": {
    "secureVmFeaturesEnabled": false
  }
}
```

#### Method 2: Enterprise Policy (macOS)

Edit `~/Library/Preferences/com.anthropic.Claude.plist`:

```xml
<key>secureVmFeaturesEnabled</key>
<false/>
```

#### Method 3: Enterprise Policy (Windows)

Set in registry at `HKLM\SOFTWARE\Policies\Claude`:

```
secureVmFeaturesEnabled = 0 (DWORD)
```

#### Method 4: Disable Background Download via DevTools

```javascript
// In DevTools console:
await window.claude.ClaudeVM.setYukonSilverConfig({
  autoDownloadInBackground: false,
  autoStartOnUserIntent: true,
  memoryGB: 4
});
```

### Settings Check Flow

```javascript
// File: index.js line 156721

// How secureVmFeaturesEnabled is checked:
if (getPreference("secureVmFeaturesEnabled") === false) {
  // Cowork features are disabled
  return { status: "unsupported", reason: "user_disabled" };
}

// Enterprise check (takes precedence):
if ((await getEnterpriseConfig()).secureVmFeaturesEnabled === false) {
  return { status: "unsupported", reason: "enterprise_disabled" };
}
```

---

## Security Architecture

The Cowork security model implements defense-in-depth with multiple isolation layers. This section documents each layer with actual code from the source.

### 1. Hypervisor Isolation (VZVirtualMachine)

The `@ant/claude-swift` native addon interfaces with Apple's Virtualization Framework to run Claude Code inside an isolated Linux VM.

```javascript
// Minified: Mx, AXe
// File: index.js lines 152262-152422

/**
 * VM startup orchestration - boots the Linux VM via Swift addon
 */
async function startVM(options) {
  // Load the Swift VM addon
  const vmInterface = await getVMInterface();

  if (!vmInterface) {
    throw new Error("Swift VM addon not available");
  }

  // Step 3/4: Start VM via VZVirtualMachine
  const ramGB = options?.memoryGB ?? 8;  // DEFAULT_VM_RAM_GB = 8
  await vmInterface.startVM(bundlePath, ramGB);

  // Step 4/4: Wait for guest agent to connect
  const VM_CONNECTION_TIMEOUT_MS = 120000;
  const GUEST_POLL_INTERVAL_MS = 100;

  while (Date.now() - startTime < VM_CONNECTION_TIMEOUT_MS) {
    if (await vmInterface.isGuestConnected()) {
      // Install SDK in guest
      await vmInterface.installSdk(sdkSubpath, sdkVersion);
      return;
    }
    await sleep(GUEST_POLL_INTERVAL_MS);
  }

  throw new Error("VM connection timeout");
}
```

The Swift addon exposes these VM interface methods:

| Method | Purpose |
|--------|---------|
| `startVM(bundlePath, ramGB)` | Boot the Linux VM with specified memory |
| `stopVM()` | Gracefully shut down the VM |
| `isGuestConnected()` | Check if guest agent is ready |
| `installSdk(subpath, version)` | Install Claude Code SDK in guest |
| `spawn(...)` | Create a process inside the VM |
| `writeStdin(processId, data)` | Send data to process stdin |
| `mountPath(sessionId, path, name, mode)` | Mount host folder into VM |
| `kill(processId, signal)` | Terminate a VM process |
| `addApprovedOauthToken(token)` | Register OAuth token with MITM proxy |
| `setEventCallbacks(...)` | Register stdout/stderr/exit handlers |

---

### 2. Bubblewrap + Seccomp Sandboxing (Inside VM)

The Linux VM uses additional sandboxing internally. Error categorization reveals the tooling:

```javascript
// Minified: (inline in error categorization)
// File: index.js lines 154211-154234

/**
 * Categorizes VM errors for reporting and retry logic.
 * Reveals bubblewrap and seccomp usage inside the VM.
 */
function categorizeVMError(errorText) {
  const output = extractOutput(errorText);

  // Seccomp filter violations
  if (errorText.includes("Killed") && errorText.includes("apply-seccomp"))
    return { category: "seccomp_killed", rawOutput: output };

  // Sandbox dependency issues (reveals tooling)
  if (errorText.includes("Sandbox dependencies are not available") ||
      errorText.includes("ripgrep") ||
      errorText.includes("bubblewrap") ||
      errorText.includes("socat"))
    return { category: "sandbox_deps_missing", rawOutput: output };

  // Mount failures
  if (errorText.includes("was not found") && errorText.includes("/sessions/"))
    return { category: "mount_not_found", rawOutput: output };

  if (errorText.includes("failed to unmount") ||
      errorText.includes("device or resource busy"))
    return { category: "mount_busy", rawOutput: output };

  // VM disconnection
  if (errorText.includes("Disconnected from guest") ||
      errorText.includes("disconnected unexpectedly"))
    return { category: "vm_disconnected", rawOutput: output };

  // ... additional categories
}
```

The sandbox stack inside the VM:
- **Bubblewrap (bwrap)** - Linux namespace isolation
- **Seccomp filters** - Syscall allowlisting
- **Socat** - Socket relay for controlled network access
- **Ripgrep** - Used by Claude Code for file searching

---

### 3. Path Traversal Detection & Blocked Extensions

Every file access is validated before being allowed.

```javascript
// Minified: qbe (blocked extensions), Wst (validateVMPathAccess)
// File: index.js lines 173457-173496

/**
 * Blocked binary file extensions - prevents execution of dangerous files
 */
const blockedBinaryExtensions = [
  ".exe", ".com", ".msi", ".bin",
  ".app", ".dmg", ".pkg", ".jar"
];

/**
 * Blocked executable script extensions (for openLocalFile operations)
 */
const blockedExecutableExtensions = [
  ".sh", ".bash", ".zsh", ".command",
  ".bat", ".cmd", ".ps1", ".vb", ".jnlp",
  ".js", ".pl", ".py", ".rb",
  ".scpt", ".scptd", ".applescript", ".workflow"
];

/**
 * Validates VM path access - checks session ownership, traversal, and extensions.
 *
 * @param sessionId - Must start with "local_"
 * @param vmPath - Path in format /sessions/<name>/mnt/<mount>/<subpath>
 * @throws Error with codes: INVALID_SESSION, INVALID_PATH, PATH_TRAVERSAL, BLOCKED_EXTENSION
 */
function validateVMPathAccess(sessionId, vmPath) {
  // Must be a local session
  if (!sessionId.startsWith("local_"))
    throw new Error("Invalid session: " + sessionId, "INVALID_SESSION");

  // Extract session name from path
  const sessionName = extractSessionName(vmPath);  // Minified: Hst
  if (!sessionName)
    throw new Error("Invalid VM path format: " + vmPath, "INVALID_PATH");

  // Verify session ownership
  const expectedName = getVMProcessName(sessionId);
  if (!expectedName || expectedName !== sessionName)
    throw new Error("Session mismatch for path: " + vmPath, "INVALID_SESSION");

  // Normalize path and check for traversal
  const normalized = path.posix.normalize(vmPath);
  const expectedPrefix = `/sessions/${sessionName}/`;

  if (!normalized.startsWith(expectedPrefix)) {
    logger.warn(`validateVMPathAccess: path traversal detected: ${vmPath}`);
    throw new Error("Path traversal detected: " + vmPath, "PATH_TRAVERSAL");
  }

  // Check file extension against blocklist
  const ext = path.extname(vmPath).toLowerCase();
  if (blockedBinaryExtensions.includes(ext)) {
    logger.warn(`validateVMPathAccess: blocked binary file type ${ext}: ${vmPath}`);
    throw new Error("Blocked file type: " + ext, "BLOCKED_EXTENSION");
  }

  return { vmProcessName: sessionName, normalizedPath: normalized };
}

/**
 * Archive path safety check - prevents path traversal in archives
 */
function isPathSafe(filePath) {
  if (filePath.includes("..")) return false;
  const normalized = path.normalize(filePath);
  return !path.isAbsolute(normalized);
}
```

The `extractSessionName` function (minified as `Hst`):

```javascript
// Minified: Hst
// File: index.js lines 173458-173464

function extractSessionName(vmPath) {
  if (!vmPath.startsWith("/sessions/")) return null;

  const afterSessions = vmPath.slice(10);  // Remove "/sessions/"
  const slashIndex = afterSessions.indexOf("/");
  const sessionName = slashIndex === -1 ? afterSessions : afterSessions.slice(0, slashIndex);

  // Reject traversal attempts
  if (!sessionName || sessionName === ".." || sessionName === ".") return null;

  return sessionName;
}
```

---

### 4. OAuth MITM Proxy for Token Approval

The VM runs a man-in-the-middle proxy that intercepts API requests and validates OAuth tokens before allowing them through.

```javascript
// File: index.js lines 151984-152007

/**
 * Spawns a process in the VM with OAuth token pre-approval.
 * Tokens are registered with the MITM proxy before the process starts.
 */
async function spawnInVM(process, processId, command, args, cwd, envVars, config) {
  const vmInterface = await getVMInterface();

  if (!vmInterface) {
    process.setError(new Error("Swift VM addon not available"), "lam_vm_process_spawn_failed");
    return;
  }

  try {
    // Register OAuth token with MITM proxy BEFORE spawning
    const oauthToken = envVars.CLAUDE_CODE_OAUTH_TOKEN;
    if (oauthToken) {
      await vmInterface.addApprovedOauthToken(oauthToken);
      logger.info(`[Spawn:vm] id=${processId} OAuth token approved with MITM proxy`);
    }

    // Now spawn the process - it can only use pre-approved tokens
    await vmInterface.spawn(
      processId,
      config.processName,
      command,
      args,
      cwd,
      Object.keys(envVars).length > 0 ? envVars : undefined,
      config.additionalMounts,
      config.isResume,
      config.allowedDomains,
      config.sharedCwdPath,
    );

    await process.confirmSpawn();
    // ...
  } catch (error) {
    // ...
  }
}
```

The session environment setup passes the OAuth token:

```javascript
// File: index.js lines 149675-149681

function createSessionEnv(config) {
  return {
    CLAUDE_CODE_ENTRYPOINT: "claude-desktop",
    ANTHROPIC_BASE_URL: config.apiHost,
    ANTHROPIC_API_KEY: "",  // Empty - uses OAuth instead
    CLAUDE_CODE_OAUTH_TOKEN: config.oauthToken,  // Token for MITM approval
    DISABLE_AUTOUPDATER: "1",
    CLAUDE_CODE_ENABLE_ASK_USER_QUESTION_TOOL: "true",
  };
}
```

---

### 5. Network Egress Allow-List (allowedDomains)

The VM restricts outbound network connections to a whitelist of approved domains.

```javascript
// File: index.js lines 151941-152000

/**
 * Creates a spawn function with network restrictions.
 * allowedDomains is passed to the VM and enforced at the network layer.
 */
function createSpawnFunction(config) {
  const mountCount = Object.keys(config.additionalMounts).length;
  const mountNames = Object.keys(config.additionalMounts).join(", ");

  logger.info(
    `[Spawn:config] Creating spawn function for process=${config.processName}, ` +
    `isResume=${config.isResume}, mounts=${mountCount} (${mountNames}), ` +
    `allowedDomains=${config.allowedDomains?.length ?? 0}`
  );

  return (spawnOptions) => {
    // ... spawn logic passes allowedDomains to VM
    await vmInterface.spawn(
      processId,
      config.processName,
      command,
      args,
      cwd,
      envVars,
      config.additionalMounts,
      config.isResume,
      config.allowedDomains,  // Network egress whitelist
      config.sharedCwdPath,
    );
  };
}
```

The `egressAllowedDomains` are validated in the IPC layer:

```javascript
// File: index.js lines 5780-5786

// Session start validation includes egressAllowedDomains
(typeof t.egressAllowedDomains < "u" &&
  !(
    Array.isArray(t.egressAllowedDomains) &&
    t.egressAllowedDomains.every((e) => typeof e == "string")
  ))
```

And passed through from the session configuration:

```javascript
// File: index.js lines 155438-155443

// Session creation passes domains to spawn config
{
  // ... other config
  isResume: !!sessionOptions.resume,
  allowedDomains: sessionInfo.egressAllowedDomains,
  sharedCwdPath: sharedCwdPath,
}
```

Plugin/marketplace operations also enforce domain restrictions:

```javascript
// File: index.js (CustomPlugins interface)

// All plugin operations require egressAllowedDomains parameter
await e.addMarketplace(name, url, egressAllowedDomains);
await e.installPlugin(pluginId, egressAllowedDomains);
await e.listMarketplaces(egressAllowedDomains);
await e.listInstalledPlugins(egressAllowedDomains);
await e.listAvailablePlugins(egressAllowedDomains);
```

---

### Security Layer Summary

| Layer | Technology | Protection Level |
|-------|------------|------------------|
| **1. Hypervisor** | VZVirtualMachine | Hardware-enforced isolation from host |
| **2. Namespace isolation** | Bubblewrap | Process/mount/network namespaces inside VM |
| **3. Syscall filtering** | Seccomp | Kernel-level syscall whitelist |
| **4. Path validation** | Custom JS | Blocks traversal, dangerous extensions |
| **5. OAuth proxy** | MITM in VM | Token approval before API access |
| **6. Network egress** | allowedDomains | Whitelist-only outbound connections |

---

## Symbol Reference

Quick reference for minified symbols → meaningful names:

### VM Module & Lifecycle

| Minified | Meaning | Location |
|----------|---------|----------|
| `ff` | swiftModuleCache | index.js:151513 |
| `Vw` | swiftModulePromise | index.js:151514 |
| `p0e` | loadSwiftModule() | index.js:151515 |
| `li` | getVMInterface() | index.js:151534 |
| `eXe` | getSwiftModule() | index.js:151541 |
| `ii` | notificationsModuleCache | index.js:163988 |
| `Ht` | quickEntryModuleCache | index.js:166528 |
| `Unt` | loadNotificationsModule() | index.js:163988 |
| `tit` | initializeQuickEntry() | index.js:166528 |

### VM Process Management

| Minified | Meaning | Location |
|----------|---------|----------|
| `tXe` | VMProcess class | index.js:151555 |
| `dc` | activeProcesses (Map) | index.js:151551 |
| `zw` | activeProcessCount | index.js:151552 |
| `sF` | vmEventEmitter | index.js:151553 |
| `rXe` | initializeVMEventCallbacks() | index.js:151690 |
| `gXe` | createSpawnFunction() | index.js:151941 |
| `yXe` | spawnInVM() | index.js:151974 |

### VM Startup & Shutdown

| Minified | Meaning | Location |
|----------|---------|----------|
| `AXe` | startVMInternal() | index.js:152262 |
| `Mx` | startVM() | index.js:152422 |
| `kg` | vmStartupPromise | index.js:152423 |
| `h_` | isGuestConnected() | index.js:152017 |
| `iae` | VM_CONNECTION_TIMEOUT_MS (120000) | index.js |
| `TXe` | DEFAULT_VM_RAM_GB (8) | index.js |
| `$Xe` | GUEST_POLL_INTERVAL_MS (100) | index.js |
| `mXe` | startHeartbeat() | index.js:152362 |
| `vXe` | stopHeartbeat() | index.js:152305 |

### VM Bundle Management

| Minified | Meaning | Location |
|----------|---------|----------|
| `CXe` | downloadVMBundle() | index.js:152118 |
| `g0e` | getBundleStatus() | index.js:152090 |
| `m_` | getVMBundlePath() | index.js:152031 |
| `ci` | bundleVersion | index.js:152088 |
| `gy` | bundleChecksum | index.js:152176 |
| `wXe` | VM_BUNDLES_DIR ("vm_bundles") | index.js:152031 |
| `SXe` | BUNDLE_FILENAME ("claudevm.bundle") | index.js:152032 |

### Path Translation & Security

| Minified | Meaning | Location |
|----------|---------|----------|
| `Og` | translateVMPathToHost() | index.js:152669 |
| `DXe` | (path translation helper) | index.js:152669 |
| `C0e` | resolveMountToDirectory() | index.js:152760 |
| `qbe` | blockedBinaryExtensions | index.js:173457 |
| `Kst` | blockedExecutableExtensions | index.js:173498 |
| `Wst` | validateVMPathAccess() | index.js:173465 |
| `Hst` | extractSessionName() | index.js:173458 |
| `b8` | isPathSafe() | index.js:127669 |

### Settings & Configuration

| Minified | Meaning | Location |
|----------|---------|----------|
| `Xp` | getConfigFilePath() | index.js:71527 |
| `GDe` | readConfigFile() | index.js:71530 |
| `U0` | writeConfigFile() | index.js:71601 |
| `KDe` | configFileSchema | index.js:71515 |
| `zue` | preferencesSchema | index.js:20612 |
| `JDe` | defaultPreferences | index.js:71674 |
| `gi` | getPreference() | index.js:71806 |
| `Ky` | setPreference() | index.js:71817 |
| `zd` | setConfigValue() | index.js:71652 |
| `Un` | getCachedConfig() | index.js:71530 |
| `rB` | applyDefaultPreferences() | index.js:71806 |
| `bf` | settingsEventEmitter | index.js:71817 |

### YukonSilver (VM Config)

| Minified | Meaning | Location |
|----------|---------|----------|
| `YKe` | yukonSilverConfigSchema | index.js:129376 |
| `X4` | getYukonSilverConfig() | index.js:129376 |
| `Uge` | setYukonSilverConfig() | index.js:129376 |
| `JKe` | yukonSilverConfigStore | index.js:129376 |
| `VKe` | ConfigStore class | index.js:129296 |
| `Ax` | createConfigStore() | index.js:129296 |
| `wj` | checkYukonSilverSupport() | index.js:156704 |

### Enterprise Configuration

| Minified | Meaning | Location |
|----------|---------|----------|
| `XDe` | enterpriseSettingKeys | index.js:71870 |
| `eLe` | readMacOSEnterpriseConfig() | index.js:71925 |
| `rLe` | readWindowsEnterpriseConfig() | index.js:71999 |

### IPC Dispatchers

| Minified | Meaning | Location |
|----------|---------|----------|
| `qfe` | AppPreferencesDispatcher | index.js:44884 |
| `It` | LocalAgentModeSessions | mainView.js:3778 |
| `na` | electronStore instance | index.js:71333 |
| `z5` | ElectronStoreWrapper class | index.js:60177 |

### Logging & Telemetry

| Minified | Meaning | Location |
|----------|---------|----------|
| `Me` | logger (VM context) | index.js:151510 |
| `H` | logger (general) | various |
| `pr` | trackEvent() | various |
| `Ci` | Error constructor with code | various |

### Menu & UI

| Minified | Meaning | Location |
|----------|---------|----------|
| `krt` | buildAppFeaturesMenu() | index.js:159033 |
| `P$e` | defaultFeatureFlags | index.js:20670 |
| `I$e` | featureFlagLabels | index.js:20670 |
| `Dst` | getCoworkVmMenuItem() | index.js:173117 |

---

## Heartbeat Monitoring

The VM uses a bidirectional heartbeat protocol to detect failures and automatically restart.

```javascript
// Minified: mXe (startHeartbeat), vXe (stopHeartbeat)
// File: index.js lines 152362-152375

const NETWORK_TIMEOUT_MS = 30000;  // tae = 3e4

/**
 * Heartbeat protocol types
 */
const HeartbeatMessageType = {
  heartbeat_request: "heartbeat_request",
  heartbeat_response: "heartbeat_response",
};

/**
 * Starts heartbeat monitoring with automatic restart on failure.
 * Called after VM startup completes successfully.
 */
function startHeartbeat(config) {
  // config.onRestart is called when heartbeat fails
  return {
    onRestart: async () => {
      logger.info("[VM:heartbeat] Heartbeat failure detected, restarting VM...");
      try {
        await vmInterface.stopVM();
        clearVMInstanceId();
        await startVM(options);
        logger.info("[VM:heartbeat] VM restart completed successfully");
      } catch (error) {
        logger.error("[VM:heartbeat] VM restart failed:", error);
      }
    },
  };
}

/**
 * Checks if a heartbeat process is running
 */
function isHeartbeatAlive() {
  return isProcessRunning("__heartbeat_ping__");
}
```

Telemetry event on failure:
```javascript
trackEvent("lam_vm_heartbeat_failure", {
  vm_instance_id: getVMInstanceId() ?? "unknown",
  // ... additional context
});
```

---

## VM Bundle Management

### Bundle Structure

```javascript
// Minified: wXe, SXe, m_
// File: index.js lines 152031-152032

const VM_BUNDLES_DIR = "vm_bundles";        // wXe
const BUNDLE_FILENAME = "claudevm.bundle";  // SXe

// Bundle contents:
// - rootfs.img (compressed as .zst during download)
// - Version tracked in .{filename}.origin files
```

### Download with Checksum Verification

```javascript
// Minified: CXe
// File: index.js lines 152118-152200

/**
 * Downloads VM bundle with zstd decompression and SHA256 verification.
 * Attempts warm bundle promotion before full download.
 */
async function downloadVMBundle(progressCallback) {
  const bundleDir = getVMBundlePath();
  await fs.mkdir(bundleDir, { recursive: true });

  let isFreshDownload = false;
  let downloadSizeBytes = -1;

  // Step 1: Try warm bundle promotion (pre-downloaded in background)
  if (await promoteWarmBundle(bundleDir)) {
    logger.info("[downloadVM] Warm bundle promoted successfully");
    return false;  // Not a fresh download
  }

  isFreshDownload = true;
  const startTime = Date.now();

  // Step 2: Download with zstd decompression
  const downloadUrl = `${baseUrl}/rootfs.img.zst`;
  const { sha256, bytesWritten } = await downloadWithTransform({
    url: downloadUrl,
    outputPath: path.join(bundleDir, "rootfs.img"),
    computeHash: true,
    transform: zstd.createZstdDecompress(),
    onProgress: progressCallback,
  });

  downloadSizeBytes = bytesWritten;

  // Step 3: Verify checksum
  if (sha256 !== expectedChecksum) {
    throw new Error("Checksum mismatch for VM bundle");
  }

  // Step 4: Write version marker
  await fs.writeFile(versionFile, bundleVersion);

  trackEvent("lam_vm_bundle_download_completed", {
    bundle_version: bundleVersion,
    duration_ms: Date.now() - startTime,
    download_size_bytes: downloadSizeBytes,
    was_warm_promoted: false,
  });

  return true;  // Fresh download
}
```

### Warm Bundle System

Background downloads allow instant VM starts:

```javascript
// File: index.js lines 172931-173041

/**
 * Warm bundle events for background pre-downloading
 */
// lam_vm_warm_download_started - Background download initiated
// lam_vm_warm_download_completed - Background download finished
// lam_vm_warm_download_failed - Background download error
// lam_vm_warm_promote_completed - Warm bundle moved to active
// lam_vm_warm_promote_failed - Promotion failed, fallback to download
```

---

## Knowledge Base Integration

Multiple knowledge bases can be mounted into a single session.

```javascript
// File: index.js lines 155507-155569

/**
 * Session methods for knowledge base management
 */
const sessionMethods = {
  /**
   * Mount a knowledge base into the session
   * @param kbId - Knowledge base identifier
   * @param mountName - Name for the mount point
   * @param mountPath - Full path in VM
   */
  addMountedKnowledgeBase(kbId, mountName, mountPath) {
    const session = this.sessions.get(sessionId);
    if (!session) return;

    // Track mounted KBs
    session.mountedKnowledgeBases = session.mountedKnowledgeBases || [];
    if (!session.mountedKnowledgeBases.includes(kbId)) {
      session.mountedKnowledgeBases.push(kbId);
    }

    // Track mount paths
    session.knowledgeBaseMountPaths = session.knowledgeBaseMountPaths || new Map();
    session.knowledgeBaseMountPaths.set(mountName, mountPath);

    this.saveSession(session);
  },

  /**
   * Get path for a mounted knowledge base
   */
  getKnowledgeBasePath(kbId) {
    // Returns path like /sessions/<name>/mnt/.knowledge/<kbName>/
  },
};
```

Knowledge base content is injected into the system prompt:

```javascript
// File: index.js lines 154311-154337

// KB mount path template
const kbMountPath = `/mnt/.knowledge/${mountName}/`;

// Telemetry
trackEvent("local_kb_session_mounted", {
  session_id: sessionId,
  kb_id: kbId,
  file_count: stats.fileCount,
  total_size_bytes: stats.totalSize,
});
```

---

## File System Watching

The app monitors host directories for file changes:

```javascript
// Minified: FileSystemWatcher class
// File: index.js lines 152950-153049

class FileSystemWatcher extends EventEmitter {
  constructor() {
    this.watchers = new Map();      // sessionId -> FSWatcher
    this.knownFiles = new Map();    // sessionId -> Set<filename>
  }

  /**
   * Start watching a directory for file changes
   */
  startWatching(sessionId, dirPath) {
    const knownFiles = new Set();

    // Initial scan
    for (const file of fs.readdirSync(dirPath)) {
      if (!file.startsWith(".")) {  // Skip hidden files
        knownFiles.add(file);
      }
    }

    this.knownFiles.set(sessionId, knownFiles);

    // Watch for changes
    const watcher = fs.watch(dirPath, { recursive: false }, (eventType, filename) => {
      if (!filename || filename.startsWith(".")) return;

      const fullPath = path.join(dirPath, filename);
      const exists = fs.existsSync(fullPath);
      const isFile = exists && fs.statSync(fullPath).isFile();

      if (exists && isFile && !knownFiles.has(filename)) {
        // New file created
        knownFiles.add(filename);
        this.emit("fsEvent", {
          type: "fs_file_created",
          sessionId,
          hostPath: fullPath,
          fileName: filename,
          timestamp: Date.now(),
        });
      } else if (!exists && knownFiles.has(filename)) {
        // File deleted
        knownFiles.delete(filename);
        this.emit("fsEvent", {
          type: "fs_file_deleted",
          sessionId,
          fileName: filename,
          timestamp: Date.now(),
        });
      }
    });

    this.watchers.set(sessionId, watcher);
  }

  stopWatching(sessionId) {
    const watcher = this.watchers.get(sessionId);
    if (watcher) {
      watcher.close();
      this.watchers.delete(sessionId);
      this.knownFiles.delete(sessionId);
    }
  }
}
```

---

## ClaudeVM Web IPC Interface

Additional IPC methods exposed via contextBridge (separate from LocalAgentModeSessions):

```javascript
// File: index.js lines 8419-8561, mainView.js lines 468-530

const ClaudeVM = {
  // ─────────────────────────────────────────────
  // Bundle Management
  // ─────────────────────────────────────────────

  /**
   * Download VM bundle (manual trigger)
   */
  download() {
    return ipcRenderer.invoke("$eipc_message$_..._$_ClaudeVM_$_download");
  },

  /**
   * Get current download status
   * @returns { status: "idle" | "downloading" | "ready" | "error", progress?: number }
   */
  getDownloadStatus() {
    return ipcRenderer.invoke("$eipc_message$_..._$_ClaudeVM_$_getDownloadStatus");
  },

  /**
   * Delete bundle and trigger reinstall
   */
  deleteAndReinstall() {
    return ipcRenderer.invoke("$eipc_message$_..._$_ClaudeVM_$_deleteAndReinstall");
  },

  // ─────────────────────────────────────────────
  // VM Control
  // ─────────────────────────────────────────────

  /**
   * Start VM with optional memory configuration
   * @param options - { memoryGB?: number }
   */
  startVM(options) {
    return ipcRenderer.invoke("$eipc_message$_..._$_ClaudeVM_$_startVM", options);
  },

  /**
   * Get current running status
   * @returns { running: boolean, instanceId?: string }
   */
  getRunningStatus() {
    return ipcRenderer.invoke("$eipc_message$_..._$_ClaudeVM_$_getRunningStatus");
  },

  // ─────────────────────────────────────────────
  // YukonSilver Configuration (VM Settings)
  // ─────────────────────────────────────────────

  /**
   * Get current YukonSilver configuration
   * @returns { autoDownloadInBackground: boolean, autoStartOnUserIntent: boolean, memoryGB: number }
   */
  getYukonSilverConfig() {
    return ipcRenderer.invoke("$eipc_message$_..._$_ClaudeVM_$_getYukonSilverConfig");
  },

  /**
   * Configure YukonSilver settings
   * @param config - { autoDownloadInBackground?: boolean, autoStartOnUserIntent?: boolean, memoryGB?: number }
   *
   * Example to disable background downloading:
   *   await window.claude.ClaudeVM.setYukonSilverConfig({
   *     autoDownloadInBackground: false,
   *     autoStartOnUserIntent: true,
   *     memoryGB: 4
   *   });
   */
  setYukonSilverConfig(config) {
    return ipcRenderer.invoke("$eipc_message$_..._$_ClaudeVM_$_setYukonSilverConfig", config);
  },

  // ─────────────────────────────────────────────
  // Event Subscriptions
  // ─────────────────────────────────────────────

  onDownloadProgress(callback) {
    // Fires with progress percentage during download
    const handler = (event, progress) => callback(progress);
    ipcRenderer.on("$eipc_message$_..._$_ClaudeVM_$_onDownloadProgress", handler);
    return () => ipcRenderer.removeListener("$eipc_message$_..._$_ClaudeVM_$_onDownloadProgress", handler);
  },

  onDownloadStatusChanged(callback) {
    // Fires when download status transitions (idle -> downloading -> ready)
    const handler = (event, status) => callback(status);
    ipcRenderer.on("$eipc_message$_..._$_ClaudeVM_$_onDownloadStatusChanged", handler);
    return () => ipcRenderer.removeListener("$eipc_message$_..._$_ClaudeVM_$_onDownloadStatusChanged", handler);
  },

  onRunningStatusChanged(callback) {
    // Fires when VM starts/stops
    const handler = (event, status) => callback(status);
    ipcRenderer.on("$eipc_message$_..._$_ClaudeVM_$_onRunningStatusChanged", handler);
    return () => ipcRenderer.removeListener("$eipc_message$_..._$_ClaudeVM_$_onRunningStatusChanged", handler);
  },

  onStartupError(callback) {
    // Fires when VM startup fails with error message
    const handler = (event, error) => callback(error);
    ipcRenderer.on("$eipc_message$_..._$_ClaudeVM_$_onStartupError", handler);
    return () => ipcRenderer.removeListener("$eipc_message$_..._$_ClaudeVM_$_onStartupError", handler);
  },
};
```

### Developer Console Examples

```javascript
// Check current VM config
await window.claude.ClaudeVM.getYukonSilverConfig()
// Returns: { autoDownloadInBackground: false, autoStartOnUserIntent: true, memoryGB: 8 }

// Disable background VM bundle downloading (~2GB)
await window.claude.ClaudeVM.setYukonSilverConfig({
  autoDownloadInBackground: false,
  autoStartOnUserIntent: true,
  memoryGB: 4
});

// Check download status
await window.claude.ClaudeVM.getDownloadStatus()
// Returns: { status: "idle" | "downloading" | "ready" | "error", progress?: number }

// Check if VM is running
await window.claude.ClaudeVM.getRunningStatus()
// Returns: { running: boolean, instanceId?: string }

// Delete VM bundle and reinstall (via Developer menu)
await window.claude.ClaudeVM.deleteAndReinstall()
```

---

## Idle Session Notifications

Native notifications when agent is waiting for user input:

```javascript
// File: index.js lines 164190-164231

/**
 * Shows native notification for idle session
 * Uses Swift addon on macOS, falls back to Electron Notification
 */
async function showIdleNotificationForSession(sessionId, title, onClick) {
  const swiftModule = await getSwiftModule();

  if (swiftModule?.notifications) {
    // Native macOS notification via Swift addon
    swiftModule.notifications.show({
      title,
      sessionId,
      onClick,
    });
  } else {
    // Fallback to Electron notification
    const notification = new Notification({
      title,
      body: "Click to return to session",
    });
    notification.on("click", onClick);
    notification.show();
  }
}

function closeIdleNotificationForSession(sessionId) {
  // Dismiss notification when session resumes
}
```

---

## Telemetry Events Reference

Complete list of `lam_*` analytics events:

### VM Lifecycle
| Event | Description |
|-------|-------------|
| `lam_vm_startup_completed` | VM boot + guest connection successful |
| `lam_vm_startup_failed` | VM failed to start (with error_type) |
| `lam_vm_shutdown_completed` | Clean VM shutdown |
| `lam_vm_shutdown_failed` | Shutdown error |
| `lam_vm_heartbeat_failure` | Heartbeat check failed |

### Process Events
| Event | Description |
|-------|-------------|
| `lam_vm_process_spawned` | Process started in VM |
| `lam_vm_process_spawn_failed` | Process failed to start |
| `lam_vm_process_exited` | Process terminated (with exit_code, duration_ms) |
| `lam_vm_runtime_error` | Error during process execution |

### Bundle Events
| Event | Description |
|-------|-------------|
| `lam_vm_bundle_download_completed` | Bundle download finished |
| `lam_vm_bundle_download_failed` | Bundle download error |
| `lam_vm_warm_download_started` | Background download initiated |
| `lam_vm_warm_download_completed` | Background download finished |
| `lam_vm_warm_download_failed` | Background download error |
| `lam_vm_warm_promote_completed` | Warm bundle activated |
| `lam_vm_warm_promote_failed` | Warm bundle promotion error |

### Session Events
| Event | Description |
|-------|-------------|
| `lam_session_start_attempted` | Session creation initiated |
| `lam_session_initialization_failed` | Session setup error |
| `lam_session_stopped` | Session manually stopped |
| `lam_session_archived` | Session archived |
| `lam_session_timeout` | Session timed out |
| `lam_session_app_quit` | Session ended due to app quit |
| `lam_session_step_completed` | Session step finished |
| `lam_session_turn_completed` | Conversation turn finished |
| `lam_session_query_error` | Query execution error |

### Message Events
| Event | Description |
|-------|-------------|
| `lam_message_cycle_outcome` | Message processing result |

### Other Events
| Event | Description |
|-------|-------------|
| `lam_project_sync_failed` | Project context sync error |
| `local_kb_session_mounted` | Knowledge base mounted |

---

## Internal Feature Codenames

Anthropic uses animal-themed codenames for internal features. Each has a status check returning `supported`, `unsupported` (with reason), or `unavailable`.

### yukonSilver - VM Platform Gate

```javascript
// Minified: wj
// File: index.js lines 156704-156711

function checkYukonSilverSupport() {
  return process.platform !== "darwin"
    ? { status: "unsupported", reason: "Darwin only" }
    : process.arch !== "arm64"
      ? { status: "unsupported", reason: "arm64 only" }
      : getMacOSVersion().major < 14
        ? { status: "unsupported", reason: "minimum macOS version not met" }
        : { status: "supported" };
}

// Async version adds enterprise/user overrides
async function checkYukonSilverSupportAsync() {
  const platformCheck = checkYukonSilverSupport();
  if (platformCheck.status !== "supported") return platformCheck;

  if ((await getEnterpriseConfig()).secureVmFeaturesEnabled === false)
    return { status: "unsupported", reason: "enterprise_disabled" };

  if (getSetting("secureVmFeaturesEnabled") === false)
    return { status: "unsupported", reason: "user_disabled" };

  return { status: "supported" };
}
```

Config schema (`yukon-silver-config`):
```javascript
{
  autoDownloadInBackground: boolean,  // Pre-download VM bundle
  autoStartOnUserIntent: boolean,     // Auto-start VM for Cowork
  memoryGB: number,                   // RAM allocation
}
```

### Other Codenames

| Codename | Purpose | Evidence |
|----------|---------|----------|
| `yukonSilverGems` | Dependent on yukonSilver | Just checks if VM supported (NOT Google Gemini) |
| `chillingSloth` | Git worktrees | `chillingSlothLocation` → `~/.claude-worktrees` |
| `chillingSlothEnterprise` | Enterprise worktrees | Async check with enterprise config |
| `chillingSlothFeat` | Worktrees feature flag | Platform check (darwin only) |
| `chillingSlothLocal` | Local worktrees | Blocks Windows ARM64 |
| `sparkleHedgehog` | UI prototype | Has `appearance` and `scale` settings |
| `plushRaccoon` | Keyboard shortcuts | 3 configurable accelerators, dev-only |
| `quietPenguin` | Unknown | macOS-only, dev-only |
| `louderPenguin` | Unknown | macOS-only, dev-only |
| `midnightOwl` | Unknown prototype | In Swift addon: `midnightOwl.setEnabled()` |
| `desktopTopBar` | Top bar UI | Hardcoded: `feature_flag_disabled` |
| `dxt` | Browser extensions | "Allows loading browser extensions in the app" |
| `nativeQuickEntry` | Quick entry popup | macOS 13+ required |
| `quickEntryDictation` | Voice dictation | macOS-only |

### Development vs Production

```javascript
// Minified: IM
// File: index.js line 156637

function devOnlyFeature(checkFn) {
  return app.isPackaged
    ? { status: "unavailable" }  // Production: disabled
    : checkFn();                  // Development: run check
}

// Usage:
plushRaccoon: devOnlyFeature(() => someCheck),
quietPenguin: devOnlyFeature(checkMacOS),
louderPenguin: devOnlyFeature(checkMacOS),
```

---

## VM Image Analysis

This section documents the contents of Anthropic's Linux VM rootfs image, obtained from their CDN.

### Download URLs

Anthropic hosts VM images for both architectures:

```
ARM64: https://downloads.claude.ai/vms/linux/arm64/{commit_hash}/rootfs.img.zst
x64:   https://downloads.claude.ai/vms/linux/x64/{commit_hash}/rootfs.img.zst
```

Example (January 2026):
```
https://downloads.claude.ai/vms/linux/x64/300d0efc7ac6b45a8384f2a36ff5dbe0d1f89baf/rootfs.img.zst
```

### Image Structure

```
Compressed size:   2.3 GB (zstd)
Decompressed size: 10 GB
Partition table:   GPT

Device        Start      End  Sectors  Size Type
rootfs.img1  227328 20971486 20744159  9.9G Linux filesystem
rootfs.img14   2048    10239     8192    4M BIOS boot
rootfs.img15  10240   227327   217088  106M EFI System
```

### Base System

```
PRETTY_NAME="Ubuntu 22.04.5 LTS"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
```

File system layout:
```
/home/ubuntu/          - Default user home
/workspace/            - Empty mount point for sessions
/usr/local/bin/        - SDK daemon and tools
/usr/local/lib/node_modules_global/  - Global npm packages
```

### SDK Daemon

The SDK daemon (`/usr/local/bin/sdk-daemon`) is a Go binary that bridges the host and VM via vsock.

**Systemd service** (`/etc/systemd/system/sdk-daemon.service`):
```ini
[Unit]
Description=Claude SDK Daemon - vsock RPC bridge for process management
After=network.target local-fs.target systemd-udev-settle.service
Wants=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/sdk-daemon
Restart=always
RestartSec=3
User=root
Group=root
Environment=HOME=/root

[Install]
WantedBy=multi-user.target
```

**Dependencies** (from binary strings):
```
github.com/mdlayher/vsock   v1.2.1  - Virtio socket communication
github.com/elazarl/goproxy  v1.7.2  - HTTP MITM proxy (for OAuth)
github.com/mdlayher/socket  v0.4.1  - Low-level socket handling
```

**RPC Methods** (extracted from binary):
```
Spawn          - Create a process in the VM
Kill           - Terminate a process
KillAll        - Terminate all processes
Mount          - Mount a host directory
Stdin          - Write to process stdin
WriteStdin     - Alias for Stdin
ReadFile       - Read file from VM
InstallToTrustStore - Install certificates
```

**RPC Data Types**:
```go
type SpawnParams struct { ... }
type SpawnResult struct { ... }
type MountConfig struct { ... }
type StdinParams struct { ... }
type KillParams struct { ... }
type ReadFileParams struct { ... }
type ReadFileResult struct { ... }
```

### Sandbox Runtime Package

Location: `/usr/local/lib/node_modules_global/lib/node_modules/@anthropic-ai/sandbox-runtime/`

**Package info** (`package.json`):
```json
{
  "name": "@anthropic-ai/sandbox-runtime",
  "version": "0.0.28",
  "description": "Anthropic Sandbox Runtime (ASRT) - A general-purpose tool for wrapping security boundaries around arbitrary processes",
  "bin": {
    "srt": "dist/cli.js"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}
```

**Available on npm**: https://www.npmjs.com/package/@anthropic-ai/sandbox-runtime

**Key dependencies**:
```json
{
  "@pondwader/socks5-server": "^1.0.10",
  "commander": "^12.1.0",
  "lodash-es": "^4.17.21",
  "shell-quote": "^1.8.3",
  "zod": "^3.24.1"
}
```

**Sandbox mechanisms**:
- Linux: bubblewrap (namespace isolation)
- macOS: sandbox-exec
- Network: SOCKS5 proxy for domain filtering
- Syscalls: seccomp BPF filters

### Seccomp Filters

Pre-compiled filters in `vendor/seccomp/`:
```
vendor/seccomp/arm64/apply-seccomp   (542 KB)
vendor/seccomp/arm64/unix-block.bpf  (88 bytes)
vendor/seccomp/x64/apply-seccomp     (828 KB)
vendor/seccomp/x64/unix-block.bpf    (104 bytes)
```

**Filter source** (`vendor/seccomp-src/seccomp-unix-block.c`):
```c
/*
 * Seccomp BPF filter generator to block Unix domain socket creation
 *
 * This program generates a seccomp-bpf filter that blocks the socket() syscall
 * when called with AF_UNIX as the domain argument. This prevents creation of
 * Unix domain sockets while allowing all other socket types (AF_INET, AF_INET6, etc.)
 * and all other syscalls.
 */

#include <seccomp.h>
#include <sys/socket.h>

int main(int argc, char *argv[]) {
    scmp_filter_ctx ctx;

    /* Create seccomp context with default action ALLOW */
    ctx = seccomp_init(SCMP_ACT_ALLOW);

    /* Add rule to block socket(AF_UNIX, ...) */
    /* Returns EPERM when AF_UNIX socket creation is attempted */
    seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(socket), 1,
                     SCMP_A0(SCMP_CMP_EQ, AF_UNIX));

    /* Export the filter to a file for use with bubblewrap --seccomp */
    seccomp_export_bpf(ctx, fd);

    seccomp_release(ctx);
    return 0;
}
```

**Security note from source**:
> SECURITY LIMITATION - 32-bit x86 (ia32):
> This filter does NOT block socketcall() syscall, which is a security issue
> on 32-bit x86 systems. On ia32, the socket() syscall doesn't exist - instead,
> all socket operations are multiplexed through socketcall().

### Installed Tools

**Sandbox dependencies** (`/usr/bin/`):
| Binary | Purpose |
|--------|---------|
| `bwrap` | Bubblewrap - namespace isolation |
| `socat` | Socket relay for controlled network access |
| `rg` | Ripgrep - fast file search |

**Runtime** (`/usr/bin/`, `/usr/local/bin/`):
| Binary | Version | Purpose |
|--------|---------|---------|
| `node` | 18.x | JavaScript runtime |
| `npm` | 9.x | Package manager |
| `python3` | 3.10 | Python runtime |
| `git` | 2.x | Version control |

**Document processing** (`/usr/local/lib/python3.10/dist-packages/`):
| Package | Purpose |
|---------|---------|
| `camelot` | PDF table extraction |
| `cv2` (OpenCV) | Image processing |
| `lxml` | XML/HTML parsing |
| `docx` | Word document handling |
| `magika` | File type detection (Google) |
| `bs4` | HTML parsing (BeautifulSoup) |
| `markdown` | Markdown processing |
| `img2pdf` | Image to PDF conversion |
| `pdfminer` | PDF text extraction |

**Other utilities** (`/usr/local/bin/`):
```
camelot, chardetect, coloredlogs, csv2ods, distro, dotenv,
dumppdf.py, f2py, fonttools, humanfriendly, imageio_download_bin,
img2pdf, isympy, magika, mailodf, marked, markdown-toc
```

### Cloud-Init Instance

The image was built with cloud-init, instance ID `claude-1`:
```
/var/lib/cloud/instances/claude-1/
```

### Key Paths Summary

| Path | Purpose |
|------|---------|
| `/usr/local/bin/sdk-daemon` | vsock RPC bridge (Go binary, 7MB) |
| `/usr/local/lib/node_modules_global/bin/srt` | Sandbox runtime CLI |
| `/usr/local/lib/node_modules_global/lib/node_modules/@anthropic-ai/sandbox-runtime/` | Sandbox runtime package |
| `/workspace/` | Session mount point (empty) |
| `/home/ubuntu/` | Default user home |
| `/etc/systemd/system/sdk-daemon.service` | SDK daemon systemd unit |

### Implications for Linux Port

1. **x64 images exist** - A VM-based Linux implementation is viable without building custom rootfs
2. **vsock is the protocol** - QEMU/KVM and Firecracker both support virtio-vsock
3. **Sandbox runtime is open source** - Can use `@anthropic-ai/sandbox-runtime` directly
4. **Claude Code is not pre-installed** - Installed dynamically via `installSdk()` RPC call
5. **SDK daemon protocol** - Would need to implement compatible RPC server for full VM approach