A community-driven registry for Claude, Cursor, Windsurf, Cline & more. Not affiliated with Anthropic.
Are you the author? Sign in to claim
Run Claude Desktop’s Cowork mode natively on Linux — no macOS or VM required
Claude Cowork is a special Claude Desktop build that works inside a folder you point it at—it reads, writes, and organizes files there while it runs a plan. Cowork is currently a macOS-only preview backed by a sandboxed Linux VM; this repo reverse-engineers and stubs the macOS-native pieces so Cowork can run directly on Linux (x86_64)—no VM and no macOS required. The stub translates VM paths to host paths so Cowork points at the right files on Linux.
How it works:
| Step | Description |
|---|---|
Replace macOS-only native modules (@ant/claude-swift, @ant/claude-native) with JavaScript | |
| Run the Claude Code binary directly (no VM needed—we're already on Linux!) | |
| Convert VM paths to host paths transparently | |
| Send macOS headers so the server enables the feature |
$WAYLAND_DISPLAY / $XDG_SESSION_TYPE (Ozone backend).| Distro | Desktop | Status | Notes |
|---|---|---|---|
| Arch Linux | Hyprland (Wayland) | Tested | Primary dev environment |
| Arch Linux | KDE Plasma (Wayland) | Expected | KDE Wallet exposed via SecretService D-Bus |
| Arch Linux | GNOME (Wayland) | Expected | Global shortcuts require manual DE config (GNOME lacks portal support) |
| Ubuntu 22.04+ | GNOME / X11 | Expected | gnome-keyring provides SecretService |
| Fedora 39+ | GNOME / KDE | Expected | May need p7zip-plugins for DMG extraction |
| Debian 12+ | Any | Expected | p7zip-full in apt |
| NixOS | Any | Untested | Electron + bwrap sandboxing may need extra config |
| openSUSE | Any | Tested | Uses 7zip package (not p7zip); nodejs-default for Node.js |
Known caveats:
GlobalShortcuts portal (GNOME) won't have global hotkey support -- set a custom shortcut in your DE settings instead.gnome-keyring or another SecretService provider isn't running, the launcher falls back to --password-store=basic (credentials stored on disk, not in a keyring)./sessions root symlink requires sudo once during install. If your distro restricts root symlinks differently, point it manually: sudo ln -s "$HOME/.config/Claude/local-agent-mode-sessions/sessions" /sessions.Run ./install.sh --doctor (or claude-desktop --doctor) after install to validate your environment.
npm install -g @electron/asar)7zip instead)enable-cowork.py patching — the installer uses Node.js to download DMGs)--password-store=basic.git clone https://github.com/johnzfitch/claude-cowork-linux.git
cd claude-cowork-linux
./install.sh # prompts before downloading the Claude Desktop asar
claude-desktop
The installer prompts before downloading the Claude Desktop asar so you can confirm the version. See COMPAT.md for the tested-versions matrix. Pass --force (or --yes) to skip the prompt for scripted installs.
yay -S claude-cowork-linux
bash <(curl -fsSL https://raw.githubusercontent.com/johnzfitch/claude-cowork-linux/master/install.sh)
Piped (non-interactive) installs refuse to download without an explicit confirmation flag. Use:
bash <(curl -fsSL .../install.sh) --force
You can also provide an archive manually:
./install.sh ~/Downloads/Claude-*.dmg
# or
CLAUDE_ARCHIVE=~/Downloads/Claude-1.6259.1.dmg ./install.sh
NixOS can't use install.sh directly: its package-manager branch runs imperative nix-env -iA, and the npm electron it would otherwise install is a prebuilt ELF that won't run on NixOS. The flake builds the app against electron_41 from nixpkgs instead.
# Run without installing
nix run github:johnzfitch/claude-cowork-linux
# Or add to a NixOS / home-manager config
# environment.systemPackages = [ inputs.claude-cowork-linux.packages.${system}.default ];
The derivation extracts and patches the pinned Claude release at build time, then a wrapper mirrors the tree into $XDG_DATA_HOME/claude-cowork-linux (writable) and runs the upstream launch.sh with all dependencies on PATH. The pinned version lives in nix/package.nix; override claudeVersion/claudeUrl/claudeHash to track a different release.
A nix develop shell is also provided with every dependency install.sh needs, for the manual installer flow.
claude-desktop --update # re-fetch and repack with prompt (install.sh flow)
The launcher prints a [WARN] line once if your installed asar is newer than the last tested version listed in COMPAT.md. To dismiss the warning permanently for the current version, just keep using the app -- it only fires once per asar version change.
[!IMPORTANT] This repo does not include Anthropic's proprietary code. The installer downloads it directly from Anthropic's CDN.
┌─────────────────────────────────────────────────────────────────┐
│ Claude Desktop (Electron) │
├─────────────────────────────────────────────────────────────────┤
│ @ant/claude-swift (STUBBED) │
│ ├── vm.setEventCallbacks() → Register process event handlers │
│ ├── vm.startVM() → No-op (we're already on Linux) │
│ ├── vm.spawn() → Delegates to session orchestrator │
│ ├── vm.kill() → Kills spawned processes │
│ └── vm.writeStdin() → Writes to process stdin │
├─────────────────────────────────────────────────────────────────┤
│ @ant/claude-native (STUBBED) │
│ ├── AuthRequest → Opens system browser (xdg-open) │
│ └── Platform helpers → Minimal compatibility shims │
├─────────────────────────────────────────────────────────────────┤
│ stubs/cowork/ — Orchestration Layer (15 modules) │
│ ├── session_orchestrator.js → Coordinates spawn lifecycle │
│ ├── asar_adapter.js → Asar IPC API compatibility │
│ ├── process_manager.js → Process lifecycle & I/O │
│ ├── resume_coordinator.js → Session resume logic │
│ ├── sessions_api.js → Session CRUD operations │
│ ├── session_store.js → In-memory session state │
│ ├── transcript_store.js → Transcript persistence │
│ ├── file_registry.js → Working directory tracking │
│ ├── file_watch_manager.js → File change detection │
│ ├── stream_protocol.js → JSON-RPC stream parsing │
│ ├── credential_classifier.js → Token leak prevention │
│ ├── eipc_channel.js → EIPC message protocol │
│ ├── ipc_tap.js → IPC channel discovery │
│ ├── dirs.js → XDG directory resolution │
│ └── file_identity.js → Path normalization │
├─────────────────────────────────────────────────────────────────┤
│ Claude Code Binary │
│ └── Resolved from ~/.local/bin, mise/asdf shims, PATH, etc. │
│ (launch.sh replaces macOS Mach-O binary with Linux symlink)│
└─────────────────────────────────────────────────────────────────┘
The stub translates VM paths to host paths:
| VM Path | Host Path |
|---|---|
/usr/local/bin/claude or claude | Resolved via ~/.local/bin/claude, ~/.config/Claude/claude-code-vm/{version}/claude, or PATH |
/sessions/... | ~/.config/Claude/local-agent-mode-sessions/sessions/... |
When you select a folder in Cowork, the stub creates symlinks to make it accessible at the expected VM path:
~/.config/Claude/local-agent-mode-sessions/sessions/<session-name>/mnt/
├── <folder> → /home/user/path/to/selected/folder (symlink)
├── .claude → ~/.config/Claude/local-agent-mode-sessions/.../session/.claude (symlink)
├── .skills → ~/.config/Claude/local-agent-mode-sessions/skills-plugin/... (symlink)
└── uploads/ (directory for file uploads)
The additionalMounts parameter from Claude Desktop provides the mapping between mount names and host paths.
[!NOTE] The Claude Code binary expects
/sessionsto exist.install.shcreates/sessionsas a symlink into~/.config/Claude/local-agent-mode-sessions/sessions(requiressudoonce) so you don't need a world-writable root directory.
The app sends these headers to Anthropic's servers:
'Anthropic-Client-OS-Platform': 'darwin'
'Anthropic-Client-OS-Version': '14.0'
This makes the server think we're on macOS 14 (Sonoma), enabling Cowork features.
The platform-gate function (minified name changes per build — xPt() in v1.1.3963, wj() in older builds) checks if Cowork is supported. enable-cowork.py finds it automatically and replaces it to unconditionally return {status: "supported"}.
The original @ant/claude-swift uses Apple's Virtualization Framework. Our stub:
session_orchestrator.js for proper lifecycle managementKey insight: The app calls Si() which returns module.default.vm, so methods must be on the vm object.
The app also expects @ant/claude-native (a macOS-specific native module). Our stub provides minimal compatibility so the app can start on Linux. For example, OAuth flows fall back to opening the system browser via xdg-open.
The stubs/cowork/ orchestration layer provides 15 modules that handle session lifecycle, IPC communication, transcript persistence, and security:
ipcMain._invokeHandlers.set()~/.config/Claude/local-agent-mode-sessions/All modules follow XDG Base Directory conventions and are tested with 215+ test cases.
On macOS, Cowork runs a Linux VM. On Linux, we skip the VM entirely and run the Claude Code binary directly on the host. This is actually simpler and faster!
The stub resolves the binary in priority order:
$CLAUDE_CODE_PATH (explicit override)
~/.config/Claude/claude-code-vm/{version}/claude (downloaded by Desktop)
~/.local/bin/claude (npm/bun global)
~/.npm-global/bin/claude
/usr/local/bin/claude
/usr/bin/claude
/home/linuxbrew/.linuxbrew/bin/claude (Linuxbrew system)
~/.linuxbrew/bin/claude (Linuxbrew user)
~/.local/share/mise/shims/claude (mise version manager)
~/.asdf/shims/claude (asdf version manager)
Code tab binary fixup: launch.sh automatically detects if the Claude Code binary in the asar is a macOS Mach-O binary and replaces it with a symlink to the host Linux binary. This enables the Code tab to work seamlessly.
claude-cowork-linux/
├── stubs/
│ ├── @ant/claude-swift/js/index.js # Primary stub: vm.spawn() → delegates to orchestrator
│ ├── @ant/claude-native/index.js # Auth (xdg-open), keyboard constants, platform helpers
│ ├── cowork/ # Orchestration layer (15 modules)
│ │ ├── session_orchestrator.js # Spawn lifecycle coordinator
│ │ ├── asar_adapter.js # Asar IPC API compatibility
│ │ ├── process_manager.js # Process lifecycle & I/O
│ │ ├── resume_coordinator.js # Session resume logic
│ │ ├── sessions_api.js # Session CRUD operations
│ │ ├── session_store.js # In-memory session state
│ │ ├── transcript_store.js # Transcript persistence
│ │ ├── file_registry.js # Working directory tracking
│ │ ├── file_watch_manager.js # File change detection
│ │ ├── stream_protocol.js # JSON-RPC stream parsing
│ │ ├── credential_classifier.js # Token leak prevention
│ │ ├── eipc_channel.js # EIPC message protocol
│ │ ├── ipc_tap.js # IPC channel discovery
│ │ ├── dirs.js # XDG directory resolution
│ │ └── file_identity.js # Path normalization
│ └── frame-fix/
│ ├── frame-fix-wrapper.js # Early bootstrap: TMPDIR fix, platform spoofing, graceful shutdown
│ └── frame-fix-entry.js # Entry point: loads frame-fix-wrapper then main index.js
├── tests/
│ ├── node/current-path/ # 18 test files, 215+ node:test cases
│ │ ├── asar_adapter.test.cjs
│ │ ├── credential_classifier.test.cjs
│ │ ├── dirs.test.cjs
│ │ ├── eipc_channel.test.cjs
│ │ ├── file_identity.test.cjs
│ │ ├── file_registry.test.cjs
│ │ ├── file_watch_manager.test.cjs
│ │ ├── ipc_tap.test.cjs
│ │ ├── process_manager.test.cjs
│ │ ├── resume_coordinator.test.cjs
│ │ ├── session_orchestrator.test.cjs
│ │ ├── session_store.test.cjs
│ │ ├── sessions_api.test.cjs
│ │ ├── stream_protocol.test.cjs
│ │ ├── transcript_store.test.cjs
│ │ └── ... (integration tests)
│ ├── test-install-paths.sh # 8-stage install validation (static analysis → Docker)
│ └── Dockerfile.test # Arch Linux container for full install testing
├── scripts/
│ ├── fetch-dmg.js # Auto-download Claude DMG via Node.js fetch
│ └── enable-cowork.py # Patches platform gate to return {status:"supported"}
├── docs/
│ ├── FAQ.md # Detailed troubleshooting guide
│ ├── extensions.md # MCP and Chrome Extension integration overview
│ ├── known-issues.md # Safe Storage encryption, keyring setup
│ └── safestorage-tokens.md # How to persist tokens across restarts
├── config/
│ └── hyprland/claude.conf # Optional: Hyprland window rules
├── .github/assets/ # README icons and hero image
├── install.sh # Installer + --doctor preflight diagnostics
├── launch.sh # Launcher: syncs stubs, repacks asar, runs electron
├── launch-devtools.sh # Launcher with --inspect (Node.js DevTools)
├── validate.sh # Env var checks, stub URL validation, log scanning
├── PKGBUILD # Arch Linux AUR package definition
├── docs/releases/ # Per-version release notes
├── docs/OAUTH-COMPLIANCE.md # OAuth token handling audit
├── CLAUDE.md # Project guide and critical paths
├── README.md # This file
└── LICENSE
After running install.sh, the linux-app-extracted/ directory will contain the extracted Claude Desktop.
If the automated installer doesn't work, follow these steps:
The installer handles app.asar extraction automatically. For manual extraction or older unpacked versions:
# Extract DMG with 7z
7z x Claude-*.dmg -o/tmp/claude-extract
# Create app directory
mkdir -p linux-app-extracted
# For newer versions (app.asar):
if [ -f "/tmp/claude-extract/Claude/Claude.app/Contents/Resources/app.asar" ]; then
npx --yes asar extract "/tmp/claude-extract/Claude/Claude.app/Contents/Resources/app.asar" linux-app-extracted
# Copy unpacked files if they exist
[ -d "/tmp/claude-extract/Claude/Claude.app/Contents/Resources/app.asar.unpacked" ] && \
cp -r "/tmp/claude-extract/Claude/Claude.app/Contents/Resources/app.asar.unpacked/"* linux-app-extracted/
# For older versions (unpacked app/):
elif [ -d "/tmp/claude-extract/Claude/Claude.app/Contents/Resources/app" ]; then
cp -r "/tmp/claude-extract/Claude/Claude.app/Contents/Resources/app/"* linux-app-extracted/
fi
# Cleanup
rm -rf /tmp/claude-extract
# Copy our stubs over the original modules
cp -r stubs/@ant/* linux-app-extracted/node_modules/@ant/
cp -r stubs/cowork linux-app-extracted/node_modules/
Run the cowork patch (auto-detects the minified function name):
python3 scripts/enable-cowork.py linux-app-extracted/.vite/build/index.js
# Create user session directory
mkdir -p "$HOME/.config/Claude/local-agent-mode-sessions/sessions"
chmod 700 "$HOME/.config/Claude/local-agent-mode-sessions/sessions"
# Create symlink (requires sudo once)
sudo ln -s "$HOME/.config/Claude/local-agent-mode-sessions/sessions" /sessions
# System package (preferred)
# Arch: pacman -S electron
# Ubuntu/Debian: apt install electron
# Or via npm:
npm install -g electron @electron/asar
If your install was working and then stopped after an update, the upstream asar likely changed in a way claude-cowork-linux has not yet caught up to. Two recovery paths:
The installer reads LAST_TESTED_ASAR_VERSION from COMPAT.md
and prompts before downloading. To roll back:
cd ~/.local/share/claude-desktop
git pull
# Wipe cached chromium artifacts (preserves your sessions and credentials):
rm -rf ~/.config/Claude/Cache \
"~/.config/Claude/Code Cache" \
~/.config/Claude/GPUCache \
~/.config/Claude/DawnGraphiteCache
# Reinstall:
bash install.sh --force
When the prompt offers to download an untested version, decline and supply the last-tested archive manually:
CLAUDE_ARCHIVE=/path/to/Claude-<last-tested>.dmg bash install.sh --force
The launcher prints a [WARN] line on the first launch after an untested
version is installed. If the warning appears and the app is broken for
you, the issue is likely already being tracked. Search the open issues at
https://github.com/johnzfitch/claude-cowork-linux/issues for the asar
version in the warning.
To update later once a fix is published:
claude-desktop --update
You will be prompted before the new asar is downloaded.
claude-desktop --doctor
Reports the installed-vs-tested asar versions plus the rest of the
preflight checks. An [OK] Asar version: X (matches last tested) line
means you are on the verified baseline. A [WARN] Asar version: X (newer than last tested Y -- see COMPAT.md) line means you are ahead of the
matrix.
For detailed troubleshooting guides, see docs/FAQ.md.
Check that the Cowork patch is present in linux-app-extracted/.vite/build/index.js:
grep -q 'cowork-patched' linux-app-extracted/.vite/build/index.js && echo "✓ Cowork patch applied" || echo "✗ Patch missing - run ./install.sh"
The patch replaces the platform-gate function to return {status:"supported"} unconditionally, enabling Cowork on Linux. The /*cowork-patched*/ marker indicates successful patching.
Create a symlink to user space instead of a world-writable directory:
mkdir -p "$HOME/.config/Claude/local-agent-mode-sessions/sessions"
sudo ln -s "$HOME/.config/Claude/local-agent-mode-sessions/sessions" /sessions
JSON parsing issue. The stub uses line buffering to send complete JSON objects. If this persists, check the trace log:
cat ~/.local/state/claude-cowork/logs/claude-swift-trace.log
Run claude-desktop --doctor first to check your environment. Then verify:
[claude-swift-stub] LOADING MODULE in logs)~/.local/bin/claude, ~/.config/Claude/claude-code-vm/{version}/claude, etc.)Check stderr in the trace log for the actual error:
tail -50 ~/.local/state/claude-cowork/logs/claude-swift-trace.log
Common issues:
/sessions symlinkThis means the stub isn't exporting methods correctly. The app expects:
module.default.vm.setEventCallbacks() — NOT on the class directlyEnsure the stub has methods on the this.vm object, not just the class.
A previous instance may not have shut down cleanly, leaving a stale lock file. Clear it and relaunch:
rm -f ~/.config/Claude/SingletonLock ~/.config/Claude/SingletonSocket ~/.config/Claude/SingletonCookie
claude-desktop
The app routes global shortcuts through the org.freedesktop.portal.GlobalShortcuts portal on Wayland (Electron's native globalShortcut relies on X11 XGrabKey, which Wayland doesn't expose). This works on KDE Plasma, Hyprland, COSMIC, and GNOME 48+ — xdg-desktop-portal-gnome implements the GlobalShortcuts portal since version 48 (refined in 50). The shortcut shows up under Settings > Keyboard and may prompt for confirmation the first time it's registered.
Workaround for GNOME < 48 (and compositors without the portal): Set a custom shortcut in GNOME Settings > Keyboard > Custom Shortcuts to launch claude-desktop.
Cowork runs on WSL2 (tested with WSLg on Ubuntu). Four things to know — ./install.sh --doctor checks all of them:
npm package conflicts with it. The installer installs packages individually so one conflict no longer blocks the rest (zstd, curl, …); NodeSource's nodejs already bundles npm.sudo apt install -y libnspr4 libnss3 libasound2t64 (or libasound2 before Ubuntu 24.04).xdg-open forwards URLs to Windows via interop, so the claude:// login callback never reaches the Linux app. The installer offers an opt-in wrapper at ~/.local/bin/xdg-open that routes claude:// to claude-desktop and http(s) to a Linux browser. Install it any time with bash install.sh --wsl-xdg-open; remove with rm ~/.local/bin/xdg-open. You'll need a Linux browser (install Chrome/Firefox via .deb/apt, not snap) for the OAuth round-trip under WSLg.gnome-keyring makes logins persist../launch.sh # repacks asar automatically if stubs changed
./launch-devtools.sh # with Node.js inspector
./validate.sh # env var checks, stub URL validation, log errors
./install.sh --doctor # preflight: binaries, node, CLI, /sessions, secret service, patches
# Run tests
node --test tests/node/current-path/*.test.cjs
# Include Claude Code stdout/stderr in the trace log (redacted, but still treat logs as sensitive)
export CLAUDE_COWORK_TRACE_IO=1
# Enable debug mode
export CLAUDE_COWORK_DEBUG=1
# Enable Electron logging
export ELECTRON_ENABLE_LOGGING=1
# Clear old logs
rm -f ~/.local/state/claude-cowork/logs/claude-swift-trace.log
# Run with output capture
./launch.sh 2>&1 | tee /tmp/claude-full.log
# In another terminal, watch the trace
tail -f ~/.local/state/claude-cowork/logs/claude-swift-trace.log
The stub writes to ~/.local/state/claude-cowork/logs/claude-swift-trace.log:
[timestamp] === MODULE LOADING ===
[timestamp] vm.setEventCallbacks() CALLED
[timestamp] vm.startVM() bundlePath=... memoryGB=4
[timestamp] vm.spawn() id=... cmd=... args=[...]
[timestamp] Translated command: /usr/local/bin/claude -> ~/.config/Claude/...
[timestamp] stdout line: {"type":"stream_event",...}
[timestamp] Process ... exited: code=0
This project includes security hardening:
vm.spawn(); unknown commands are rejectedexecFile() instead of exec()isPathSafe()Auth_$_doAuthInBrowser and AuthRequest.start() enforce Anthropic-only domainsBLOCKED_ENV_KEY_PATTERN + CREDENTIAL_EXEMPT_KEYS prevent token leakage to subprocessescredential_classifier.js enforces strict token leak prevention with allowlist-based exemptions[!CAUTION] This project is for educational and research purposes. Claude Desktop is proprietary software owned by Anthropic PBC. Use of Cowork requires a valid Claude account.
This repository contains only stub implementations and patches—not the Claude Desktop application itself. You must obtain Claude Desktop directly from Anthropic.
This project is not affiliated with, endorsed by, or sponsored by Anthropic. "Claude" is a trademark of Anthropic PBC.
Reverse engineered and implemented by examining the Claude Desktop Electron app structure, binary analysis with pyghidra-lite, and iterative debugging.
Contributors:
MIT License · See LICENSE for details
An AI-powered custom node for ComfyUI designed to enhance workflow automation and provide intelligent assistance
Deterministic multi-agent pipeline for end-to-end software development, orchestrating CLI-based AI tools (e.g. Gemini, C
💻 A curated list of papers and resources for multi-modal Graphical User Interface (GUI) agents.
Native macOS app to monitor Claude AI usage limits and watch your coding sessions live