aaddrick.com

Hobby maximalist and serial task jumper.

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
  2. Module Loading
  3. VM Lifecycle Management
  4. Process Management
  5. Stdin/Stdout Communication
  6. Path Translation & Mounting
  7. IPC Interface (Renderer → Main)
  8. Settings Configuration Architecture
  9. Security Architecture
  10. Symbol Reference
  11. Heartbeat Monitoring
  12. VM Bundle Management
  13. Knowledge Base Integration
  14. File System Watching
  15. ClaudeVM Web IPC Interface
  16. Idle Session Notifications
  17. Telemetry Events Reference
  18. Internal Feature Codenames
  19. VM Image Analysis

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

// 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

// 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)

// 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

// 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

// 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

// 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

// 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

// 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

// 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.

// 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)

// 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

// 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

// 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

// 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.

// 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.

// 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

// 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

Edit ~/.config/Claude/claude_desktop_config.json:

{
  "preferences": {
    "secureVmFeaturesEnabled": false
  }
}

Method 2: Enterprise Policy (macOS)

Edit ~/Library/Preferences/com.anthropic.Claude.plist:

<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

// In DevTools console:
await window.claude.ClaudeVM.setYukonSilverConfig({
  autoDownloadInBackground: false,
  autoStartOnUserIntent: true,
  memoryGB: 4
});

Settings Check Flow

// 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.

// 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:

// 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.

// 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):

// 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.

// 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:

// 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.

// 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:

// 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:

// 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:

// 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
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.

// 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:

trackEvent("lam_vm_heartbeat_failure", {
  vm_instance_id: getVMInstanceId() ?? "unknown",
  // ... additional context
});

VM Bundle Management

Bundle Structure

// 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

// 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:

// 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.

// 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:

// 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:

// 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):

// 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

// 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:

// 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

// 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):

{
  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

// 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):

[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:

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):

{
  "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:

{
  "@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):

/*
 * 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