Skip to content

fix: claude and ghostty from signed vendor repos - #113

Merged
catinspace-au merged 3 commits into
mainfrom
fix/vendor-repos-claude-ghostty
Oct 6, 2026
Merged

catinspace-au merged 3 commits into
mainfrom
fix/vendor-repos-claude-ghostty

Conversation

@catinspace-au

Copy link
Copy Markdown
Contributor

Claude Code on Linux and ghostty on Ubuntu now come from signed package repos, so they update with the host like everything else.

  • Claude Code installs claude-code from Anthropic's apt/dnf repo. The key must be exactly 31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE (https://code.claude.com/docs/en/setup) before anything trusts it.
    • The channel is soe_claude_channel (default stable). Stable can lag a just-launched model by about a week, so set it to latest to follow that.
    • The old per-user install (~/.local/bin/claude and ~/.local/share/claude) goes once /usr/bin/claude runs. So does its top-level "installMethod": "native" in ~/.claude.json, so claude doctor stops offering to reinstall it.
    • hyperi-update drops claude update, because apt and dnf cover it now. macOS stays on the brew cask.
  • ghostty on Ubuntu comes from ppa:mkasberg/ghostty-ubuntu (Launchpad signing key 0721FDF5FECB88DC6920361657C8EF455CEAE491). That is the same maintainer as the .deb we used to fetch, whose version sorts above the PPA build, so that one old .deb is swapped out. A ghostty you built yourself is left alone. 24.04 is frozen at 1.3.1 by the PPA, and 26.04 tracks releases.
  • If a user already added one of these repos themselves (add-apt-repository, or Anthropic's documented .list), the role removes the duplicate source and names it in the end-of-run report. Two sources for one repo with different keys stops apt entirely ("Conflicting values set for option Signed-By"). The same applies to the git-core PPA, which had this exposure already.
    • One map, hyperi_exclusive_apt_sources, lists the repos this applies to.
    • An early pass in the developer role clears a host that is already broken, before its first apt task.
    • A conflict that still occurs removes our own source instead, so a run never leaves apt unreadable.
  • Fedora COPR repos (ghostty, openvpn3) are enabled with dnf copr enable. The Ansible copr module needs dnf4's python3-dnf, which Fedora 44 Server, cloud and minimal images do not have.

Tested in containers:

  • fresh and migrated hosts on ubuntu:24.04, ubuntu:26.04, fedora:43 and fedora:44, with the second run changed=0;
  • hosts with the PPAs added by add-apt-repository and Anthropic's latest .list;
  • a host already failing apt with exit 100 recovers;
  • a wrong fingerprint fails closed with apt still clean.

Not run: macOS, arm64, check mode, and a real ghostty desktop session.

Done when claude and ghostty come from their vendors' signed repos and no combination of user-added sources can leave apt broken.

Claude Code on Linux now comes from Anthropic's signed apt/dnf repository on the stable channel, with the release key pinned by fingerprint (31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE, from code.claude.com/docs/en/setup). The package manager keeps it current, so hyperi-update drops its `claude update` step. A host that still has the old native install loses ~/.local/bin/claude and ~/.local/share/claude once the package copy runs, because the native launcher sits ahead of /usr/bin on the PATH and would shadow the package. The native install method is also cleared from ~/.claude.json, or `claude doctor` reports the launcher as broken and offers to reinstall it. macOS stays on the brew cask.

Ghostty on Ubuntu now comes from ppa:mkasberg/ghostty-ubuntu, the same maintainer as the .deb releases it replaces, with Launchpad's signing key pinned (0721FDF5FECB88DC6920361657C8EF455CEAE491). The old .deb is versioned 1.3.1-0~ppa2, which sorts above the PPA's 1.3.1~ppa2-noble1, so a version no repo offers is swapped for the PPA's newest build or apt would keep the unmanaged copy forever. Fedora and macOS are unchanged.

Both follow the vendor key pattern: stage the key, require exactly one primary key matching the pin, then trust it. On a mismatch the repo is removed so apt and dnf keep working, the already-trusted key stays, and the failure lands in deploy_warnings.
apt refuses to read any source once two entries for one repository carry different Signed-By values. A host where someone ran `add-apt-repository ppa:mkasberg/ghostty-ubuntu` or `ppa:git-core/ppa`, or followed Anthropic's own apt instructions, ended up with exactly that next to the .sources we write, and the play hard-failed at the next apt task. system_cleanup/tasks/apt_source_exclusive.yml now removes any other source for the same repository before ours is written, and says so in deploy_warnings. developer's init runs the same sweep before the first apt task in the play, for hosts already broken that way. The Claude and Ghostty rescues delete our own .sources if apt still reports the conflict, so a run never leaves apt unreadable. The git-core PPA gets the same treatment, and python3-debian joins its prerequisites because deb822_repository needs it on a minimal Ubuntu.

Fedora enables the ghostty and openvpn3 COPRs with dnf5's own `dnf copr enable`. community.general.copr needs dnf4's python3-dnf, which Fedora Server, cloud and minimal images do not ship.

The Claude channel is now soe_claude_channel (default stable), and a host on Anthropic's latest-channel source moves to it. The native-install cleanup also takes the updater's staging and locks directories, and the installMethod fix runs whenever the package copy works with no native launcher left, matching only the top-level key. The Ghostty .deb swap now touches only the GitHub-release version pattern, so a user's own build is left alone.
The repository regex and .sources path for git, ghostty and claude now live once, in hyperi_exclusive_apt_sources in playbooks/group_vars/all.yml. apt_source_exclusive.yml takes a key into that map, and git.yml, ghostty.yml, claude.yml and developer's init pass read from it, so adding a repository is one entry.
@catinspace-au
catinspace-au merged commit 9c44e7b into main Oct 6, 2026
18 checks passed
@catinspace-au
catinspace-au deleted the fix/vendor-repos-claude-ghostty branch October 6, 2026 23:28
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant