Skip to content

feat(cachyos): CachyOS desktop setup + Claude install improvements#18

Merged
ulises-c merged 18 commits into
mainfrom
feat/cachyos-setup
May 29, 2026
Merged

feat(cachyos): CachyOS desktop setup + Claude install improvements#18
ulises-c merged 18 commits into
mainfrom
feat/cachyos-setup

Conversation

@ulises-c

Copy link
Copy Markdown
Owner

Summary

  • agentic-ai/Claude/install.sh: auto-installs railguard via cargo on fresh setup
  • agentic-ai/Claude/README.md: documents railguard hook, post-test-runner.sh, and updated activation steps
  • agentic-ai/Claude/TODO.md: safeguard validation checklist and sandbox revisit items tied to upstream issue #44180

What's coming

  • linux-desktop/ updates for CachyOS/Arch support

Test plan

  • Run install.sh on a fresh CachyOS machine and verify railguard installs and hooks wire up
  • Validate hooks per agentic-ai/Claude/TODO.md checklist
  • Verify GPG commit signing works (SSH signing config removed from local .git/config)

🤖 Generated with Claude Code

ulises-c and others added 12 commits May 22, 2026 18:26
- install.sh: install railguard via cargo and run railguard install
- README: document railguard hook, post-test-runner hook, and updated activation steps
- TODO.md: safeguard validation checklist and sandbox revisit items

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
macOS/setup.sh: ${=names} is zsh word-split syntax — bash treats it as
bad substitution. Switch shebang to bash, set -euo pipefail, and drop
the = operator (bash word-splits unquoted vars by default). Also fix
[ -s ] → [[ -s ]] in the nvm sourcing block.

agentic-ai/Claude/install.sh: railguard install (the config step) only
ran on a fresh binary install, so re-running the script never repaired
broken railguard state. Move it unconditionally after the binary check.
Also surface a clear error if cargo install railguard fails instead of
silently exiting via set -e.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: resolve CARGO_BIN via command -v instead of hardcoding
  $HOME/.cargo/bin/cargo (fixes failure when cargo is installed via Homebrew)
- install.sh: save/restore settings.json around railguard install so
  railguard's machine-specific absolute paths don't overwrite the portable
  ~ paths in the repo version
- settings.json: restore custom hooks (validate-bash, validate-write,
  post-edit-shellcheck, post-test-runner) dropped by railguard install;
  add CLAUDE_CODE_SHELL env var; use ~ paths throughout
- CLAUDE.md: commit railguard's instructions block (idempotent on re-install)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Checks symlinks, hook wiring, required binaries (jq, shellcheck,
pytest), settings.json integrity, railguard binary, and runs smoke
tests against each hook with known-good and known-bad payloads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
setup.sh was hardcoded to Ubuntu (apt/snap). Make it distro-aware:

- Auto-detect ubuntu/arch from /etc/os-release (+ --distro override)
- Bootstrap yay via pacman, then drive repo+AUR installs through yay
- Enable tailscaled on Arch; distro-aware pyenv build deps
- Fix shell switch to read the real login shell via getent (handles
  fish -> zsh when $SHELL is an inherited interactive zsh over SSH)

