Guest-to-Host Path Translation in Claude Desktop Cowork Mode: Implementation Guide

The Claude Desktop Debian implementation converts virtual guest paths like /sessions/<id>/mnt/<name> to real host filesystem paths using a pure JavaScript translation layer in scripts/cowork-vm-service.js that validates mount mappings and prevents directory traversal attacks.

When running Claude Desktop in Cowork mode, file system access is virtualized through mount points that appear under /sessions/<session-id>/mnt/. The aaddrick/claude-desktop-debian repository implements a robust guest-to-host path translation system to convert these virtual paths into actual host filesystem locations before spawning processes or performing file operations.

Core Translation Architecture in Cowork Mode

Understanding the Virtual Path Format

Guest-side paths in Cowork mode follow a strict virtual pattern:


/sessions/<session-id>/mnt/<mount-name>[/sub-path]

The daemon must detect this format and extract the mount identifier to perform the lookup. According to the source code in scripts/cowork-vm-service.js, the translation system handles three potential mount map keys to accommodate Electron's ta() normalization: the raw mount name, a dot-prefixed version, and the name stripped of a leading dot.

The Mount Map Construction

Before translation can occur, the system builds a mount map that correlates virtual mount names to host directories. The buildMountMap() function at lines 219-245 constructs this mapping from two sources:

  1. mountBinds – Created by prior calls to mountPath() representing the host-side representation of guest binds
  2. additionalMounts – User-supplied mounts filtered through resolveSubpath() to ensure they remain inside the user's $HOME directory

Key Translation Functions

translateGuestPath(): Converting Virtual to Host Paths

The core translation logic resides in translateGuestPath(guestPath, mountMap) at lines 162-196 of scripts/cowork-vm-service.js. This pure function implements a four-stage pipeline:

/**
 * Translate a VM guest path (/sessions/{id}/mnt/{name}[/rest]) to a host
 * path using mountMap. Returns the translated path, or null on failure.
 */
function translateGuestPath(guestPath, mountMap) {
    // 1. Input validation
    if (!guestPath.startsWith('/sessions/') || !mountMap || Object.keys(mountMap).length === 0) {
        return null;
    }
    
    // 2. Regex extraction
    const match = guestPath.match(/^\/sessions\/[^/]+\/mnt\/([^/]+)(\/.+)?$/);
    if (!match) return null;
    
    const mountName = match[1];
    const remainder = match[2] || '';
    
    // 3. Mount lookup with fallback keys
    let hostBase = mountMap[mountName] || 
                   mountMap['.' + mountName] || 
                   mountMap[mountName.replace(/^\./, '')];
    
    if (!hostBase) return null;
    
    // 4. Path construction and traversal safety
    const hostPath = path.resolve(path.join(hostBase, remainder));
    if (!hostPath.startsWith(hostBase)) {
        return null; // Directory traversal attack prevented
    }
    return hostPath;
}

buildSpawnEnv(): Translating Environment Variables

The buildSpawnEnv() function at lines 76-88 handles environment variable translation, specifically targeting CLAUDE_CONFIG_DIR and CLAUDE_COWORK_MEMORY_PATH_OVERRIDE. When these variables contain guest paths starting with /sessions/, the function invokes translateGuestPath() to convert them to host paths before spawning the process.

cleanSpawnArgs(): Rewriting CLI Arguments

Command-line arguments undergo translation via cleanSpawnArgs() (referenced in tests/cowork-path-translation.bats at lines 46-63). This function specifically processes:

  • --add-dir flags containing guest mount paths
  • --plugin-dir flags (with additional resolvePluginRoot() resolution)
  • --allowedTools arguments containing embedded guest paths

The translation ensures that tools like Bash(/sessions/abc/mnt/project/script.sh) become Bash(/home/user/project/script.sh) before execution.

Security and Safety Mechanisms

Directory Traversal Prevention

The translation layer implements strict path canonicalization using path.resolve() followed by a prefix check. After joining the host base with the remainder sub-path, the code verifies that the resolved path still starts with the original host base directory. Any attempt to escape via ../ sequences results in the function returning null, preventing access to sensitive host directories.

Mount Point Validation

