Ssh remote fix - #35
Closed
tommyrharper wants to merge 130 commits into
Closed
Conversation
…strap hostLabel now lives as a single let-binding in flake.nix (mirroring the existing `user` pattern) instead of being hardcoded separately in flake.nix, rebuild.sh, and bootstrap.sh. Both shell scripts now read the value back out of flake.nix via sed, the same way bootstrap.sh already does for the username. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n bootstrap/rebuild scripts
refactor: single-source hostLabel definition across flake.nix, rebuild.sh, bootstrap.sh
Alternative to PR #3's --basic CLI flag approach, opened separately so both can be compared side by side before deciding which to keep. - configuration.nix: includePersonalCasks module arg splits casks into basicCasks (dev tooling) and personalCasks (this Mac's GUI apps). - flake.nix: a single darwinConfigurations.${hostLabel} output computes includePersonalCasks = hostLabel != "basic". hostLabel does double duty as both the host's display name (from the already-merged PR #1) and the profile switch: any value except the literal "basic" gets personal casks too. - bootstrap.sh/rebuild.sh are unchanged from main - they already just read hostLabel and switch to it, so no flag or extra variable is needed at all. This design went through two earlier iterations in conversation before landing here: first a --basic CLI flag with an interactive confirm prompt, then a separate activeProfile variable alongside a second, fixed-name "basic" darwinConfigurations output. That second approach was rejected by review because it reintroduced the exact collision the user had found by testing hostLabel = "basic" against the original single-host flake (dynamic attribute "basic" already defined) - keeping both a ${hostLabel} output and a literal "basic" output meant renaming hostLabel to "basic" still crashed evaluation. Collapsing to one dynamically-keyed output removes that collision by construction, and matches what the user actually wanted: hostLabel = "mac" or hostLabel = "basic" as the one thing you edit. Verified via nix build --dry-run and nix eval .config.homebrew.casks with hostLabel set to both "mac" (all 7 casks) and "basic" (wezterm/ claude-code only, confirmed by temporarily flipping the value in place and reverting), and bash -n on the untouched bootstrap.sh/rebuild.sh. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Per user feedback: hostLabel should stay purely cosmetic (the host's display name) and not also serve as the personal/basic profile switch. Replaces includePersonalCasks = hostLabel != "basic" with a separate, plain includePersonalCasks = true; boolean in flake.nix - toggle it directly instead of relying on hostLabel's value meaning something special. configuration.nix is unchanged (already just takes includePersonalCasks as a required arg). bootstrap.sh/rebuild.sh are unaffected either way, since they only ever read hostLabel for naming, never for the cask decision. Verified via nix build --dry-run and nix eval .config.homebrew.casks with includePersonalCasks set to both true (all 7 casks) and false (wezterm/claude-code only), and confirmed hostLabel = "basic" now has zero special meaning - with includePersonalCasks left at true it still installs all 7 casks, proving the two are fully decoupled. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Trims verbosity and iteration-history framing from the docs/comments added for the hostLabel/includePersonalCasks split, so they read as a deliberate design rather than a fix-up trail. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ild cadence to bootstrap.sh
feat: split homebrew casks into basic and personal profiles
Adds basicBrews (herdr, thefuck, skills) and personalBrews (empty for
now) to configuration.nix, following the same pattern as
basicCasks/personalCasks. Both brews and casks are gated by the single
existing includePersonalCasks toggle in flake.nix - no separate
includePersonalBrews, per explicit request.
Behavior is unchanged today since personalBrews is empty; the split
just makes it structurally ready for personal-only brew formulas.
Verified via nix build --dry-run and nix eval .config.homebrew.{brews,casks}
with includePersonalCasks set to both true (all 3 brews, all 7 casks)
and false (still all 3 brews since personalBrews is empty, 2 casks).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Now that the toggle gates both brews and casks, "Casks" in the name was misleading. usePersonalSetup is more accurate and leaves room for future personal-only additions beyond Homebrew. Also trims the doc callouts explaining that hostLabel and this toggle are separate concerns - that's implicit from the code, no need to spell it out. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
feat: split brews into basic/personal, rename usePersonalSetup toggle
…c brew homebrew.onActivation.cleanup is "zap", so anything installed with a bare `brew install`/`brew install --cask` outside this config gets removed (app data included) on the next rebuild. Declared everything currently on this machine that was missing from configuration.nix: - basicCasks: add codex (already used via the `co` alias in home.nix), alongside the existing claude-code. - personalCasks: add altair-graphql-client, mongodb-compass, todoist, anki, ipfs, ipfs-desktop, iterm2, vlc, zoom. Keep just "todoist" (drop the legacy "todoist-app" cask name it was renamed from). Drop the Spotify cask - no longer wanted. - personalBrews: add the smart-contract toolchain (echidna, solidity, solc-select, plz-cli, ocaml, opam, tenderly, memo), the Python/Postgres toolchain (postgresql@15, libpq, pyenv, python@3.11), and the remaining installed CLI leaves (asdf, bzip2, cmake, ekhtml, ffmpeg, gh, git-filter-repo, gnu-tar, lcov, libusb, tree, wget, yarn). Left out jq and neovim - already declared via nix in home.nix, so the brew copies were pure duplicates. Left out spotify-tui/spotifyd along with the Spotify cask. Verified with `nix flake check --no-build` that darwinConfigurations.mac still evaluates after the change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
feat: declare personal-Mac brews and casks to prevent zap cleanup
home/.config/herdr/plugins.json and .plugins.lock are written by herdr itself at runtime (its plugin_registry.rs persistence module), not user-authored config, so they belong alongside the other herdr runtime artifacts already ignored in this block.
chore: ignore herdr plugin-registry files
…vish-axi Each tool's own setup installer already applied this change live on the machine (via the home-manager symlink aliasing ~/.claude/settings.json to this repo file), but it never landed as a reviewed, committed change. Also adds skipDangerousModePermissionPrompt to match the live configuration.
Points future agents at the shared shape (gitignored real file + committed .example, Include/read by path) instead of reinventing it for the next per-machine secret.
Colima and other tools need to keep rewriting ~/.ssh/config freely, so whole-file management via programs.ssh fought that. Instead home.nix now symlinks two dotfiles-owned fragments (a per-platform public file with safe defaults, a gitignored private file for per-host secrets) and an activation script idempotently prepends Include lines for them, leaving the rest of ~/.ssh/config untouched.
Completes the fragment + auto-Include model: removes programs.ssh from home.nix in favor of mkOutOfStoreSymlink for the two fragments plus a home.activation script that idempotently prepends their Include lines to ~/.ssh/config (respecting $DRY_RUN_CMD, never touching existing content). Updates .gitignore, README, AGENTS.md, and the SSH tests for the new dotfiles.config.public/.private naming; re-pins the Ubuntu drvPath test since removing programs.ssh changes the evaluated darwin derivation.
…roach feat(home): manage SSH config as included fragments
Captain reported herdr missing after bootstrap.sh + rebuild.sh on Ubuntu. Reproduced end-to-end in a clean Ubuntu 22.04 container: home-manager switch completed successfully but herdr was never installed anywhere. Root cause: tool-selection.nix's useNative correctly classified herdr (and claude-code/codex/skills/pi-coding-agent) as needing a non-Nix installer, but tool-selection.nix's nativeTools output was never consumed by home.nix - no installer ever ran for any of them, matching what the docs already said but confirming it end-to-end rather than trusting the docs alone. Adds a nativeInstallUrl field to tools.nix (set for herdr only, pointing at its own verified non-interactive install.sh from herdr.dev/docs/install), a nativeInstallTools helper in tool-selection.nix filtering useNative's output down to entries with that field, and a home.activation script in home.nix that runs it on rebuild (skipping if already installed) with ~/.local/bin added to home.sessionPath so the binary is reachable afterward. claude-code/codex/pi-coding-agent are left without the field since their installers are interactive or rewrite shell rc files themselves, fighting home-manager's declarative config - forcing them through the same unattended mechanism isn't safe, so they still need manual installation per their own docs. Verified post-fix in the same container: herdr installs during home-manager switch and is reachable on PATH in both bash and zsh login shells. Added regression coverage in tests/ubuntu-support.test.sh (test_linux_herdr_native_install_wired, test_darwin_native_install_absent) that would have caught this - it checks the activation script actually mentions herdr and its install URL, not just that useNative classifies it correctly, and that macOS's darwinConfigurations.mac evaluation is unaffected. Also re-pinned EXPECTED_DARWIN_DRVPATH, which had already drifted stale on main before this branch (confirmed via git stash that the old pin fails identically without this branch's diff).
…tallers on Ubuntu Finishes the native-install mechanism PR #25 built for herdr: each remaining useNative tools.nix entry now has a verified non-interactive Linux install path. claude-code, codex, and pi-coding-agent get a nativeInstallUrl (their own official install.sh, confirmed non-interactive with no /dev/tty attached by running each live in a clean Ubuntu 22.04 container); skills has no install script upstream at all, so it gets a new nativeInstallNpmPackage field instead, plumbed through the same activation-script mechanism. Adds nativeInstallBinName for the two tools (claude-code, pi-coding-agent) whose real ~/.local/bin launcher name differs from their tools.nix entry name, so the "already installed" skip check doesn't re-run their installer every rebuild. Adds pkgs.nodejs to home.packages on Linux, since skills and pi-coding-agent's npm-backed launchers need node on PATH at runtime, not just during install - verified this also works with node's own directory read-only, matching how it lives in the Nix store. Extends tests/ubuntu-support.test.sh accordingly and confirms darwinConfigurations.mac's drvPath stays byte-for-byte unchanged.
Reproduced on a genuinely minimal jammy container (Docker Hub's ubuntu:22.04 happens to ship tar, masking this in prior testing): codex's installer fails with "tar is required to install Codex." on a base image that lacks tar, and because installNativeTools concatenates every tool's install block into one activation script with no per-tool error isolation, that failure (under set -e/pipefail) aborted the script before it ever reached herdr - next in tools.nix's ordering - or skills/pi-coding-agent after it. - Add gnutar to home.packages (Ubuntu-only) and to installNativeTools' own curated PATH export, so tar is Nix-guaranteed rather than assumed from the base image. - Wrap each tool's install subshell with `|| echo "WARNING: ..." >&2` so one tool's failure is reported loudly but no longer blocks the rest. - Add regression coverage: gnutar presence, and a network-free fault-isolation test that patches the real generated activation script with local stand-ins to prove one broken installer doesn't block the other four.
test: add root test runner
…e-installer fix: harden Ubuntu native tool installation
Ubuntu remotes with no desktop/gnome-keyring had no persistent ssh-agent process: AddKeysToAgent had nothing long-lived to add keys to, so each git/ssh invocation re-prompted for the key passphrase. Even Ubuntu's own bundled ssh-agent user unit no-ops here since it's gated on an X11 session marker file that doesn't exist on a headless server. Enable home-manager's native services.ssh-agent module on Linux only: a systemd --user unit keeps one agent alive until logout/reboot, with SSH_AUTH_SOCK wired into zsh automatically. macOS is untouched (already covered by launchd + Keychain); darwinConfigurations.mac's drvPath is byte-for-byte unchanged. Verified end-to-end in a systemd-PID1 Ubuntu 22.04 container: reproduced the bug (fresh login, agent running but no key cached, prompts every time), applied the fix, added a passphrase-protected key once, then confirmed ssh-add -l lists it across multiple independent fresh login sessions with no re-prompt, and that it survives with no active session via loginctl linger.
The prior fix enabled home-manager's services.ssh-agent systemd --user unit but never enabled lingering. Verified on a real Ubuntu VM (Lima, genuine independent SSH login sessions, not docker exec) that without loginctl enable-linger, systemd-logind tears down the user's whole systemd --user instance - agent and any cached key included - the moment the last SSH session closes, so every fresh connection gets an empty agent and the passphrase prompt returns every time, exactly as reported. home.activation.enableSshAgentLinger now runs loginctl enable-linger on every Linux rebuild (no sudo needed, takes effect immediately). It's a no-op on Darwin but still shifts the generated activation script text, so tests/ubuntu-support.test.sh's pinned EXPECTED_DARWIN_DRVPATH moved too.
There was a problem hiding this comment.
Thanks for taking an interest in these dotfiles.
This repo is public so you can read it, learn from it, and fork it freely, but it is Kun's personal setup and does not accept pull requests or feature requests.
If you want to adapt something, please fork the repo and make it yours.
If you found a bug, please open a bug report issue instead of sending a patch.
Pull requests are auto-closed here so the repo can stay personal and predictable.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thanks for the interest
Thanks for taking an interest in these dotfiles.
This repo is public so you can read it, learn from it, and fork it freely, but it is Kun's personal setup and does not accept pull requests or feature requests.
If you want to adapt something, please fork the repo and make it yours.
If you found a bug, please open a bug report issue instead of sending a patch.
Pull requests are auto-closed here so the repo can stay personal and predictable.