{"id":47685812,"url":"https://github.com/techtoboggan/claude-desktop-hardened-linux","last_synced_at":"2026-06-11T23:00:38.516Z","repository":{"id":346911763,"uuid":"1192133110","full_name":"techtoboggan/claude-desktop-hardened-linux","owner":"techtoboggan","description":"Security-focused Linux packaging of Claude Desktop with bubblewrap sandboxing, Wayland support, and Cowork (Local Agent Mode)","archived":false,"fork":false,"pushed_at":"2026-06-11T15:07:21.000Z","size":3091335,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T17:08:12.595Z","etag":null,"topics":["bubblewrap","claude","claude-desktop","electron","linux","sandbox","security","wayland"],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/techtoboggan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":"techtoboggan","thanks_dev":null,"custom":null}},"created_at":"2026-03-25T23:27:25.000Z","updated_at":"2026-06-11T16:57:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/techtoboggan/claude-desktop-hardened-linux","commit_stats":null,"previous_names":["techtoboggan/claude-desktop-linux","techtoboggan/claude-desktop-hardened-linux"],"tags_count":155,"template":false,"template_full_name":null,"purl":"pkg:github/techtoboggan/claude-desktop-hardened-linux","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techtoboggan%2Fclaude-desktop-hardened-linux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techtoboggan%2Fclaude-desktop-hardened-linux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techtoboggan%2Fclaude-desktop-hardened-linux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techtoboggan%2Fclaude-desktop-hardened-linux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/techtoboggan","download_url":"https://codeload.github.com/techtoboggan/claude-desktop-hardened-linux/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techtoboggan%2Fclaude-desktop-hardened-linux/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34221150,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bubblewrap","claude","claude-desktop","electron","linux","sandbox","security","wayland"],"created_at":"2026-04-02T14:49:46.894Z","updated_at":"2026-06-11T23:00:38.449Z","avatar_url":"https://github.com/techtoboggan.png","language":"Shell","funding_links":["https://buymeacoffee.com/techtoboggan"],"categories":[],"sub_categories":[],"readme":"***This is an unofficial, community-maintained project. It is not affiliated with or endorsed by Anthropic.***\n\nThis project repackages the official Claude Desktop release for Linux with additional security hardening (bubblewrap sandboxing, credential redaction, permission-gated Computer Use). Anthropic does not provide support for this build — if you run into an issue, [open an issue on our tracker](https://github.com/techtoboggan/claude-desktop-hardened-linux/issues), not Anthropic's.\n\n# Claude Desktop for Linux (Hardened)\n\nA security-focused Linux build of Claude Desktop. Downloads the official release, applies bubblewrap sandboxing, credential redaction, and permission-gated Computer Use — then packages it for Fedora, Debian/Ubuntu, and Arch Linux.\n\n## Features\n\n- **Cowork / Local Agent Mode** — sandboxed via [bubblewrap](https://github.com/containers/bubblewrap) with default-deny filesystem, resource limits, credential redaction, and environment allowlisting\n- **Computer Use** — screenshot, click, type, and scroll automation for both X11 and Wayland, gated by a per-session permission dialog (no auto-grant)\n- **MCP** (Model Context Protocol) — configure servers in `~/.config/Claude/claude_desktop_config.json`\n- **Ctrl+Alt+Space** quick entry popup\n- **System tray** with auto-inverted icons for dark themes\n- **Native Wayland support** — auto-detected, with proper taskbar pinning, window grouping, and Ozone platform hints\n- **Bundled Claude Code CLI** — `claude` command available system-wide after install\n- **Diagnostic tool** — `claude-desktop-hardened --doctor` checks your system for missing dependencies and misconfigurations\n\n---\n\n## Installation\n\n### Fedora (COPR)\n\nAvailable from [Fedora COPR](https://copr.fedorainfracloud.org/coprs/techtoboggan/claude-desktop-hardened/) for Fedora 43 and 44:\n\n```bash\nsudo dnf copr enable techtoboggan/claude-desktop-hardened\nsudo dnf install claude-desktop-hardened\n```\n\nUpdates automatically with `sudo dnf upgrade`.\n\n### Arch Linux (AUR)\n\n```bash\nyay -S claude-desktop-hardened-bin\n```\n\nOr manually:\n\n```bash\ngit clone https://aur.archlinux.org/claude-desktop-hardened-bin.git\ncd claude-desktop-hardened-bin\nmakepkg -si\n```\n\n### Debian / Ubuntu (APT)\n\n```bash\ncurl -fsSL https://techtoboggan.github.io/claude-desktop-hardened-linux/pubkey.asc | sudo gpg --dearmor -o /usr/share/keyrings/claude-desktop-hardened.gpg\necho \"deb [signed-by=/usr/share/keyrings/claude-desktop-hardened.gpg] https://techtoboggan.github.io/claude-desktop-hardened-linux stable main\" | sudo tee /etc/apt/sources.list.d/claude-desktop-hardened.list\nsudo apt update\nsudo apt install claude-desktop-hardened\n```\n\n### Quick install (any distro)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/techtoboggan/claude-desktop-hardened-linux/main/install.sh | bash\n```\n\nDetects your distro, downloads the latest release from GitHub, verifies SHA256 checksums, and installs it.\n\n### Manual install\n\nDownload the latest package from [Releases](https://github.com/techtoboggan/claude-desktop-hardened-linux/releases):\n\n```bash\n# Fedora / RHEL / Rocky\nsudo dnf install claude-desktop-hardened-*.rpm\n\n# Debian / Ubuntu\nsudo dpkg -i claude-desktop-hardened_*.deb \u0026\u0026 sudo apt-get install -f\n\n# Arch Linux\nsudo pacman -U claude-desktop-hardened-*.pkg.tar.zst\n```\n\n### Build from source\n\n```bash\ngit clone https://github.com/techtoboggan/claude-desktop-hardened-linux.git\ncd claude-desktop-hardened-linux\n\n# Auto-detects your distro and builds the right package\nsudo ./build.sh\n\n# Or specify a format explicitly\nsudo FORMAT=rpm ./build.sh\nsudo FORMAT=deb ./build.sh\nsudo FORMAT=arch ./build.sh\n```\n\nRequires Node.js 18-23, npm, and root/sudo access. Build dependencies are installed automatically.\n\n---\n\n## Supported distros\n\n| Family | Distros | Package | Repo |\n|--------|---------|---------|------|\n| RPM | Fedora 43/44 | `.rpm` | [COPR](https://copr.fedorainfracloud.org/coprs/techtoboggan/claude-desktop-hardened/) |\n| RPM | RHEL, CentOS, Rocky, AlmaLinux, Nobara | `.rpm` | [GitHub Releases](https://github.com/techtoboggan/claude-desktop-hardened-linux/releases) |\n| DEB | Debian, Ubuntu, Pop!_OS, Linux Mint | `.deb` | [APT repo](https://techtoboggan.github.io/claude-desktop-hardened-linux) |\n| Arch | Arch Linux, Manjaro, EndeavourOS, CachyOS | `.pkg.tar.zst` | [AUR](https://aur.archlinux.org/packages/claude-desktop-hardened-bin) |\n\nx86_64 only.\n\n---\n\n## Post-install\n\n### Verify your setup\n\n```bash\nclaude-desktop-hardened --doctor\n```\n\nChecks Electron, chrome-sandbox permissions, bubblewrap, display server, Computer Use tools, MCP config, Claude Code CLI, Node.js, and keyring availability.\n\n### Computer Use tools (optional)\n\nInstall the tools for your display server to enable Computer Use:\n\n**Wayland** (GNOME, KDE Plasma, Sway, Hyprland):\n```bash\n# Fedora\nsudo dnf install grim slurp wl-clipboard ydotool wlr-randr\n\n# Debian / Ubuntu\nsudo apt install grim slurp wl-clipboard ydotool wlr-randr\n\n# Arch\nsudo pacman -S grim slurp wl-clipboard ydotool wlr-randr\n```\n\n**X11**:\n```bash\n# Fedora\nsudo dnf install wmctrl xdotool scrot xclip xrandr\n\n# Debian / Ubuntu\nsudo apt install wmctrl xdotool scrot xclip x11-xserver-utils\n\n# Arch\nsudo pacman -S wmctrl xdotool scrot xclip xorg-xrandr\n```\n\n### Keyboard shortcuts on Wayland\n\nWayland does not allow applications to register global keyboard shortcuts (like Ctrl+Alt+Space) — this is a security feature of the protocol. The launcher enables the `GlobalShortcutsPortal` Electron feature flag, which works on **KDE Plasma** and **Hyprland** (users assign the key in system settings).\n\nFor compositors without portal support (GNOME, Sway), bind a shortcut manually:\n\n```bash\n# Hyprland (~/.config/hypr/hyprland.conf)\nbind = CTRL ALT, Space, exec, claude-desktop-hardened --focus\n\n# Sway (~/.config/sway/config)\nbindsym Ctrl+Alt+Space exec claude-desktop-hardened --focus\n\n# i3 (~/.config/i3/config)\nbindsym Ctrl+Alt+space exec claude-desktop-hardened --focus\n```\n\nRun `claude-desktop-hardened --doctor` to check if your compositor supports the GlobalShortcuts portal.\n\n### MCP servers\n\nConfigure MCP servers in `~/.config/Claude/claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"example\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/server.js\"]\n    }\n  }\n}\n```\n\n---\n\n## Using a custom model backend\n\nPoint Code / Cowork sessions at your own model backend — a local LLM via **LM Studio** or **Ollama**, a routing proxy like **LiteLLM** or **OpenRouter**, or a self-hosted **vLLM** server — instead of Anthropic's default endpoint.\n\n\u003e **Scope:** this override applies to **Code / Cowork (agent) mode only**. Conversation mode keeps using `claude.ai` because that UI is a hosted web app, not something we can redirect to a different frontend.\n\n### Backend toggle in the title bar\n\nThere's a segmented chip in the top-left of the title bar (right next to the Claude icon) showing both backend options side-by-side. The active one is highlighted, the other is faded:\n\n```\n[ Anthropic | qwen35-4bit ]\n```\n\n- 🟢 **green** pill = local backend active\n- 🟡 **amber** pill = Anthropic (default) active\n- ⚫ **dim \"Local (not set)\"** = no local backend configured yet\n\n**Hover** for a native tooltip with the full URL, model, and source (shell env vs config file). **Click** either pill to switch — the new backend applies to the **next Code session you start**. Currently-running sessions keep their original env vars (we don't swap mid-conversation).\n\n**Real-time refresh:** changes from `--use-local` / `--toggle-backend` / direct JSON edits to `~/.config/Claude/custom-backend.json` update the chip in the running app immediately (we `fs.watch` the file).\n\n### Third-Party Inference setup (conversation mode too!) 🎉\n\nRecent Claude Desktop builds ship a **hidden third-party model provider setup window** — full OAuth + MCP integration that affects both conversation and Code modes. It's gated in the upstream Help menu behind a dev settings flag, but **our title bar chip bypasses the gate**: click the dim \"Local (not set)\" pill, or right-click either pill, to open the setup window directly.\n\nThe setup window (900×720) is Anthropic's own UI for configuring custom inference providers. It handles:\n\n- **OAuth flow** for providers that require it (with HTTPS enforcement and encrypted token storage)\n- **Enterprise config** schema for BYOK deployments\n- **Custom HTTP headers** per provider\n- **MCP-based integration** so the app's normal tool + model pipeline routes through your provider\n\nThis is **Anthropic-authored infrastructure** — no reverse engineering, just flipping a flag they already built. Unlike our env-var-only override (which only affects Code mode), configuring a provider here makes **conversation mode use your backend too**.\n\n\u003e ⚠️ This is a hidden feature that Anthropic hasn't officially launched. No TODOs or \"experimental\" gates in the code, but treat it as unofficial: Anthropic could change the setup flow, config format, or remove the feature in a future release.\n\n**Alternative access paths:**\n\n```bash\n# Unlock the upstream menu item (Help → Configure Third-Party Inference…).\n# Requires app restart. Not needed if you use the chip — just a convenience\n# if you prefer the menu.\nclaude-desktop-hardened --enable-third-party-setup\n```\n\nOr edit `~/.config/Claude/developer_settings.json` directly:\n\n```json\n{ \"allowDevTools\": true }\n```\n\n### Two ways to configure\n\n**1. CLI flags (quickest for trying it out):**\n\n```bash\nclaude-desktop-hardened --model claude-sonnet-4-5-20250929 \\\n                       --base-url http://localhost:4000\n```\n\nFlags are consumed by the launcher and forwarded as env vars to the Code CLI. They don't get passed to Electron.\n\n\u003e **Tier mapping:** The `--model` flag also sets `ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL` and `ANTHROPIC_SMALL_FAST_MODEL` to the same value (unless you've explicitly set those env vars first). This is required because the UI's model picker spawns the CLI with `--model sonnet` (etc.), which would otherwise override your `ANTHROPIC_MODEL`. With tier aliasing, whichever tier you click in the UI resolves to your chosen backend model.\n\n\u003e **Persistence:** `--model` and `--base-url` also write to `~/.config/Claude/custom-backend.json` so the title bar toggle remembers them. Run once with the flags, then toggle on/off from the UI going forward without needing the flags again.\n\n**Keyboard-shortcut-friendly CLI flags:**\n\n```bash\nclaude-desktop-hardened --toggle-backend   # flip on/off (binds nicely to a shortcut)\nclaude-desktop-hardened --use-local        # force local backend on\nclaude-desktop-hardened --use-anthropic    # force Anthropic backend\n```\n\nThese exit immediately after updating the config, so you can bind them in your WM / DE:\n\n```bash\n# Hyprland (~/.config/hypr/hyprland.conf)\nbind = SUPER SHIFT, L, exec, claude-desktop-hardened --toggle-backend\n```\n\n**2. Shell env vars (persistent — put in `~/.bashrc`, `~/.zshrc`, or `~/.config/fish/config.fish`):**\n\n```bash\nexport ANTHROPIC_BASE_URL=http://localhost:4000\nexport ANTHROPIC_AUTH_TOKEN=sk-your-backend-key\nexport ANTHROPIC_MODEL=claude-sonnet-4-5-20250929\n```\n\nEnv vars are the recommended path for daily use. Secrets (API keys, auth tokens) are deliberately **not** accepted as CLI flags — they'd leak into `ps aux` and shell history.\n\n### Provider recipes\n\nEach block shows the env var form. Swap `export FOO=bar` for `claude-desktop-hardened --foo bar` equivalents as needed.\n\n**LiteLLM proxy** — the most common multi-provider setup:\n\n```bash\n# Terminal 1: start the proxy\npip install litellm\nlitellm --port 4000 --model claude-sonnet-4-5-20250929\n\n# Terminal 2: point the app at it\nexport ANTHROPIC_BASE_URL=http://localhost:4000\nexport ANTHROPIC_AUTH_TOKEN=sk-your-litellm-key\nexport ANTHROPIC_MODEL=claude-sonnet-4-5-20250929\nclaude-desktop-hardened\n```\n\n**LM Studio** — local GUI LLM server:\n\n```bash\n# In LM Studio: load a model, start the server (default port 1234).\nexport ANTHROPIC_BASE_URL=http://localhost:1234/v1\nexport ANTHROPIC_AUTH_TOKEN=lm-studio\nexport ANTHROPIC_MODEL=\u003cyour-loaded-model-id\u003e\nclaude-desktop-hardened\n```\n\n**Ollama** — via LiteLLM passthrough (Ollama's native API isn't Anthropic-compatible, so proxy through LiteLLM):\n\n```bash\n# ~/litellm.config.yaml\n# model_list:\n#   - model_name: llama3.1:70b\n#     litellm_params:\n#       model: ollama/llama3.1:70b\n#       api_base: http://localhost:11434\n\nlitellm --config ~/litellm.config.yaml --port 4000 \u0026\nexport ANTHROPIC_BASE_URL=http://localhost:4000\nexport ANTHROPIC_MODEL=llama3.1:70b\nclaude-desktop-hardened\n```\n\n**OpenRouter** — hosted multi-provider routing:\n\n```bash\nexport ANTHROPIC_BASE_URL=https://openrouter.ai/api/v1\nexport ANTHROPIC_AUTH_TOKEN=sk-or-v1-...\nexport ANTHROPIC_MODEL=anthropic/claude-sonnet-4.5\nclaude-desktop-hardened\n```\n\n**vLLM** — self-hosted inference server:\n\n```bash\n# Start vLLM with Anthropic-compatible endpoints enabled:\nvllm serve \u003cyour-model\u003e --host 0.0.0.0 --port 8000\n\nexport ANTHROPIC_BASE_URL=http://localhost:8000\nexport ANTHROPIC_MODEL=\u003cyour-model\u003e\nclaude-desktop-hardened\n```\n\n**Anthropic direct (BYOK)** — use your own Anthropic key instead of OAuth:\n\n```bash\nexport ANTHROPIC_API_KEY=sk-ant-...\n# (no ANTHROPIC_BASE_URL needed — uses Anthropic's default)\nclaude-desktop-hardened\n```\n\n### Environment variable reference\n\n| Variable | Purpose | Example |\n|---|---|---|\n| `ANTHROPIC_BASE_URL` | Override backend URL | `http://localhost:4000` |\n| `ANTHROPIC_AUTH_TOKEN` | Bearer token for the backend | `sk-litellm-…` |\n| `ANTHROPIC_API_KEY` | Anthropic-style API key | `sk-ant-…` |\n| `ANTHROPIC_MODEL` | Default model | `claude-sonnet-4-5-20250929` |\n| `ANTHROPIC_SMALL_FAST_MODEL` | Quick/cheap model for summaries | `claude-haiku-4-5` |\n| `ANTHROPIC_DEFAULT_OPUS_MODEL` | Per-tier override (when UI picks Opus) | `gpt-4o` |\n| `ANTHROPIC_DEFAULT_SONNET_MODEL` | Per-tier override (when UI picks Sonnet) | `gpt-4o-mini` |\n| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Per-tier override (when UI picks Haiku) | `llama3.1:8b` |\n| `ANTHROPIC_CUSTOM_HEADERS` | Extra HTTP headers sent with every request | `X-Project: foo` |\n| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | Per-request output token cap | `8192` |\n\n### Verify\n\n```bash\nclaude-desktop-hardened --doctor\n```\n\nIf any `ANTHROPIC_*` env var is set, `--doctor` shows a **Custom Model Backend** section with the resolved values (secrets redacted) and probes `ANTHROPIC_BASE_URL` for reachability. A `401` or `404` from the probe is still `[OK]` — it proves the socket is up and TLS worked, which is what the check is actually testing.\n\n### Troubleshooting\n\n- **\"My CLI flag is ignored\"** — make sure `--model` and `--base-url` come *before* any `--` separator or free args. Order: `claude-desktop-hardened --model X --base-url Y`.\n- **\"Cowork session hangs on connect\"** — run `--doctor`, confirm reachability. Check firewalls and whether your backend is listening on `0.0.0.0` (not just `127.0.0.1` if you're using a container).\n- **\"TLS error\"** — local proxies with self-signed certs will fail. Use plain `http://` for loopback, or install the self-signed cert into your system CA store.\n- **\"Model name rejected\"** — provider model-name formats differ: OpenRouter uses `vendor/model`, Ollama uses `name:tag`, LM Studio wants the exact loaded model's ID string from its server UI.\n- **\"401 / 403 from backend\"** — if you were logged in via OAuth before, `CLAUDE_CODE_OAUTH_TOKEN` may still be set and the CLI will try to use it against your custom backend. Run `unset CLAUDE_CODE_OAUTH_TOKEN` before launching, or include it in your shell rc file's custom-backend section. `--doctor` will warn when this conflict is detected.\n- **\"UI shows Sonnet but my backend model is being used\"** — that's expected. The `--model` flag aliases all three tiers (Opus/Sonnet/Haiku) to your model, so the UI picker is effectively cosmetic. Check the backend's access log to confirm traffic is going where you want.\n\n### Bedrock / Vertex / extra env vars (advanced)\n\nAWS Bedrock and Google Vertex aren't default-enabled because their required env vars (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `GOOGLE_APPLICATION_CREDENTIALS`, `CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_VERTEX`) are cloud credentials with broader scope than just the model backend — we don't want them silently leaking into the sandbox unless you've explicitly said you want them there.\n\nTo opt in, edit the allowlist in `/usr/lib64/claude-desktop-hardened/stubs/claude-swift-stub/index.js` (or `/usr/lib/claude-desktop-hardened/...` on Debian). Add the vars you need to the `ENV_ALLOWLIST` Set. **Note:** the edit is reverted on package upgrade; for reproducibility, keep a post-install hook that re-applies your override, or open an issue if you'd like first-class support.\n\n---\n\n## Security model\n\nThis project treats Claude's agentic capabilities as a security boundary. Every feature that touches the host system is sandboxed, logged, or gated behind user confirmation.\n\n### Cowork sandboxing\n\nWhen Cowork spawns a Claude Code session, it runs inside a [bubblewrap](https://github.com/containers/bubblewrap) sandbox with a default-deny filesystem policy:\n\n- **Minimal rootfs** — only `/usr`, `/lib`, `/lib64`, and select `/etc` files are mounted read-only. The agent cannot see your home directory, browser data, password managers, or other users' files.\n- **Writable mounts** limited to the working directory, session data, and `~/.config/Claude`\n- **Resource limits** via `systemd-run` — 4GB memory, 200% CPU (2 cores), 512 max tasks to prevent runaway processes and fork bombs\n- **`--die-with-parent`** ensures cleanup if the parent process exits\n- **Environment allowlisting** — only safe variables pass through (HOME, PATH, DISPLAY, XDG_*, plus standard `ANTHROPIC_*` SDK vars for [custom backends](#using-a-custom-model-backend)). Cloud credentials (`AWS_*`, `GOOGLE_*`) are deliberately excluded.\n- **No unsandboxed fallback** — if bubblewrap is not found, sessions refuse to start\n\nBubblewrap is a **hard dependency** — if you're using this package, you get sandboxing. That's the point.\n\n### Network access\n\nSandboxed sessions have **full network access**. Claude Code needs HTTPS to `api.anthropic.com` to function, and isolating the network would break core functionality. This means the agent can theoretically reach internal services on your network. If you run services on localhost or your LAN that accept unauthenticated requests, be aware of this. We may add network policy support (via nftables or a proxy) in a future release.\n\n### Computer Use permissions\n\nEvery Computer Use permission request shows a native dialog — nothing is auto-granted:\n\n- **Screen Recording** — screenshot capture via `grim` (Wayland) or `scrot` (X11)\n- **Input Automation** — click/type/scroll via `ydotool` (Wayland) or `xdotool` (X11)\n- **Window Listing** — via `hyprctl`/`swaymsg` (Wayland) or `wmctrl` (X11)\n\nGrants are session-only — they reset when you close Claude Desktop. All Computer Use actions are logged to the transcript store with credential redaction applied.\n\n### Credential redaction\n\nAll session transcripts are scrubbed before hitting disk:\n\n- Bearer tokens, API keys (AWS, GitHub, Anthropic, OpenAI, Slack, Stripe, npm, PyPI)\n- JWTs, OAuth tokens, private keys, database connection strings\n- Google Cloud service account key IDs\n- Generic secrets in environment-style assignments\n- Sensitive environment variables filtered from subprocess environments\n\n### Path safety\n\nFile operations are checked against a blocklist that includes sensitive directories (`.ssh`, `.gnupg`, `.aws`, `.kube`, `.docker`) and persistence vectors (`.bashrc`, `.profile`, `.config/autostart`, `cron`). Path traversal (`..`) is blocked at the raw input level before normalization.\n\n### Electron sandbox\n\nThe `chrome-sandbox` binary is set to `4755 root:root` (setuid) during post-install. This preserves Electron's multi-process sandbox — the renderer runs in a restricted namespace even if the main process is compromised.\n\n---\n\n## Supply chain integrity\n\n### Version pinning\n\nTwo files control all external dependency versions:\n\n- **`CLAUDE_VERSION`** — pins the exact Claude Desktop release (version + SHA256 of the nupkg)\n- **`TOOL_VERSIONS`** — pins Electron, asar, cdxgen, Claude CLI, vet, and container image digests\n\nAll GitHub Actions are pinned to full commit SHAs. Container images are pinned to SHA256 digests. npm packages are installed with `--ignore-scripts`.\n\n### CI pipeline\n\nEvery push and PR runs:\n\n- **Unit tests** — credential classifier (19 tests), path safety (9 tests), patch system (19 tests), doctor integration (7 tests)\n- **Package smoke tests** — verifies each built package contains expected files, correct permissions, valid desktop entry, and reasonable size\n- **Source integrity** — trojan source / Unicode attack scanning on all JS, shell, and Python files\n- **Dependency scanning** — OWASP depscan for vulnerabilities, vet for malware\n- **Post-patch validation** — `node --check` verifies patched JS is syntactically valid\n- **SBOM** — CycloneDX bill of materials attached to every release\n\n### Automated updates\n\nA CI workflow checks for new Claude Desktop releases daily. When a new version is found, it:\n\n1. Downloads the new nupkg and computes its SHA256\n2. Test-builds an RPM in a Fedora container to verify patches apply cleanly\n3. Validates the patched JS with `node --check`\n4. If everything passes, pushes the version bump to `main` (which triggers the release pipeline)\n5. If the build fails, opens a GitHub issue with diagnostics and the build log\n\n### Release pipeline\n\nWhen `CLAUDE_VERSION` changes on `main`, the release workflow:\n\n1. Builds RPM, DEB, and Arch packages in pinned containers\n2. Generates a CycloneDX SBOM\n3. Creates a GitHub Release with SHA256SUMS (GPG-signed if key is configured)\n4. Publishes to Fedora COPR, GitHub Pages APT repo, and AUR automatically\n\n---\n\n## Configuration reference\n\nThese optional config files let you customize Cowork behavior. All paths follow the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/) — if `XDG_CONFIG_HOME` is set, it replaces `~/.config`.\n\n### Resource limits (`~/.config/Claude/cowork-limits.json`)\n\nOverride the default systemd-run resource limits for sandboxed Cowork sessions:\n\n```json\n{\n  \"memoryMax\": \"8G\",\n  \"cpuQuota\": \"400%\",\n  \"tasksMax\": \"1024\"\n}\n```\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `memoryMax` | `4G` | Maximum memory (systemd format: `4G`, `512M`, etc.) |\n| `cpuQuota` | `200%` | CPU quota — `200%` means 2 cores |\n| `tasksMax` | `512` | Maximum number of processes/threads |\n\nValues must match the pattern `^\\d+[GMKT%]?$`. Invalid entries are silently ignored and defaults are used.\n\n### Custom credential patterns (`~/.config/Claude/credential-patterns.json`)\n\nAdd your own regex patterns for credential redaction on top of the built-in set:\n\n```json\n{\n  \"patterns\": [\n    \"my-internal-token-[A-Za-z0-9]{32}\",\n    \"CORP_SECRET_[A-Z0-9]+\"\n  ]\n}\n```\n\nEach pattern is compiled as a case-sensitive regex. Patterns longer than 500 characters are skipped. Malformed regexes are logged and ignored.\n\n### Debug logging (`COWORK_DEBUG` environment variable)\n\nEnable verbose debug logging for all Cowork subsystems (session orchestrator, Computer Use, credential classifier):\n\n```bash\nCOWORK_DEBUG=1 claude-desktop-hardened\n```\n\nDebug output is prefixed with `[cowork-debug]` and written to stderr. Useful for diagnosing sandbox startup failures, tool resolution issues, or Computer Use problems.\n\n### Transcript and session logs\n\nCowork session transcripts (with credential redaction applied) are stored at:\n\n```\n~/.local/state/claude-cowork/logs/\n```\n\nThese contain a log of all Computer Use actions, session lifecycle events, and redacted command output. Transcripts are retained until manually deleted.\n\n---\n\n## How it works\n\nClaude Desktop ships as a Windows `.exe` installer containing an Electron app. The build script:\n\n1. **Downloads** the pinned nupkg (version + SHA256 verified from `CLAUDE_VERSION`)\n2. **Extracts icons** from the Windows exe (16px through 256px)\n3. **Replaces native modules** — swaps Windows/macOS native addons with Linux stubs for keyboard constants, platform detection, and Cowork session management\n4. **Installs Cowork stubs** — a process orchestrator for spawning sandboxed Claude Code CLI sessions with credential redaction, file watching, session persistence, and IPC handling\n5. **Installs Computer Use modules** — display-server-aware screenshot, window listing, and input automation with a permission dialog layer\n6. **Patches platform gating** — modular patches in `patches/` surgically modify 8 locations in the minified JS to accept Linux as a supported platform\n7. **Patches window decorations** — switches from macOS `hiddenInset` to Electron CSD with transparent title bar overlay\n8. **Injects startup code** — sets the window icon, fixes the system tray, sets Wayland `app_id`, and registers permission-gated Computer Use handlers\n9. **Inverts tray icons** for dark Linux system trays\n10. **Bundles Claude Code CLI** from npm (pinned version, `--ignore-scripts`)\n11. **Packages** as RPM, DEB, or Arch with post-install hooks for icon caches, desktop database updates, and chrome-sandbox setuid\n\n### Patch architecture\n\nPlatform patches are modular — each lives in its own file under `patches/`:\n\n| Patch | Purpose |\n|-------|---------|\n| `patch_platform_gating.py` | Accept Linux in platform check functions |\n| `patch_vm_manifest.py` | Add Linux entries to VM image manifest |\n| `patch_platform_constants.py` | Include Linux in `isSupportedPlatform` |\n| `patch_enterprise_config.py` | Ensure VM features aren't forced off |\n| `patch_api_headers.py` | Spoof platform headers for feature checks |\n| `patch_binary_manager.py` | Add Linux to `getHostPlatform()` |\n| `patch_binary_resolution.py` | Find system-installed Claude CLI |\n| `inject_cowork_init.py` | Wire up Cowork lifecycle hooks |\n\nThis makes version bumps easier — when upstream renames minified symbols, you update one file instead of a monolithic script.\n\n### Package metadata\n\nAll packaging specs (RPM, DEB, Arch, COPR repackage) are generated from a single source of truth:\n\n```bash\npython3 packaging/generate-specs.py\n```\n\nThis reads `packaging/metadata.json` and outputs all four spec files, ensuring dependencies, file lists, and descriptions never drift.\n\n---\n\n## License\n\nBuild scripts and stubs are dual-licensed under [MIT](LICENSE-MIT) and [Apache 2.0](LICENSE-APACHE).\n\nThe Claude Desktop application itself is covered by [Anthropic's Consumer Terms](https://www.anthropic.com/legal/consumer-terms).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechtoboggan%2Fclaude-desktop-hardened-linux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechtoboggan%2Fclaude-desktop-hardened-linux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechtoboggan%2Fclaude-desktop-hardened-linux/lists"}