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:
mountBinds– Created by prior calls tomountPath()representing the host-side representation of guest bindsadditionalMounts– User-supplied mounts filtered throughresolveSubpath()to ensure they remain inside the user's$HOMEdirectory
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-dirflags containing guest mount paths--plugin-dirflags (with additionalresolvePluginRoot()resolution)--allowedToolsarguments 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 inscripts/cowork-vm-service.js. - The mount map construction combines automatic bind mounts from
mountPath()calls with user-suppliedadditionalMounts, enforcing containment within$HOME. translateGuestPath()performs regex extraction, mount lookup with fallback keys, and canonicalization with traversal protection usingpath.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.batsvalidates 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →