Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down
43 changes: 43 additions & 0 deletions docs/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
34 changes: 34 additions & 0 deletions scripts/launcher-common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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} $*"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down