JSON: pyenv/nvm use the curl installers on Arch too (unified ~/.pyenv
and ~/.nvm across all OSes); fix hf arch_name to python-huggingface-hub;
add pre-commit + shellcheck (the repo's own dev gate).

zshrc.example: add guarded pyenv/nvm init (was missing on all platforms).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…batch

The pyenv step ran `eval "$(pyenv init -)"`, which detects the shell from
$SHELL and emits fish/zsh syntax. Under a fish login shell that syntax fails
in this bash script's eval, and set -e aborted the whole run right after
pyenv installed — so nvm, the medium package batch, the shell switch, and the
zshrc/ghostty config never ran. Force `pyenv init - bash`.

Also make yay_install resilient: if the batch install errors (e.g. one AUR
build breaks), retry packages individually so a single failure can't abort
the run and skip the shell/config steps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CachyOS ships zlib-ng-compat (which provides zlib), so `pacman -S zlib`
wants to replace it -> unresolvable conflict -> --noconfirm declines ->
set -e aborts the run before pyenv installs Python. Filter the Arch build
deps through `pacman -T` and only install the unsatisfied ones (e.g. tk).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Distro-aware (Ubuntu/Arch) verification script mirroring setup.sh's
selection logic, so the same flags check exactly what setup.sh installs.
Also checks runtime state: login shell is zsh, pyenv Python, nvm Node,
antidote, zshrc/plugins/ghostty configs, and the tailscaled service.

Verified on CachyOS: 52/52 with --work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a RELATED_SCRIPTS array + print_related_scripts() so the done message
points at the repo's other installers (Claude config, SSH/GPG key scripts,
verify.sh). Only scripts present on disk are shown; adding a new one is a
one-line array entry. Replaces the previously hardcoded SSH/GPG lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the RELATED_SCRIPTS discoverability block from linux-desktop/setup.sh
into macOS/setup.sh so a run ends by pointing at Claude config, SSH/GPG key
generation, and verify.sh — only scripts present on disk are shown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capture the agreed architecture for collapsing the three per-folder setup
stacks into one root setup.sh + single packages.json (managers keyed by
platform, shared lib/core.sh, platforms/ modules). Detailed enough to pick up
in a separate PR: schema spec, old→new migration rules, phased breakdown with
dry-run parity gates, and open questions. Link the phases from TODO.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ulises-c

Copy link
Copy Markdown
Owner Author

Addresses #23 — npm minimum release age (supply-chain cooldown)

Adds a 7-day min-release-age gate so npm refuses to install package versions younger than 7 days, defending against the recent npm/PyPI token-theft attacks (e.g. the axios RAT, Mar 2026) where a poisoned patch of a trusted package executes within seconds of npm install. Such malicious releases are typically caught and yanked within hours to ~2 days, so a short cooldown blocks them while barely delaying legit updates. 7 days is also pnpm 11's new default; protection plateaus past that and longer windows mostly delay the security patch that lands after an attack is found.

Patch:

  • linux-desktop/setup.sh / macOS/setup.sh — new configure_npm_cooldown helper (NPM_MIN_RELEASE_AGE=7) writes a user-level ~/.npmrc during the nvm/Node step, before any npm install -g runs. Idempotent, portable (no BSD/GNU sed -i split), preserves existing .npmrc lines, and respects --dry-run.
  • linux-desktop/verify.sh / macOS/verify.sh — health check confirming min-release-age is set and showing the value.

Checks: bash -n / zsh -n per each script's interpreter; shellcheck --severity=warning clean on the Linux scripts.

Note: npm's min-release-age has no per-package exclusion yet (pnpm/yarn do) — fine here since we only do a handful of global installs.

ulises-c and others added 2 commits May 28, 2026 19:14
Write a user-level ~/.npmrc with min-release-age=7 during nvm/Node setup so
npm refuses to install package versions younger than 7 days, mitigating
token-theft attacks where a poisoned patch of a trusted package runs within
seconds of install. Applied before the repo's own `npm install -g` steps.

Helper is idempotent, portable, preserves existing .npmrc lines, and respects
--dry-run. verify.sh confirms the setting on both platforms.

