From de275ef7c1f7eb288281360bc0f5cc4660739719 Mon Sep 17 00:00:00 2001 From: CyPack Date: Mon, 30 Mar 2026 17:22:40 +0200 Subject: [PATCH] fix(doctor): detect virtiofsd outside PATH + document COWORK_VM_BACKEND Add _doctor_find_virtiofsd() to probe /usr/libexec/virtiofsd (Fedora/RHEL) and /usr/lib/qemu/virtiofsd (Debian/Ubuntu) when virtiofsd is not in PATH. Shows [PASS] with the discovered path instead of a false [WARN] not-found. Add warning for invalid COWORK_VM_BACKEND values with valid-values hint. Document COWORK_VM_BACKEND in CONFIGURATION.md with priority table, override examples, and desktop entry persistence. Add three Cowork troubleshooting sections to TROUBLESHOOTING.md: - VM connection timeout (vsock/first-boot) with bwrap workaround - virtiofsd not found on Fedora (doctor vs runtime PATH distinction) - cross-device link error on Fedora tmpfs /tmp Co-Authored-By: Claude --- docs/CONFIGURATION.md | 37 ++++++++++++++++++++++++++++++++ docs/TROUBLESHOOTING.md | 43 ++++++++++++++++++++++++++++++++++++++ scripts/launcher-common.sh | 34 ++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index eabbf6a6..302e09af 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -15,6 +15,7 @@ Model Context Protocol settings are stored in: |----------|---------|-------------| | `CLAUDE_USE_WAYLAND` | unset | Set to `1` to use native Wayland instead of XWayland. Note: Global hotkeys won't work in native Wayland mode. | | `CLAUDE_MENU_BAR` | unset (`auto`) | Controls menu bar behavior: `auto` (hidden, Alt toggles), `visible` / `1` (always shown), `hidden` / `0` (always hidden, Alt disabled). See [Menu Bar](#menu-bar) below. | +| `COWORK_VM_BACKEND` | unset (auto-detect) | Force a specific Cowork isolation backend: `kvm` (full VM), `bwrap` (bubblewrap namespace sandbox), or `host` (no isolation). See [Cowork Backend](#cowork-backend) below. | ### Wayland Support @@ -48,6 +49,42 @@ CLAUDE_MENU_BAR=visible claude-desktop export CLAUDE_MENU_BAR=visible ``` +### Cowork Backend + +Cowork mode auto-detects the best available isolation backend: + +| Priority | Backend | Isolation | Detection | +|----------|---------|-----------|-----------| +| 1 | bubblewrap | Namespace sandbox | `bwrap` installed and functional | +| 2 | KVM | Full QEMU/KVM VM | `/dev/kvm` (r/w) + `qemu-system-x86_64` + `/dev/vhost-vsock` | +| 3 | host | None (direct execution) | Always available | + +To override auto-detection: + +```bash +# Force bubblewrap (recommended if KVM times out) +COWORK_VM_BACKEND=bwrap claude-desktop + +# Force host mode (no isolation) +COWORK_VM_BACKEND=host claude-desktop + +# Make permanent via desktop entry override +mkdir -p ~/.local/share/applications/ +cat > ~/.local/share/applications/claude-desktop.desktop << 'EOF' +[Desktop Entry] +Name=Claude +Exec=env COWORK_VM_BACKEND=bwrap /usr/bin/claude-desktop %u +Icon=claude-desktop +Type=Application +Terminal=false +Categories=Office;Utility; +MimeType=x-scheme-handler/claude; +StartupWMClass=Claude +EOF +``` + +Run `claude-desktop --doctor` to see which backend is selected and which dependencies are available. + ## Application Logs Runtime logs are available at: diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index c379ed37..f9e1fa03 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -80,6 +80,49 @@ If the global hotkey (Ctrl+Alt+Space) doesn't work, ensure you're not running in See [CONFIGURATION.md](CONFIGURATION.md) for more details on the `CLAUDE_USE_WAYLAND` environment variable. +### Cowork: "VM connection timeout after 60 seconds" + +If Cowork fails with a VM timeout, the KVM backend is selected but the guest VM cannot connect back to the host via vsock within the timeout window. Common causes: + +1. **First-boot initialization** — the guest VM may take longer than 60 seconds on first launch +2. **vsock driver issues** — the host may be missing the `vhost_vsock` module (`sudo modprobe vhost_vsock`), or the guest initrd may lack `vmw_vsock_virtio_transport` + +**Fix:** Force the bubblewrap backend, which provides namespace-level isolation without a VM: + +```bash +COWORK_VM_BACKEND=bwrap claude-desktop +``` + +See [CONFIGURATION.md](CONFIGURATION.md#cowork-backend) for how to make this permanent. + +### Cowork: virtiofsd not found (Fedora/RHEL) + +On Fedora and RHEL, `virtiofsd` installs to `/usr/libexec/virtiofsd` which is +outside `$PATH`. The `--doctor` check detects it there automatically and will +show `[PASS]`, but the KVM backend spawns `virtiofsd` by name at runtime and +resolves it through `$PATH` only. + +**Fix:** Create a symlink so the KVM backend can find it at runtime: + +```bash +sudo ln -s /usr/libexec/virtiofsd /usr/local/bin/virtiofsd +``` + +On Debian/Ubuntu, the same issue can occur with `/usr/lib/qemu/virtiofsd`. + +### Cowork: cross-device link error on Fedora tmpfs /tmp + +On Fedora, `/tmp` is a tmpfs by default. VM bundle downloads may fail with `EXDEV: cross-device link not permitted` when moving files from `/tmp` to `~/.config/Claude/`. + +**Fix:** Set `TMPDIR` to a directory on the same filesystem: + +```bash +mkdir -p ~/.config/Claude/tmp +TMPDIR=~/.config/Claude/tmp claude-desktop +``` + +Or add `TMPDIR=%h/.config/Claude/tmp` to the `Exec=` line in your `.desktop` file. + ### AppImage Sandbox Warning AppImages run with `--no-sandbox` due to electron's chrome-sandbox requiring root privileges for unprivileged namespace creation. This is a known limitation of AppImage format with Electron applications. diff --git a/scripts/launcher-common.sh b/scripts/launcher-common.sh index e2113405..ddac37a8 100755 --- a/scripts/launcher-common.sh +++ b/scripts/launcher-common.sh @@ -263,6 +263,21 @@ _cowork_pkg_hint() { printf '%s' "$pkg_cmd $pkg" } +# Probe common non-PATH locations for virtiofsd +# On Fedora/RHEL: /usr/libexec/virtiofsd +# On Debian/Ubuntu: /usr/lib/qemu/virtiofsd +# Returns 0 if found, 1 if not; prints path on stdout +_doctor_find_virtiofsd() { + local path + for path in /usr/libexec/virtiofsd /usr/lib/qemu/virtiofsd; do + if [[ -x $path ]]; then + printf '%s' "$path" + return 0 + fi + done + return 1 +} + _pass() { echo -e "${_green}[PASS]${_reset} $*"; } _fail() { echo -e "${_red}[FAIL]${_reset} $*" @@ -566,6 +581,19 @@ print(len(servers)) if command -v "$_tool_bin" &>/dev/null; then _pass "$_tool_label: found" + elif [[ $_tool_bin == 'virtiofsd' ]]; then + local _vfsd_path + _vfsd_path=$(_doctor_find_virtiofsd) || true + if [[ -n $_vfsd_path ]]; then + _pass "$_tool_label: found" \ + "($_vfsd_path, not in PATH)" + else + "$_kvm_issue" "$_tool_label: not found" + if $_kvm_active; then + _info "Fix: $(_cowork_pkg_hint \ + "$_distro_id" "$_tool_pkg")" + fi + fi else "$_kvm_issue" "$_tool_label: not found" if $_kvm_active; then @@ -594,6 +622,12 @@ print(len(servers)) kvm) cowork_backend='KVM (full VM isolation, via override)' ;; bwrap) cowork_backend='bubblewrap (namespace sandbox, via override)' ;; host) cowork_backend='host-direct (no isolation, via override)' ;; + *) + _warn "Unknown COWORK_VM_BACKEND:" \ + "'${COWORK_VM_BACKEND}'" + _info 'Valid values: kvm, bwrap, host' + cowork_backend="unknown ('${COWORK_VM_BACKEND}')" + ;; esac elif command -v bwrap &>/dev/null \ && bwrap --ro-bind / / true &>/dev/null; then