The buildMountMap() function enforces containment policies for user-supplied mounts. When processing additionalMounts, each path undergoes resolveSubpath() validation to ensure it resolves within the user's $HOME directory. This prevents the daemon from exposing arbitrary host system paths to the guest environment.

Practical Implementation Examples

Example 1: Basic Path Translation

const { translateGuestPath } = require('./scripts/cowork-vm-service');

const mountMap = {
    project: '/home/alice/projects/my-app',
    '.auto-memory': '/home/alice/.auto-memory'
};

const guestPath = '/sessions/12345/mnt/project/src/index.js';
const hostPath = translateGuestPath(guestPath, mountMap);

console.log(hostPath);
// Output: /home/alice/projects/my-app/src/index.js

This demonstrates how the virtual file src/index.js inside the guest session resolves to its real location on the host filesystem.

Example 2: CLI Argument Sanitization

When the Electron front-end requests a spawn operation:

claude-cli spawn \
    --add-dir /sessions/abcde/mnt/project \
    --plugin-dir /sessions/abcde/mnt/.plugins \
    --allowedTools "Bash(/sessions/abcde/mnt/project/script.sh)"

The daemon processes these through cleanSpawnArgs() and translateEmbeddedGuestPaths():

const cleanArgs = cleanSpawnArgs(rawArgs, mountMap);
// Result:
// [
//   '--add-dir', '/home/alice/projects/my-app',
//   '--plugin-dir', '/home/alice/.plugins',
//   '--allowedTools', 'Bash(/home/alice/projects/my-app/script.sh)'
// ]

Example 3: Environment Variable Handling

The front-end may set session-specific configuration:

CLAUDE_CONFIG_DIR=/sessions/xyz/mnt/.claude

Before spawning the subprocess, buildSpawnEnv() performs the translation:

const translatedEnv = buildSpawnEnv(
    { CLAUDE_CONFIG_DIR: '/sessions/xyz/mnt/.claude' }, 
    mountMap
);
// translatedEnv.CLAUDE_CONFIG_DIR === '/home/alice/.claude'

This ensures Claude Code reads its per-session configuration from the correct host directory.

Summary

  • Guest-to-host path translation in Cowork mode converts virtual paths (/sessions/<id>/mnt/<name>) to real host filesystem locations using a pure JavaScript implementation in scripts/cowork-vm-service.js.
  • The mount map construction combines automatic bind mounts from mountPath() calls with user-supplied additionalMounts, enforcing containment within $HOME.
  • translateGuestPath() performs regex extraction, mount lookup with fallback keys, and canonicalization with traversal protection using path.resolve() and prefix verification.
  • Translation applies to environment variables (CLAUDE_CONFIG_DIR, CLAUDE_COWORK_MEMORY_PATH_OVERRIDE), CLI arguments (--add-dir, --plugin-dir), and embedded tool paths (--allowedTools).
  • Comprehensive test coverage in tests/cowork-path-translation.bats validates the translation logic against regression.

Frequently Asked Questions

How does the translation system prevent directory traversal attacks?

The translateGuestPath() function uses path.resolve() to canonicalize the joined host base and sub-path, then verifies that the resulting path starts with the original host base directory. If the resolved path attempts to escape via ../ sequences, the function returns null, blocking access to sensitive host directories.

What environment variables get translated during Cowork mode execution?

The system specifically translates CLAUDE_CONFIG_DIR and CLAUDE_COWORK_MEMORY_PATH_OVERRIDE when they contain guest-style paths starting with /sessions/. The buildSpawnEnv() function handles these conversions to ensure the spawned processes receive valid host filesystem paths.

Where can I find the test suite for path translation logic?

The automated test suite resides in tests/cowork-path-translation.bats. These Bats tests exercise the pure JavaScript translation helpers in isolation, validating regex extraction, mount lookup logic, and security constraints against regression.

How does the system handle plugin directory paths differently from standard mount paths?

While --add-dir flags undergo direct translation through cleanSpawnArgs(), --plugin-dir flags receive additional processing via resolvePluginRoot(). This extra resolution step determines the actual plugin root directory on the host before the path is passed to the spawn helper, accommodating the specific directory structure requirements of Claude Desktop plugins.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →