I haven't dug too deep, but it appears to be using a bubblewrap sandbox inside a vm on the Mac using Apple's Virtualization.framework from what I can tell. It then uses unix sockets to proxy network via socat.
ETA: used Claude Code to reverse engineer it:
Insight ─────────────────────────────────────
Claude.app VM Architecture:
1. Uses Apple's Virtualization.framework (only on ARM64/Apple Silicon, macOS 13+)
2. Communication is via VirtioSocket (not stdio pipes directly to host)
3. The VM runs a full Linux system with EFI/GRUB boot
─────────────────────────────────────────────────
┌─────────────────────────────────────────────────────────────────────────────────┐
│ macOS Host │
│ │
│ Claude Desktop App (Electron + Swift native bindings) │
│ │ │
│ ├─ @anthropic-ai/claude-swift (swift_addon.node) │
│ │ └─ Links: Virtualization.framework (ARM64 only, macOS 13+) │
│ │ │
│ ↓ Creates/Starts VM via VZVirtualMachine │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ Linux VM (claudevm.bundle) │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Bubblewrap Sandbox (bwrap) │ │ │
│ │ │ - Network namespace isolation (--unshare-net) │ │ │
│ │ │ - PID namespace isolation (--unshare-pid) │ │ │
│ │ │ - Seccomp filtering (unix-block.bpf) │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ /usr/local/bin/claude │ │ │ │
│ │ │ │ (Claude Code SDK - 213MB ARM64 ELF binary) │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ --input-format stream-json │ │ │ │
│ │ │ │ --output-format stream-json │ │ │ │
│ │ │ │ --model claude-opus-4-5-20251101 │ │ │ │
│ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │
│ │ │ ↑↓ stdio (JSON-RPC) │ │ │
│ │ │ │ │ │
│ │ │ socat proxies: │ │ │
│ │ │ - TCP:3128 → /tmp/claude-http-*.sock (HTTP proxy) │ │ │
│ │ │ - TCP:1080 → /tmp/claude-socks-*.sock (SOCKS proxy) │ │ │
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
│ ↕ VirtioSocket (RPC) │
│ ClaudeVMDaemonRPCClient.swift │
│ ↕ │
│ Node.js IPC layer │
└─────────────────────────────────────────────────────────────────────────────────┘
VM Specifications (from inside)ComponentDetailsKernelLinux 6.8.0-90-generic aarch64 (Ubuntu PREEMPT_DYNAMIC)OSUbuntu 22.04.5 LTS (Jammy Jellyfish)HostnameclaudeCPU4 cores, Apple Silicon (virtualized), 48 BogoMIPSRAM3.8 GB total (~620MB used at idle)SwapNone
Storage Layout
DeviceSizeTypeMount PointPurpose/dev/nvme0n1p19.6 GBext4/Root filesystem (rootfs.img)/dev/nvme0n1p1598 MBvfat/boot/efiEFI boot partition/dev/nvme1n19.8 GBext4/sessionsSession data (sessiondata.img)virtiofs-virtiofs/mnt/.virtiofs-root/shared/...Host filesystem access
Filesystem Mounts (User Perspective)
/sessions/gallant-vigilant-lamport/
├── mnt/
│ ├── claude-cowork/ → Your selected folder (virtiofs + bindfs)
│ ├── .claude/ → ~/.claude config (bindfs, rw)
│ ├── .skills/ → Skills/plugins (bindfs, ro)
│ └── uploads/ → Uploaded files (bindfs)
└── tmp/ → Session temp files
Session User
A dedicated user is created per session with a Docker-style random name:
User: gallant-vigilant-lamport
UID: 1001
Home: /sessions/gallant-vigilant-lamport
Process Tree
PID 1: bwrap (bubblewrap sandbox)
└── bash (shell wrapper)
├── socat TCP:3128 → unix socket (HTTP proxy)
├── socat TCP:1080 → unix socket (SOCKS proxy)
└── /usr/local/bin/claude (Claude Code SDK)
└── bash (tool execution shells)
Security Layers
Apple Virtualization.framework - Hardware-level VM isolation
Bubblewrap (bwrap) - Linux container/sandbox
--unshare-net - No direct network access
--unshare-pid - Isolated PID namespace
--ro-bind / / - Read-only root (with selective rw binds)
Seccomp - System call filtering (unix-block.bpf)
Network Isolation - All traffic via proxied unix sockets
Network Architecture
┌─────────────────────────────────────────────────────────────┐
│ Inside Sandbox │
│ │
│ claude process │
│ │ │
│ ↓ HTTP/HTTPS requests │
│ localhost:3128 (HTTP proxy via env vars) │
│ │ │
│ ↓ │
│ socat → /tmp/claude-http-*.sock ─────────┐ │
│ │ │
│ localhost:1080 (SOCKS proxy) │ │
│ │ │ │
│ ↓ │ │
│ socat → /tmp/claude-socks-*.sock ────────┤ │
└───────────────────────────────────────────┼────────────────┘
│
VirtioSocket ←──────┘
│
┌───────────────────────────────────────────┼────────────────┐
│ Host (macOS) │ │
│ ↓ │
│ Claude Desktop App │
│ │ │
│ ↓ │
│ Internet │
└─────────────────────────────────────────────────────────────┘
Key insight: The VM has only a loopback interface (lo). No eth0, no bridge. All external network access is tunneled through unix sockets that cross the VM boundary via VirtioSocket.
Communication Flow
From the logs and symbols:
1. VM Start: Swift calls VZVirtualMachine.start() with EFI boot
2. Guest Ready: VM guest connects (takes ~6 seconds)
3. SDK Install: Copies /usr/local/bin/claude into VM
4. Process Spawn: RPC call to spawn /usr/local/bin/claude with args
The spawn command shows the actual invocation:
/usr/local/bin/claude --output-format stream-json --verbose \
--input-format stream-json --model claude-opus-4-5-20251101 \
--permission-prompt-tool stdio --mcp-config {...}