mirror of
https://github.com/aaddrick/claude-desktop-debian.git
synced 2026-05-17 00:26:21 +03:00
feat: make bubblewrap the default cowork isolation backend
Swap auto-detection order from KVM → bwrap → host to bwrap → KVM → host. KVM remains available via COWORK_VM_BACKEND=kvm. - detectBackend(): check bwrap before KVM - --doctor: bwrap checked first; KVM deps shown as info (not warnings) unless COWORK_VM_BACKEND=kvm is set - Fix header comment inaccuracy about rootfs.qcow2 check - Update README and handover docs to reflect new default Fixes #326 Co-Authored-By: Claude <claude@anthropic.com>
This commit is contained in:
@@ -11,11 +11,11 @@ This project provides build scripts to run Claude Desktop natively on Linux syst
|
||||
>
|
||||
> | Backend | Isolation | Requirements |
|
||||
> |---------|-----------|-------------|
|
||||
> | **KVM** (preferred) | Full VM via QEMU/KVM | `/dev/kvm`, `qemu-system-x86_64`, `/dev/vhost-vsock`, `socat`, `virtiofsd` |
|
||||
> | **bubblewrap** (fallback) | Namespace sandbox | `bwrap` installed and functional |
|
||||
> | **bubblewrap** (default) | Namespace sandbox | `bwrap` installed and functional |
|
||||
> | **KVM** (opt-in) | Full VM via QEMU/KVM | `/dev/kvm`, `qemu-system-x86_64`, `/dev/vhost-vsock`, `socat`, `virtiofsd` |
|
||||
> | **host** (last resort) | None — runs directly on host | No additional requirements |
|
||||
>
|
||||
> The best available backend is auto-detected at startup. Run `claude-desktop --doctor` to check which backend will be used and which dependencies are missing.
|
||||
> The best available backend is auto-detected at startup. Run `claude-desktop --doctor` to check which backend will be used and which dependencies are missing. For full VM-level isolation matching the upstream Windows (Hyper-V) behavior, set `COWORK_VM_BACKEND=kvm`.
|
||||
>
|
||||
> **Note:** The bubblewrap backend mounts your home directory as read-only (only the project working directory is writable). The host backend provides no isolation — use it only if you understand the security implications.
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ cowork-vm-service (Node.js daemon)
|
||||
→ delegates to this.backend (auto-detected or COWORK_VM_BACKEND override)
|
||||
|
||||
Backend selection (priority order):
|
||||
1. KvmBackend — QEMU/KVM + vsock + virtiofs (full VM isolation)
|
||||
2. BwrapBackend — bubblewrap namespace sandbox (lightweight isolation)
|
||||
1. BwrapBackend — bubblewrap namespace sandbox (default)
|
||||
2. KvmBackend — QEMU/KVM + vsock + virtiofs (opt-in, full VM isolation)
|
||||
3. HostBackend — direct on host, no isolation (fallback)
|
||||
|
||||
KvmBackend path:
|
||||
@@ -173,15 +173,15 @@ The `detectBackend()` function selects the active backend at daemon startup. The
|
||||
|
||||
### Auto-detection order:
|
||||
|
||||
1. **KVM** — Requires ALL of:
|
||||
1. **Bwrap** (default) — Requires:
|
||||
- `bwrap` in PATH
|
||||
- `bwrap --ro-bind / / true` succeeds (functional test)
|
||||
|
||||
2. **KVM** (opt-in via `COWORK_VM_BACKEND=kvm`) — Requires ALL of:
|
||||
- `/dev/kvm` readable and writable
|
||||
- `qemu-system-x86_64` in PATH
|
||||
- `/dev/vhost-vsock` readable
|
||||
- `~/.local/share/claude-desktop/vm/rootfs.qcow2` exists
|
||||
|
||||
2. **Bwrap** — Requires:
|
||||
- `bwrap` in PATH
|
||||
- `bwrap --ro-bind / / true` succeeds (functional test)
|
||||
- Rootfs image checked at `startVM()` time, not during detection
|
||||
|
||||
3. **Host** — Always available (fallback)
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
* - KvmBackend: QEMU/KVM virtual machine with vsock communication
|
||||
*
|
||||
* Backend selection (auto-detected or overridden via COWORK_VM_BACKEND env):
|
||||
* 1. kvm — if /dev/kvm, qemu-system-x86_64, /dev/vhost-vsock, and
|
||||
* rootfs.qcow2 are all available
|
||||
* 2. bwrap — if bwrap is installed and functional
|
||||
* 1. bwrap — if bwrap is installed and functional (default)
|
||||
* 2. kvm — if /dev/kvm, qemu-system-x86_64, and /dev/vhost-vsock
|
||||
* are available (rootfs checked at startVM time)
|
||||
* 3. host — fallback, no isolation
|
||||
*
|
||||
* Protocol:
|
||||
@@ -1806,7 +1806,18 @@ function detectBackend(emitEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-detect: try KVM first, then bwrap, then host.
|
||||
// Auto-detect: try bwrap first, then KVM, then host.
|
||||
try {
|
||||
execFileSync('which', ['bwrap'], { stdio: 'pipe' });
|
||||
execFileSync('bwrap', ['--ro-bind', '/', '/', 'true'], {
|
||||
stdio: 'pipe', timeout: 5000
|
||||
});
|
||||
log('Backend: bwrap');
|
||||
return new BwrapBackend(emitEvent);
|
||||
} catch (e) {
|
||||
log(`bwrap not available: ${e.message}`);
|
||||
}
|
||||
|
||||
// Note: rootfs is NOT checked here — the app downloads it to
|
||||
// bundlePath which isn't known until startVM(). The rootfs
|
||||
// check happens at startVM time instead.
|
||||
@@ -1820,17 +1831,6 @@ function detectBackend(emitEvent) {
|
||||
log(`KVM not available: ${e.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
execFileSync('which', ['bwrap'], { stdio: 'pipe' });
|
||||
execFileSync('bwrap', ['--ro-bind', '/', '/', 'true'], {
|
||||
stdio: 'pipe', timeout: 5000
|
||||
});
|
||||
log('Backend: bwrap');
|
||||
return new BwrapBackend(emitEvent);
|
||||
} catch (e) {
|
||||
log(`bwrap not available: ${e.message}`);
|
||||
}
|
||||
|
||||
log('Backend: host (no isolation)');
|
||||
return new HostBackend(emitEvent);
|
||||
}
|
||||
|
||||
@@ -468,35 +468,59 @@ print(len(servers))
|
||||
local _distro_id
|
||||
_distro_id=$(_cowork_distro_id)
|
||||
|
||||
# KVM access
|
||||
# Bubblewrap (default backend)
|
||||
if command -v bwrap &>/dev/null; then
|
||||
_pass 'bubblewrap: found'
|
||||
else
|
||||
_warn 'bubblewrap: not found'
|
||||
_info \
|
||||
"Fix: $(_cowork_pkg_hint "$_distro_id" bubblewrap)"
|
||||
fi
|
||||
|
||||
# Use _warn for missing KVM deps only when KVM is explicitly
|
||||
# requested; otherwise use _info since bwrap is the default.
|
||||
local _kvm_flag
|
||||
_kvm_flag="${COWORK_VM_BACKEND:-}"
|
||||
local _kvm_issue
|
||||
if [[ ${_kvm_flag,,} == kvm ]]; then
|
||||
_kvm_issue=_warn
|
||||
else
|
||||
_kvm_issue=_info
|
||||
fi
|
||||
|
||||
# KVM backend (opt-in via COWORK_VM_BACKEND=kvm)
|
||||
if [[ -e /dev/kvm ]]; then
|
||||
if [[ -r /dev/kvm && -w /dev/kvm ]]; then
|
||||
_pass 'KVM: accessible'
|
||||
else
|
||||
_warn 'KVM: /dev/kvm exists but not accessible'
|
||||
"$_kvm_issue" 'KVM: /dev/kvm exists but not accessible'
|
||||
_info "Fix: sudo usermod -aG kvm $USER"
|
||||
_info '(Log out and back in after running this)'
|
||||
fi
|
||||
else
|
||||
_warn 'KVM: not available'
|
||||
_info 'Fix: Install qemu-kvm and ensure KVM is enabled in BIOS'
|
||||
"$_kvm_issue" 'KVM: not available'
|
||||
if [[ ${_kvm_flag,,} == kvm ]]; then
|
||||
_info \
|
||||
'Fix: Install qemu-kvm and ensure KVM is enabled in BIOS'
|
||||
fi
|
||||
fi
|
||||
|
||||
# vsock module
|
||||
if [[ -e /dev/vhost-vsock ]]; then
|
||||
_pass 'vsock: module loaded'
|
||||
else
|
||||
_warn 'vsock: /dev/vhost-vsock not found'
|
||||
_info 'Fix: sudo modprobe vhost_vsock'
|
||||
"$_kvm_issue" 'vsock: /dev/vhost-vsock not found'
|
||||
if [[ ${_kvm_flag,,} == kvm ]]; then
|
||||
_info 'Fix: sudo modprobe vhost_vsock'
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check required tools: label, binary, pkg-hint name
|
||||
# KVM tools: QEMU, socat, virtiofsd
|
||||
local _tool_label _tool_bin _tool_pkg
|
||||
for _tool_label in \
|
||||
'QEMU:qemu-system-x86_64:qemu' \
|
||||
'socat:socat:socat' \
|
||||
'virtiofsd:virtiofsd:virtiofsd' \
|
||||
'bubblewrap:bwrap:bubblewrap'
|
||||
'virtiofsd:virtiofsd:virtiofsd'
|
||||
do
|
||||
_tool_bin="${_tool_label#*:}"
|
||||
_tool_pkg="${_tool_bin#*:}"
|
||||
@@ -506,9 +530,11 @@ print(len(servers))
|
||||
if command -v "$_tool_bin" &>/dev/null; then
|
||||
_pass "$_tool_label: found"
|
||||
else
|
||||
_warn "$_tool_label: not found"
|
||||
_info \
|
||||
"Fix: $(_cowork_pkg_hint "$_distro_id" "$_tool_pkg")"
|
||||
"$_kvm_issue" "$_tool_label: not found"
|
||||
if [[ ${_kvm_flag,,} == kvm ]]; then
|
||||
_info \
|
||||
"Fix: $(_cowork_pkg_hint "$_distro_id" "$_tool_pkg")"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -526,15 +552,14 @@ print(len(servers))
|
||||
|
||||
# Determine active backend (matches daemon's detectBackend())
|
||||
local cowork_backend='none (host-direct, no isolation)'
|
||||
if [[ -e /dev/kvm ]] \
|
||||
&& [[ -r /dev/kvm && -w /dev/kvm ]] \
|
||||
&& command -v qemu-system-x86_64 &>/dev/null \
|
||||
&& [[ -e /dev/vhost-vsock ]] \
|
||||
&& [[ -f $vm_image ]]; then
|
||||
cowork_backend='KVM (full VM isolation)'
|
||||
elif command -v bwrap &>/dev/null \
|
||||
if command -v bwrap &>/dev/null \
|
||||
&& bwrap --ro-bind / / true &>/dev/null; then
|
||||
cowork_backend='bubblewrap (namespace sandbox)'
|
||||
elif [[ -e /dev/kvm ]] \
|
||||
&& [[ -r /dev/kvm && -w /dev/kvm ]] \
|
||||
&& command -v qemu-system-x86_64 &>/dev/null \
|
||||
&& [[ -e /dev/vhost-vsock ]]; then
|
||||
cowork_backend='KVM (full VM isolation)'
|
||||
fi
|
||||
_info "Cowork isolation: $cowork_backend"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user