Closes #23

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enable pnpm through corepack (ships with nvm's Node) during setup and apply a
7-day supply-chain cooldown via `pnpm config set minimumReleaseAge 10080`
(minutes; set explicitly so pnpm enforces it strictly — the built-in 1-day
default is non-strict). Migrate the sole npm package (codeburn) to `pnpm add -g`
and wire PNPM_HOME onto PATH (zshrc.example on Linux, add_to_zshrc on macOS) so
globally-installed CLIs resolve.

The npm ~/.npmrc min-release-age=7 cooldown stays as defense-in-depth, since
npm ships with Node and still runs for ad-hoc/project/tool installs. verify.sh
now checks pnpm presence and its minimumReleaseAge on both platforms.

Refs #23

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ulises-c

Copy link
Copy Markdown
Owner Author

Follow-up to #23 — adopt pnpm as the daily-driver package manager

Per discussion, switched from npm to pnpm for global installs while keeping the npm cooldown as defense-in-depth.

Why pnpm: pnpm 11 ships supply-chain cooldown as a default and adds per-package exclusions (minimumReleaseAgeExclude) that npm lacks. Note the only npm-managed package in the repo was codeburn, so the migration surface is small.

Patch (commit 27f2936):

  • Install: corepack enable && corepack prepare pnpm@latest --activate during the nvm/Node step (corepack ships with Node — no extra gated download).
  • Cooldown: pnpm config set minimumReleaseAge 10080 --location=user → 7 days in minutes. Set explicitly so pnpm enforces it strictly; the built-in 1-day default is non-strict.
  • Migration: codeburn flipped from npmpnpm in both *_packages.json; install functions use pnpm add -g.
  • PATH: PNPM_HOME wired in (zshrc.example on Linux, add_to_zshrc on macOS) so globally-installed CLIs resolve.
  • Defense-in-depth: the npm ~/.npmrc min-release-age=7 cooldown stays — npm ships with Node and still runs for ad-hoc/project/tool installs.
  • verify.sh: now checks pnpm presence + minimumReleaseAge on both platforms.

Checks: bash -n/zsh -n per interpreter, shellcheck --severity=warning, jq empty on both JSON files, and a Linux --dry-run confirming the corepack → cooldown → install sequence all pass.

Exclusions (minimumReleaseAgeExclude) are available but left empty — no package currently needs to bypass the cooldown.

ulises-c and others added 2 commits May 28, 2026 22:57
pnpm 11 defaults the global bin dir to $PNPM_HOME/bin when global-bin-dir is
unconfigured, so `pnpm add -g` failed with "global bin directory is not in
PATH". Point PATH at $PNPM_HOME/bin (not $PNPM_HOME) in both setup scripts,
both zshrc configs, and both verify scripts. Verified live: `pnpm add -g
codeburn` resolves to ~/.local/share/pnpm/bin/codeburn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ulises-c

Copy link
Copy Markdown
Owner Author

Fixups from a real install test

  • fix(pnpm) (9039dcc): the first pnpm pass failed with "global bin directory ~/.local/share/pnpm/bin is not in PATH". pnpm 11 defaults the global bin dir to $PNPM_HOME/**bin** when global-bin-dir is unset, but the scripts had put $PNPM_HOME (no /bin) on PATH. Fixed in both setup scripts, both zshrc configs, and both verify scripts. Verified live — pnpm add -g codeburn now resolves to ~/.local/share/pnpm/bin/codeburn, and the cooldown even held back a 0.9.11 release younger than 7 days. ✅
  • feat(packages) (3a1472e): added the rust toolchain — yay (rust) on Arch, apt (rustup) on Ubuntu.

ulises-c and others added 2 commits May 28, 2026 23:12
shellcheck errors with SC1071 on zsh scripts (e.g. macOS/*.sh), blocking edits
to files it can't lint anyway. Bail out early when the shebang is zsh, matching
the .pre-commit-config.yaml exclusion of macOS/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
linux-desktop installed tmux but never deployed a config, so it ran defaults
(mouse off) — no scroll-wheel support. Deploy a tmux.conf (with `set -g mouse
on`) the same way linux-server does, with backup-on-change, plus a verify check.

Remove the `prefix C-a` remap from the shared config (both server and desktop)
— keep the default C-b so existing muscle memory (C-b w, etc.) is preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ulises-c
ulises-c marked this pull request as ready for review May 29, 2026 06:15
@ulises-c
ulises-c merged commit 566f717 into main May 29, 2026
3 checks passed
@ulises-c
ulises-c deleted the feat/cachyos-setup branch May 29, 2026 06:15
@ulises-c ulises-c linked an issue Jun 10, 2026 that may be closed by this pull request
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.

Npm minimum age for packages

1 participant