Files
claude-desktop-debian/README.md

251 lines
12 KiB
Markdown
Raw Normal View History

# Claude Desktop for Linux
This project provides build scripts to run Claude Desktop natively on Linux systems. It repackages the official Windows application for Linux distributions, producing `.deb` packages (Debian/Ubuntu), `.rpm` packages (Fedora/RHEL), distribution-agnostic AppImages, an [AUR package](https://aur.archlinux.org/packages/claude-desktop-appimage) for Arch Linux, and a Nix flake for NixOS.
**Note:** This is an unofficial build script. For official support, please visit [Anthropic's website](https://www.anthropic.com). For issues with the build script or Linux implementation, please [open an issue](https://github.com/aaddrick/claude-desktop-debian/issues) in this repository.
---
> **⚠️ EXPERIMENTAL: Cowork Mode Support**
> Cowork mode is **enabled by default** in this build with a pluggable isolation backend:
feat: KVM/bwrap isolation backends for cowork mode (#269) * feat: add KVM/bwrap isolation backends for cowork mode Refactor cowork-vm-service.js from monolithic VMManager into a pluggable backend architecture with three isolation levels: - HostBackend: direct execution on host (existing Phase 1 behavior) - BwrapBackend: bubblewrap namespace sandbox (PID/mount isolation) - KvmBackend: full QEMU/KVM VM with vsock, virtiofs, QMP monitor Backend auto-detected at startup (KVM > bwrap > host) or overridden via COWORK_VM_BACKEND env var. Shared helpers extracted for env filtering, arg cleanup, command resolution, and work dir handling. Also: - Add Cowork Mode section to --doctor diagnostics with per-tool checks and distro-specific install hints - Update Patch 4 to extract real win32 file entries for linux bundle manifest (enables app download infrastructure) - Update handover documentation for Phase 2/3 architecture Co-Authored-By: Claude <claude@anthropic.com> * fix: correct KvmBackend vsock port, direction, and kernel cmdline The guest sdk-daemon connects TO the host (CID=2), not the other way around. Confirmed via disassembly of the guest binary: the vsock port is 51234 (0xC822), matching the Hyper-V GUID in the Windows service. - Change VSOCK_GUEST_PORT from 2222 to 51234 - Reverse socat bridge: VSOCK-LISTEN (host listens) instead of VSOCK-CONNECT (host connecting to guest) - Add bridge server to accept persistent guest connection and route events/responses via callback map - Fix kernel cmdline: root=LABEL=cloudimg-rootfs (matches fstab) Co-Authored-By: Claude <claude@anthropic.com> * fix: patch VM download to use disk-backed temp dir on Linux Linux systems often mount /tmp as a small tmpfs (3-4GB). The VM rootfs download decompresses to ~9GB, causing ENOSPC. Patch the app's mkdtemp("wvm-") call to use the bundle directory (on real disk) instead of os.tmpdir() on Linux. Uses regex-based dynamic variable extraction to remain version-agnostic across minified code changes. Co-Authored-By: Claude <claude@anthropic.com> * fix: handle stale cowork socket (ECONNREFUSED) on Linux Stale sockets from previous sessions give ECONNREFUSED instead of ENOENT, bypassing the retry loop and auto-launch entirely. Fix: - Expand ENOENT check in retry loop to include ECONNREFUSED on Linux - Add cleanup_stale_cowork_socket() to launcher scripts (all formats) that removes dead sockets before Electron starts - Increase tmpdir patch search window from 1000 to 2000 chars Co-Authored-By: Claude <claude@anthropic.com> * fix: preserve DNS resolution inside bwrap sandbox On systems using systemd-resolved, /etc/resolv.conf is a symlink to /run/systemd/resolve/stub-resolv.conf. The bwrap --tmpfs /run option wiped this out, breaking DNS resolution inside the sandbox and preventing the spawned Claude process from reaching the API. Bind-mount the resolved /run/systemd/resolve/ directory back into the sandbox as read-only to restore DNS. Co-Authored-By: Claude <claude@anthropic.com> * fix: harden cowork isolation and build patches - Remove broken _setupEventForwarding (events already flow through _handleGuestData); the second bridge connection was silently ignored - Mount $HOME read-only in bwrap sandbox; only workDir and explicit mounts are writable (prevents writes to ~/.ssh, ~/.gnupg, etc.) - Scope Patch 4 win32 extraction to actual win32:{} block via brace counting to avoid crossing into darwin/linux sections - Set _qmpAvailable flag on QMP timeout instead of silently continuing - Wrap CID allocation at 65535 to prevent unbounded growth - Use execFileSync instead of execSync('which ...') in detectBackend - Coerce response ID to String for Map lookup in _handleGuestData - Use non-greedy [\s\S]*? in Patch 6 regex for nested brace robustness - Update patch count threshold from 4 to 5 after adding Patch 8 - Add age-based fallback for stale socket cleanup when socat is missing - Use indexOf-based splice in Patch 8 instead of string.replace() - Extract shared resolveSdkBinary helper to deduplicate SDK resolution - Remove dead retryFuncRe/retryFuncMatch variables from Patch 6 Co-Authored-By: Claude <claude@anthropic.com> * fix: address security and correctness issues from code review - Replace execSync string interpolation with execFileSync for qemu-img calls to eliminate shell injection risk - Add path validation to readFile in both LocalBackend and KvmBackend to restrict reads to within the user's home directory - Fix QMP _sendQmpCommand timer leak by clearing timeout on success - Fix _pendingCallbacks.delete() to use String(msg.id) matching the String(msg.id) used in the .get() lookup - Extract FORWARDED_EVENTS constant, cleanup helper, extractBlock helper, and consolidate doctor tool checks (from simplifier pass) Co-Authored-By: Claude <claude@anthropic.com> * docs: update README cowork notice with isolation backends and doctor info Co-Authored-By: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com>
2026-02-28 21:13:09 -05:00
>
> | Backend | Isolation | Requirements |
> |---------|-----------|-------------|
> | **bubblewrap** (default) | Namespace sandbox | `bwrap` installed and functional |
> | **host** (fallback) | None — runs directly on host | No additional requirements |
feat: KVM/bwrap isolation backends for cowork mode (#269) * feat: add KVM/bwrap isolation backends for cowork mode Refactor cowork-vm-service.js from monolithic VMManager into a pluggable backend architecture with three isolation levels: - HostBackend: direct execution on host (existing Phase 1 behavior) - BwrapBackend: bubblewrap namespace sandbox (PID/mount isolation) - KvmBackend: full QEMU/KVM VM with vsock, virtiofs, QMP monitor Backend auto-detected at startup (KVM > bwrap > host) or overridden via COWORK_VM_BACKEND env var. Shared helpers extracted for env filtering, arg cleanup, command resolution, and work dir handling. Also: - Add Cowork Mode section to --doctor diagnostics with per-tool checks and distro-specific install hints - Update Patch 4 to extract real win32 file entries for linux bundle manifest (enables app download infrastructure) - Update handover documentation for Phase 2/3 architecture Co-Authored-By: Claude <claude@anthropic.com> * fix: correct KvmBackend vsock port, direction, and kernel cmdline The guest sdk-daemon connects TO the host (CID=2), not the other way around. Confirmed via disassembly of the guest binary: the vsock port is 51234 (0xC822), matching the Hyper-V GUID in the Windows service. - Change VSOCK_GUEST_PORT from 2222 to 51234 - Reverse socat bridge: VSOCK-LISTEN (host listens) instead of VSOCK-CONNECT (host connecting to guest) - Add bridge server to accept persistent guest connection and route events/responses via callback map - Fix kernel cmdline: root=LABEL=cloudimg-rootfs (matches fstab) Co-Authored-By: Claude <claude@anthropic.com> * fix: patch VM download to use disk-backed temp dir on Linux Linux systems often mount /tmp as a small tmpfs (3-4GB). The VM rootfs download decompresses to ~9GB, causing ENOSPC. Patch the app's mkdtemp("wvm-") call to use the bundle directory (on real disk) instead of os.tmpdir() on Linux. Uses regex-based dynamic variable extraction to remain version-agnostic across minified code changes. Co-Authored-By: Claude <claude@anthropic.com> * fix: handle stale cowork socket (ECONNREFUSED) on Linux Stale sockets from previous sessions give ECONNREFUSED instead of ENOENT, bypassing the retry loop and auto-launch entirely. Fix: - Expand ENOENT check in retry loop to include ECONNREFUSED on Linux - Add cleanup_stale_cowork_socket() to launcher scripts (all formats) that removes dead sockets before Electron starts - Increase tmpdir patch search window from 1000 to 2000 chars Co-Authored-By: Claude <claude@anthropic.com> * fix: preserve DNS resolution inside bwrap sandbox On systems using systemd-resolved, /etc/resolv.conf is a symlink to /run/systemd/resolve/stub-resolv.conf. The bwrap --tmpfs /run option wiped this out, breaking DNS resolution inside the sandbox and preventing the spawned Claude process from reaching the API. Bind-mount the resolved /run/systemd/resolve/ directory back into the sandbox as read-only to restore DNS. Co-Authored-By: Claude <claude@anthropic.com> * fix: harden cowork isolation and build patches - Remove broken _setupEventForwarding (events already flow through _handleGuestData); the second bridge connection was silently ignored - Mount $HOME read-only in bwrap sandbox; only workDir and explicit mounts are writable (prevents writes to ~/.ssh, ~/.gnupg, etc.) - Scope Patch 4 win32 extraction to actual win32:{} block via brace counting to avoid crossing into darwin/linux sections - Set _qmpAvailable flag on QMP timeout instead of silently continuing - Wrap CID allocation at 65535 to prevent unbounded growth - Use execFileSync instead of execSync('which ...') in detectBackend - Coerce response ID to String for Map lookup in _handleGuestData - Use non-greedy [\s\S]*? in Patch 6 regex for nested brace robustness - Update patch count threshold from 4 to 5 after adding Patch 8 - Add age-based fallback for stale socket cleanup when socat is missing - Use indexOf-based splice in Patch 8 instead of string.replace() - Extract shared resolveSdkBinary helper to deduplicate SDK resolution - Remove dead retryFuncRe/retryFuncMatch variables from Patch 6 Co-Authored-By: Claude <claude@anthropic.com> * fix: address security and correctness issues from code review - Replace execSync string interpolation with execFileSync for qemu-img calls to eliminate shell injection risk - Add path validation to readFile in both LocalBackend and KvmBackend to restrict reads to within the user's home directory - Fix QMP _sendQmpCommand timer leak by clearing timeout on success - Fix _pendingCallbacks.delete() to use String(msg.id) matching the String(msg.id) used in the .get() lookup - Extract FORWARDED_EVENTS constant, cleanup helper, extractBlock helper, and consolidate doctor tool checks (from simplifier pass) Co-Authored-By: Claude <claude@anthropic.com> * docs: update README cowork notice with isolation backends and doctor info Co-Authored-By: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com>
2026-02-28 21:13:09 -05:00
>
> 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.
feat: KVM/bwrap isolation backends for cowork mode (#269) * feat: add KVM/bwrap isolation backends for cowork mode Refactor cowork-vm-service.js from monolithic VMManager into a pluggable backend architecture with three isolation levels: - HostBackend: direct execution on host (existing Phase 1 behavior) - BwrapBackend: bubblewrap namespace sandbox (PID/mount isolation) - KvmBackend: full QEMU/KVM VM with vsock, virtiofs, QMP monitor Backend auto-detected at startup (KVM > bwrap > host) or overridden via COWORK_VM_BACKEND env var. Shared helpers extracted for env filtering, arg cleanup, command resolution, and work dir handling. Also: - Add Cowork Mode section to --doctor diagnostics with per-tool checks and distro-specific install hints - Update Patch 4 to extract real win32 file entries for linux bundle manifest (enables app download infrastructure) - Update handover documentation for Phase 2/3 architecture Co-Authored-By: Claude <claude@anthropic.com> * fix: correct KvmBackend vsock port, direction, and kernel cmdline The guest sdk-daemon connects TO the host (CID=2), not the other way around. Confirmed via disassembly of the guest binary: the vsock port is 51234 (0xC822), matching the Hyper-V GUID in the Windows service. - Change VSOCK_GUEST_PORT from 2222 to 51234 - Reverse socat bridge: VSOCK-LISTEN (host listens) instead of VSOCK-CONNECT (host connecting to guest) - Add bridge server to accept persistent guest connection and route events/responses via callback map - Fix kernel cmdline: root=LABEL=cloudimg-rootfs (matches fstab) Co-Authored-By: Claude <claude@anthropic.com> * fix: patch VM download to use disk-backed temp dir on Linux Linux systems often mount /tmp as a small tmpfs (3-4GB). The VM rootfs download decompresses to ~9GB, causing ENOSPC. Patch the app's mkdtemp("wvm-") call to use the bundle directory (on real disk) instead of os.tmpdir() on Linux. Uses regex-based dynamic variable extraction to remain version-agnostic across minified code changes. Co-Authored-By: Claude <claude@anthropic.com> * fix: handle stale cowork socket (ECONNREFUSED) on Linux Stale sockets from previous sessions give ECONNREFUSED instead of ENOENT, bypassing the retry loop and auto-launch entirely. Fix: - Expand ENOENT check in retry loop to include ECONNREFUSED on Linux - Add cleanup_stale_cowork_socket() to launcher scripts (all formats) that removes dead sockets before Electron starts - Increase tmpdir patch search window from 1000 to 2000 chars Co-Authored-By: Claude <claude@anthropic.com> * fix: preserve DNS resolution inside bwrap sandbox On systems using systemd-resolved, /etc/resolv.conf is a symlink to /run/systemd/resolve/stub-resolv.conf. The bwrap --tmpfs /run option wiped this out, breaking DNS resolution inside the sandbox and preventing the spawned Claude process from reaching the API. Bind-mount the resolved /run/systemd/resolve/ directory back into the sandbox as read-only to restore DNS. Co-Authored-By: Claude <claude@anthropic.com> * fix: harden cowork isolation and build patches - Remove broken _setupEventForwarding (events already flow through _handleGuestData); the second bridge connection was silently ignored - Mount $HOME read-only in bwrap sandbox; only workDir and explicit mounts are writable (prevents writes to ~/.ssh, ~/.gnupg, etc.) - Scope Patch 4 win32 extraction to actual win32:{} block via brace counting to avoid crossing into darwin/linux sections - Set _qmpAvailable flag on QMP timeout instead of silently continuing - Wrap CID allocation at 65535 to prevent unbounded growth - Use execFileSync instead of execSync('which ...') in detectBackend - Coerce response ID to String for Map lookup in _handleGuestData - Use non-greedy [\s\S]*? in Patch 6 regex for nested brace robustness - Update patch count threshold from 4 to 5 after adding Patch 8 - Add age-based fallback for stale socket cleanup when socat is missing - Use indexOf-based splice in Patch 8 instead of string.replace() - Extract shared resolveSdkBinary helper to deduplicate SDK resolution - Remove dead retryFuncRe/retryFuncMatch variables from Patch 6 Co-Authored-By: Claude <claude@anthropic.com> * fix: address security and correctness issues from code review - Replace execSync string interpolation with execFileSync for qemu-img calls to eliminate shell injection risk - Add path validation to readFile in both LocalBackend and KvmBackend to restrict reads to within the user's home directory - Fix QMP _sendQmpCommand timer leak by clearing timeout on success - Fix _pendingCallbacks.delete() to use String(msg.id) matching the String(msg.id) used in the .get() lookup - Extract FORWARDED_EVENTS constant, cleanup helper, extractBlock helper, and consolidate doctor tool checks (from simplifier pass) Co-Authored-By: Claude <claude@anthropic.com> * docs: update README cowork notice with isolation backends and doctor info Co-Authored-By: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com>
2026-02-28 21:13:09 -05:00
>
> **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.
>
> **KVM status:** The KVM/QEMU backend code exists but is non-functional — VM file downloads are disabled on Linux to prevent a checksum loop (#337). The backend code remains for potential future use.
---
## Features
- **Native Linux Support**: Run Claude Desktop without virtualization or Wine
- **MCP Support**: Full Model Context Protocol integration
Configuration file location: `~/.config/Claude/claude_desktop_config.json`
- **System Integration**:
- Global hotkey support (Ctrl+Alt+Space) - works on X11 and Wayland (via XWayland)
- System tray integration
- Desktop environment integration
2025-04-03 01:27:06 -04:00
### Screenshots
<p align="center">
<img src="https://raw.githubusercontent.com/aaddrick/claude-desktop-debian/main/docs/images/claude-desktop-screenshot1.png" alt="Claude Desktop running on Linux" />
</p>
<p align="center">
<img src="https://raw.githubusercontent.com/aaddrick/claude-desktop-debian/main/docs/images/claude-desktop-screenshot2.png" alt="Global hotkey popup" />
</p>
## Installation
2025-01-17 11:17:57 +01:00
### Using APT Repository (Debian/Ubuntu - Recommended)
Add the repository for automatic updates via `apt`:
```bash
# Add the GPG key
curl -fsSL https://aaddrick.github.io/claude-desktop-debian/KEY.gpg | sudo gpg --dearmor -o /usr/share/keyrings/claude-desktop.gpg
# Add the repository
echo "deb [signed-by=/usr/share/keyrings/claude-desktop.gpg arch=amd64,arm64] https://aaddrick.github.io/claude-desktop-debian stable main" | sudo tee /etc/apt/sources.list.d/claude-desktop.list
# Update and install
sudo apt update
sudo apt install claude-desktop
```
Future updates will be installed automatically with your regular system updates (`sudo apt upgrade`).
### Using DNF Repository (Fedora/RHEL - Recommended)
Add the repository for automatic updates via `dnf`:
```bash
# Add the repository
sudo curl -fsSL https://aaddrick.github.io/claude-desktop-debian/rpm/claude-desktop.repo -o /etc/yum.repos.d/claude-desktop.repo
# Install
sudo dnf install claude-desktop
```
Future updates will be installed automatically with your regular system updates (`sudo dnf upgrade`).
### Using AUR (Arch Linux)
The [`claude-desktop-appimage`](https://aur.archlinux.org/packages/claude-desktop-appimage) package is available on the AUR and is automatically updated with each release.
```bash
# Using yay
yay -S claude-desktop-appimage
# Or using paru
paru -S claude-desktop-appimage
```
The AUR package installs the AppImage build of Claude Desktop.
### Using Nix Flake (NixOS)
Install directly from the flake:
```bash
# Basic install
nix profile install github:aaddrick/claude-desktop-debian
# With MCP server support (FHS environment)
nix profile install github:aaddrick/claude-desktop-debian#claude-desktop-fhs
```
Or add to your NixOS configuration:
```nix
# flake.nix
{
inputs.claude-desktop.url = "github:aaddrick/claude-desktop-debian";
outputs = { nixpkgs, claude-desktop, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
({ pkgs, ... }: {
nixpkgs.overlays = [ claude-desktop.overlays.default ];
environment.systemPackages = [ pkgs.claude-desktop ];
})
];
};
};
}
```
### Using Pre-built Releases
2025-01-17 11:17:57 +01:00
Download the latest `.deb`, `.rpm`, or `.AppImage` from the [Releases page](https://github.com/aaddrick/claude-desktop-debian/releases).
### Building from Source
See [docs/BUILDING.md](docs/BUILDING.md) for detailed build instructions.
2025-04-03 01:27:06 -04:00
## Configuration
2025-04-03 01:27:06 -04:00
Model Context Protocol settings are stored in:
```
~/.config/Claude/claude_desktop_config.json
```
For additional configuration options including environment variables and Wayland support, see [docs/CONFIGURATION.md](docs/CONFIGURATION.md).
2025-03-29 02:31:42 -04:00
## Troubleshooting
feat: KVM/bwrap isolation backends for cowork mode (#269) * feat: add KVM/bwrap isolation backends for cowork mode Refactor cowork-vm-service.js from monolithic VMManager into a pluggable backend architecture with three isolation levels: - HostBackend: direct execution on host (existing Phase 1 behavior) - BwrapBackend: bubblewrap namespace sandbox (PID/mount isolation) - KvmBackend: full QEMU/KVM VM with vsock, virtiofs, QMP monitor Backend auto-detected at startup (KVM > bwrap > host) or overridden via COWORK_VM_BACKEND env var. Shared helpers extracted for env filtering, arg cleanup, command resolution, and work dir handling. Also: - Add Cowork Mode section to --doctor diagnostics with per-tool checks and distro-specific install hints - Update Patch 4 to extract real win32 file entries for linux bundle manifest (enables app download infrastructure) - Update handover documentation for Phase 2/3 architecture Co-Authored-By: Claude <claude@anthropic.com> * fix: correct KvmBackend vsock port, direction, and kernel cmdline The guest sdk-daemon connects TO the host (CID=2), not the other way around. Confirmed via disassembly of the guest binary: the vsock port is 51234 (0xC822), matching the Hyper-V GUID in the Windows service. - Change VSOCK_GUEST_PORT from 2222 to 51234 - Reverse socat bridge: VSOCK-LISTEN (host listens) instead of VSOCK-CONNECT (host connecting to guest) - Add bridge server to accept persistent guest connection and route events/responses via callback map - Fix kernel cmdline: root=LABEL=cloudimg-rootfs (matches fstab) Co-Authored-By: Claude <claude@anthropic.com> * fix: patch VM download to use disk-backed temp dir on Linux Linux systems often mount /tmp as a small tmpfs (3-4GB). The VM rootfs download decompresses to ~9GB, causing ENOSPC. Patch the app's mkdtemp("wvm-") call to use the bundle directory (on real disk) instead of os.tmpdir() on Linux. Uses regex-based dynamic variable extraction to remain version-agnostic across minified code changes. Co-Authored-By: Claude <claude@anthropic.com> * fix: handle stale cowork socket (ECONNREFUSED) on Linux Stale sockets from previous sessions give ECONNREFUSED instead of ENOENT, bypassing the retry loop and auto-launch entirely. Fix: - Expand ENOENT check in retry loop to include ECONNREFUSED on Linux - Add cleanup_stale_cowork_socket() to launcher scripts (all formats) that removes dead sockets before Electron starts - Increase tmpdir patch search window from 1000 to 2000 chars Co-Authored-By: Claude <claude@anthropic.com> * fix: preserve DNS resolution inside bwrap sandbox On systems using systemd-resolved, /etc/resolv.conf is a symlink to /run/systemd/resolve/stub-resolv.conf. The bwrap --tmpfs /run option wiped this out, breaking DNS resolution inside the sandbox and preventing the spawned Claude process from reaching the API. Bind-mount the resolved /run/systemd/resolve/ directory back into the sandbox as read-only to restore DNS. Co-Authored-By: Claude <claude@anthropic.com> * fix: harden cowork isolation and build patches - Remove broken _setupEventForwarding (events already flow through _handleGuestData); the second bridge connection was silently ignored - Mount $HOME read-only in bwrap sandbox; only workDir and explicit mounts are writable (prevents writes to ~/.ssh, ~/.gnupg, etc.) - Scope Patch 4 win32 extraction to actual win32:{} block via brace counting to avoid crossing into darwin/linux sections - Set _qmpAvailable flag on QMP timeout instead of silently continuing - Wrap CID allocation at 65535 to prevent unbounded growth - Use execFileSync instead of execSync('which ...') in detectBackend - Coerce response ID to String for Map lookup in _handleGuestData - Use non-greedy [\s\S]*? in Patch 6 regex for nested brace robustness - Update patch count threshold from 4 to 5 after adding Patch 8 - Add age-based fallback for stale socket cleanup when socat is missing - Use indexOf-based splice in Patch 8 instead of string.replace() - Extract shared resolveSdkBinary helper to deduplicate SDK resolution - Remove dead retryFuncRe/retryFuncMatch variables from Patch 6 Co-Authored-By: Claude <claude@anthropic.com> * fix: address security and correctness issues from code review - Replace execSync string interpolation with execFileSync for qemu-img calls to eliminate shell injection risk - Add path validation to readFile in both LocalBackend and KvmBackend to restrict reads to within the user's home directory - Fix QMP _sendQmpCommand timer leak by clearing timeout on success - Fix _pendingCallbacks.delete() to use String(msg.id) matching the String(msg.id) used in the .get() lookup - Extract FORWARDED_EVENTS constant, cleanup helper, extractBlock helper, and consolidate doctor tool checks (from simplifier pass) Co-Authored-By: Claude <claude@anthropic.com> * docs: update README cowork notice with isolation backends and doctor info Co-Authored-By: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com>
2026-02-28 21:13:09 -05:00
Run `claude-desktop --doctor` for built-in diagnostics that check common issues (display server, sandbox permissions, MCP config, stale locks, and more). It also reports cowork mode readiness — which isolation backend will be used, and which dependencies (KVM, QEMU, vsock, socat, virtiofsd, bubblewrap) are installed or missing.
For additional troubleshooting, uninstallation instructions, and log locations, see [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md).
2025-04-03 01:27:06 -04:00
## Acknowledgments
2025-04-03 01:27:06 -04:00
This project was inspired by [k3d3's claude-desktop-linux-flake](https://github.com/k3d3/claude-desktop-linux-flake) and their [Reddit post](https://www.reddit.com/r/ClaudeAI/comments/1hgsmpq/i_successfully_ran_claude_desktop_natively_on/) about running Claude Desktop natively on Linux.
2025-04-03 01:27:06 -04:00
Special thanks to:
- **k3d3**
- Original NixOS implementation
- Native bindings insights
- **[emsi](https://github.com/emsi/claude-desktop)**
- Title bar fix
- Alternative implementation approach
- **[leobuskin](https://github.com/leobuskin/unofficial-claude-desktop-linux)** for the Playwright-based URL resolution approach
- **[yarikoptic](https://github.com/yarikoptic)**
- Codespell support
- Shellcheck compliance
- **[IamGianluca](https://github.com/IamGianluca)** for build dependency check improvements
- **[ing03201](https://github.com/ing03201)** for IBus/Fcitx5 input method support
- **[ajescudero](https://github.com/ajescudero)** for pinning @electron/asar for Node compatibility
- **[delorenj](https://github.com/delorenj)** for Wayland compatibility support
- **[Regen-forest](https://github.com/Regen-forest)** for suggesting Gear Lever as AppImageLauncher replacement
- **[niekvugteveen](https://github.com/niekvugteveen)** for fixing Debian packaging permissions
- **[speleoalex](https://github.com/speleoalex)** for native window decorations support
- **[imaginalnika](https://github.com/imaginalnika)** for moving logs to `~/.cache/`
- **[richardspicer](https://github.com/richardspicer)** for the menu bar visibility fix on Linux
- **[jacobfrantz1](https://github.com/jacobfrantz1)**
- Claude Desktop code preview support
- Quick window submit fix
- **[janfrederik](https://github.com/janfrederik)** for the `--exe` flag to use a local installer
- **[MrEdwards007](https://github.com/MrEdwards007)** for discovering the OAuth token cache fix
- **[lizthegrey](https://github.com/lizthegrey)** for version update contributions
- **[mathys-lopinto](https://github.com/mathys-lopinto)**
- AUR package
- Automated deployment
- **[pkuijpers](https://github.com/pkuijpers)** for root cause analysis of the RPM repo GPG signing issue
- **[dlepold](https://github.com/dlepold)** for identifying the tray icon variable name bug with a working fix
- **[Voork1144](https://github.com/Voork1144)**
- Detailed analysis of the tray icon minifier bug
- Root-cause analysis of the Chromium layout cache bug
- Direct child `setBounds()` fix approach
- **[sabiut](https://github.com/sabiut)**
- `--doctor` diagnostic command
- SHA-256 checksum validation for downloads
- Post-build integration tests for deb, rpm, and AppImage artifacts
- **[milog1994](https://github.com/milog1994)**
- Popup detection
- Functional stubs
- Wayland compositor support
- **[jarrodcolburn](https://github.com/jarrodcolburn)**
- Passwordless sudo support in container/CI environments
- Identifying the gh-pages 4GB bloat fix
- Identifying the virtiofsd PATH detection issue on Debian
- Detailed analysis of the CI release pipeline failure caused by runner kills during compare-releases
- Diagnosing the session-start hook sudo blocking issue with three solution approaches
- **[chukfinley](https://github.com/chukfinley)** for experimental Cowork mode support on Linux
- **[CyPack](https://github.com/CyPack)** for orphaned cowork daemon cleanup on startup
- **[IliyaBrook](https://github.com/IliyaBrook)** for fixing the platform patch for Claude Desktop >= 1.1.3541 arm64 refactor
- **[MichaelMKenny](https://github.com/MichaelMKenny)**
- Diagnosing the `$`-prefixed electron variable bug
- Root cause analysis and workaround
- **[daa25209](https://github.com/daa25209)** for detailed root cause analysis of the cowork platform gate crash and patch script
- **[noctuum](https://github.com/noctuum)**
- `CLAUDE_MENU_BAR` env var with configurable menu bar visibility
- Boolean alias support
- **[typedrat](https://github.com/typedrat)**
- NixOS flake integration with build.sh
- node-pty derivation
- CI auto-update
- Fixing the flake package scoping regression
- **[cbonnissent](https://github.com/cbonnissent)**
- Reverse-engineering the Cowork VM guest RPC protocol
- Fixing the KVM startup blocker
- Fixing RPC response id echoing for persistent connections
- Configurable bwrap mount points via a dedicated Linux config file
- **[joekale-pp](https://github.com/joekale-pp)** for adding `--doctor` support to the RPM launcher
- **[ecrevisseMiroir](https://github.com/ecrevisseMiroir)** for the bwrap backend sandbox isolation with tmpfs-based minimal root
- **[arauhala](https://github.com/arauhala)** for detailed root cause analysis of the NixOS `isPackaged` regression
- **[cromagnone](https://github.com/cromagnone)** for confirming the VM download loop on bwrap installs with detailed logs that disproved the initial triage
- **[aHk-coder](https://github.com/aHk-coder)** for diagnosing the hardcoded minified variable crash in the cowork smol-bin patch
- **[RayCharlizard](https://github.com/RayCharlizard)**
- Detailed analysis of the self-referential `.mcpb-cache` symlink ELOOP bug
- Fixing auto-memory path translation on HostBackend
- **[reinthal](https://github.com/reinthal)** for fixing the NixOS build breakage caused by the nixpkgs `nodePackages` removal
- **[gianluca-peri](https://github.com/gianluca-peri)**
- Reporting the GNOME quit accessibility issue
- Confirming tray behavior with AppIndicator
fix: launcher-common.sh self-match and stale socket cleanup (#407) (#425) * fix: launcher-common.sh self-match and stale socket cleanup (#407) Three related bugs in scripts/launcher-common.sh that combine to break Claude Desktop startup after any crash that reparents the cowork daemon on Debian/Ubuntu/Mint systems. 1. cleanup_orphaned_cowork_daemon — the old pgrep pattern 'claude-desktop' self-matches the launcher's own bash process (cmdline `bash /usr/bin/claude-desktop`), causing the function to return early on every invocation. The SIGTERM loop never runs. Replaced with `pgrep -f 'app\.asar'` plus $$/$PPID exclusion, --type= filter (skips chromium helpers), and /proc/*/status check (skips stopped/zombie launcher bashes). Added SIGKILL escalation after ~2s so cleanup_stale_cowork_socket reliably sees no daemon. 2. cleanup_stale_cowork_socket — the old implementation required socat (not preinstalled on Debian/Ubuntu/Mint) and fell through to a find -mmin +1440 check that ignored any socket younger than 24h. Rewritten to use the ordering invariant: cleanup_orphaned_cowork_daemon runs first and kills any orphan, so at this point an extant daemon proves the socket is live and an absent daemon proves the socket is stale. No socat dependency. 3. run_doctor orphan check — same self-match flaw as (1). claude-desktop --doctor reported [PASS] Cowork daemon: running (parent alive) on systems with a genuine orphan, actively misleading users trying to diagnose this failure. Applied the same detection primitive as (1). Complements #410 (daemon-side crash recovery): #410 reduces how often orphans are created; this ensures the launcher actually cleans them up when they are. Fixes #407 Co-Authored-By: martin152 <martin152@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: credit martin152 in Acknowledgments for #407 launcher fix Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style: quote RHS of $$/$PPID comparisons (SC2053) Shellcheck SC2053: quote RHS in [[ ]] equality tests to prevent glob matching. No behavior change — $$ and $PPID are always numeric PIDs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: martin152 <martin152@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:28:22 -05:00
- **[martin152](https://github.com/martin152)** for detailed diagnosis and a complete patch for three launcher cleanup bugs: `cleanup_orphaned_cowork_daemon` self-match, `cleanup_stale_cowork_socket` socat dependency no-op, and the same self-match in `--doctor`
fix: diagnose AppArmor userns block on bwrap probe (#351) (#434) * fix: diagnose AppArmor userns block on bwrap probe (#351) Ubuntu 24.04+ ships apparmor_restrict_unprivileged_userns=1 by default, which blocks the user namespace bwrap needs to start. The daemon's probe then fails, auto-detect silently falls through to KVM, and KVM hangs waiting for a rootfs the user hasn't set up — leaving Cowork stuck in a retry loop with no clear error. - Classify the probe failure (classifyBwrapProbeError) so the daemon can distinguish AppArmor/userns blocks from generic failures and log a pointer to the TROUBLESHOOTING.md remediation. - Stop falling through to KVM when bwrap is installed but blocked; drop to host-direct instead so users see a working (if unsandboxed) Cowork and the reason bwrap didn't engage. Users who actually want KVM can still set COWORK_VM_BACKEND=kvm. - Mirror the probe + diagnosis in `--doctor` so misconfigured systems get the same actionable output without waiting for a daemon log. - Document the AppArmor profile workaround in TROUBLESHOOTING.md. - Credit @hfyeh for the diagnosis and profile snippet. Co-Authored-By: Claude <claude@anthropic.com> * refactor: simplify PR #434 per cdd-code-simplifier Drop redundant `-n` guard around the COWORK_VM_BACKEND case in `--doctor`: the `${VAR,,}` expansion is already safe on an unset var (no `set -u` in this script) and the `kvm|host` arms simply don't match an empty string. Co-Authored-By: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com>
2026-04-19 01:12:13 -05:00
- **[hfyeh](https://github.com/hfyeh)** for diagnosing the Ubuntu 24.04 AppArmor unprivileged-userns block on Cowork bwrap and contributing the AppArmor profile workaround
## Sponsorship
If this project is useful to you, consider [sponsoring on GitHub](https://github.com/sponsors/aaddrick).
## License
The build scripts in this repository are dual-licensed under:
- MIT License (see [LICENSE-MIT](LICENSE-MIT))
- Apache License 2.0 (see [LICENSE-APACHE](LICENSE-APACHE))
The Claude Desktop application itself is subject to [Anthropic's Consumer Terms](https://www.anthropic.com/legal/consumer-terms).
## Contributing
2025-08-09 21:58:44 -04:00
Contributions are welcome! By submitting a contribution, you agree to license it under the same dual-license terms as this project.