Files
wehub-resource-sync 3e076d4dd9
Deploy Worker / deploy (push) Failing after 1s
Shellcheck / Check shell scripts (push) Failing after 1s
CI / Build Packages (amd64 - appimage) (push) Has been skipped
CI / Build Packages (amd64 - deb) (push) Has been skipped
CI / Build Packages (amd64 - rpm) (push) Has been skipped
CI / Build Packages (arm64 - appimage) (push) Has been skipped
CI / Build Packages (arm64 - deb) (push) Has been skipped
CI / Test Flags Parsing (push) Failing after 1s
BATS Tests / BATS unit tests (push) Failing after 1s
CI / Build Packages (arm64 - rpm) (push) Has been skipped
CI / Test Build Artifacts (amd64) (push) Has been skipped
CI / Test Build Artifacts (arm64) (push) Has been skipped
CI / Update APT Repository (push) Has been cancelled
CI / Mirror Official .deb to Release (push) Has been cancelled
CI / Update DNF Repository (push) Has been cancelled
CI / Update AUR Package (push) Has been cancelled
CI / Create Release (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:29:21 +08:00

7.5 KiB

< Back to README

Building from Source

build.sh downloads Anthropic's official Claude Desktop Linux .deb, optionally patches its app.asar, and repackages the official application tree as a .deb, .rpm, or AppImage.

git clone https://github.com/aaddrick/claude-desktop-debian.git
cd claude-desktop-debian

# Build with the auto-detected format for your distro
./build.sh

# Or pick a format explicitly
./build.sh --build deb
./build.sh --build rpm
./build.sh --build appimage

The default format is detected from the host distribution:

Distribution family Default format
Debian, Ubuntu, Mint (/etc/debian_version) deb
Fedora, RHEL, CentOS rpm
NixOS nix (currently a stub — see Nix below)
Anything else (including Arch) appimage

Prerequisites

The official .deb is unpacked with ar + tar instead of dpkg-deb, so rpm-family and Arch hosts can build too. Per format:

Needed for Commands
Every format wget, ar (binutils), tar, xz, zstd
--build deb dpkg-deb (dpkg-dev)
--build rpm rpmbuild (rpm-build)
--build appimage appimagetool — downloaded into build/ automatically when not on PATH
The asar patch stage Node.js v20+ — a local v20.18.1 is downloaded into build/ when the system Node is missing or too old; @electron/asar is npm-installed into build/

On Debian- and RPM-family hosts, build.sh offers to install the missing system packages via apt/dnf (check_dependencies in scripts/setup/dependencies.sh). On other distros it lists what to install manually.

Build flags

From parse_arguments in scripts/setup/detect-host.sh:

./build.sh [--build deb|rpm|appimage|nix] [--clean yes|no]
           [--deb /path/to/claude-desktop.deb] [--arch amd64|arm64]
           [--release-tag TAG] [--source-dir /path] [--test-flags]
Flag Default Effect
-b, --build auto-detected Output format: deb, rpm, appimage, or nix.
-c, --clean yes Remove intermediate files in build/ after packaging. --clean no keeps them for inspection.
-d, --deb download Use a locally downloaded official .deb instead of fetching the pinned one. The SHA256 pin check is skipped for local files.
-a, --arch uname -m Override the target architecture (amd64 or arm64) for cross-building — repackaging the official .deb is arch-independent, so an amd64 host can produce an arm64 package.
-r, --release-tag unset Release tag (e.g. v3.0.0+claude1.18286.0); the wrapper version is extracted and appended to the package version (1.18286.0-3.0.0). Used by CI.
-s, --source-dir repo root Path to the repo root for scripts and assets, for out-of-tree invocations.
--test-flags off Parse flags, print the results, and exit without building.

How the official .deb is resolved

The build never scrapes a download page — it pulls a pinned artifact from Anthropic's official APT pool:

  • scripts/setup/official-deb.sh pins OFFICIAL_DEB_VERSION (currently 1.18286.0) plus a per-architecture pool path and SHA256 against https://downloads.claude.ai/claude-desktop/apt/stable.
  • The download is verified against the pinned SHA256 before extraction.
  • ar + tar extract data.tar.* and control.tar.* (zst/xz/gz all handled); the app tree must land at usr/lib/claude-desktop or the build aborts with an upstream-layout error.
  • The package version is read from the extracted control file; a mismatch against the pin is a warning, not a failure.
  • Depends: and Recommends: are read from the official control file and re-emitted verbatim into our packages — the contract differs per arch (arm64 recommends a different qemu stack than amd64), and re-emitting tracks upstream automatically.

The pins are bumped automatically: the check-claude-version workflow polls the official APT Packages index, rewrites the OFFICIAL_DEB_* pins in scripts/setup/official-deb.sh, updates the CLAUDE_DESKTOP_VERSION repo variable, and pushes a v{REPO_VERSION}+claude{VERSION} tag that triggers the release build.

To build a version before the automation catches it, download the .deb from the official pool yourself and pass --deb /path/to/claude-desktop_VERSION_ARCH.deb.

The patch stage (patch-zero contract)

active_patches in scripts/patches/app-asar.sh lists every asar patch still active. The default verdict for any patch is delete: when the array is empty, the official app.asar ships byte-identical — it is never extracted or repacked. Two patches currently survive:

  • patch_quick_window (scripts/patches/quick-window.sh) — KDE-gated blur/focus workaround so the main window reappears after a Quick Entry submit (Electron stale-focus bug on Plasma).
  • patch_org_plugins_path (scripts/patches/org-plugins.sh) — adds a case "linux" to the upstream org-plugins platform switch, which only handles darwin/win32; without it MDM org plugins are silently dead on Linux (filed upstream).

Two build-time tripwires grep the pristine app.asar on every build and fail loudly if upstream flips behavior we deleted patches for: apt_channel_pending (AU-1 — the marker that keeps the official autoupdater dormant while the APT channel is pending) and menuBarEnabled:!0 (MB-1 — the settings default that keeps the menu bar on).

When patches do run, the repack preserves upstream's app.asar.unpacked set exactly and aborts if the sets diverge.

Installing the built package

For .deb packages (Debian/Ubuntu)

sudo apt install ./claude-desktop-unofficial_VERSION_ARCHITECTURE.deb
# Or: sudo dpkg -i ./claude-desktop-unofficial_VERSION_ARCHITECTURE.deb

# If you encounter dependency issues:
sudo apt --fix-broken install

For .rpm packages (Fedora/RHEL)

sudo dnf install ./claude-desktop-unofficial-VERSION-1.ARCH.rpm
# Or: sudo rpm -i ./claude-desktop-unofficial-VERSION-1.ARCH.rpm

For AppImages

# Make executable
chmod +x ./claude-desktop-unofficial-*.AppImage

# Run directly
./claude-desktop-unofficial-*.AppImage

# Or integrate with your system using Gear Lever

Note: AppImage login requires proper desktop integration. Use Gear Lever or manually install the generated .desktop file to ~/.local/share/applications/.

Automatic updates: AppImages downloaded from GitHub releases include embedded update information and work with Gear Lever for automatic updates. Locally-built AppImages can be configured manually in Gear Lever.

Nix

The derivation (nix/claude-desktop.nix) repackages the official .deb: fetchurl from the official APT pool, autoPatchelfHook over the bare co-located tree, no nixpkgs Electron. The FHS output (nix/fhs.nix, the flake default) additionally provides MCP runtime dependencies and OVMF firmware at Cowork's hardcoded probe paths.

nix build .#claude-desktop
nix build .#claude-desktop-fhs

Build-verified on x86_64; runtime on real NixOS and the aarch64 leg are open validation items (owner @typedrat). Design contract, SRI auto-bump anchors, and a no-NixOS testing recipe: docs/learnings/nix.md.

Two facts about the --build nix path that hold on this branch: build.sh --build nix requires --deb (it never downloads inside the sandbox), and it stops after the patch stage — the Nix derivation is expected to handle installation itself.