The CDI generator aborted with "failed to initialize NVML: Driver Not
Loaded", taking docker.service with it (requiredBy) and failing the
switch.
Two causes. The nixpkgs NVIDIA module only adds nvidia/nvidia_modeset/
nvidia_drm to boot.kernelModules when services.xserver.enable is set,
which is false on this Wayland-only host, so load them explicitly.
nvidia_uvm stays out: the module's modprobe softdep loads it once the GPU
device exists.
The generator also runs during activation, when a module rebuilt against a
new kernel cannot be loaded until reboot -- a guaranteed failure after
every kernel bump. Guard it with ConditionPathExists on
/proc/driver/nvidia/version so it skips rather than fails; the toolkit's
udev rule restarts it when the device appears, so the specs are generated
on the next boot.
The stock GPU has been replaced with a Quadro P400 (Pascal, GP108). Add
hosts/MacPro31/nvidia.nix:
- Driver branch 580 (nvidiaPackages.legacy_580), not the nixpkgs default
production branch (595.x). 580 is the last branch supporting
Maxwell/Pascal/Volta and is an LTS branch until Aug 2028; a newer one
does not drive this card.
- modesetting.enable for Wayland (nvidia-drm.modeset=1), open = false
(the open kernel modules need Turing or later), and sway
--unsupported-gpu, which wlroots requires with the proprietary driver.
- Docker with GPU access via CDI (hardware.nvidia-container-toolkit),
rather than the deprecated virtualisation.docker.enableNvidia runtime
wrapper. Containers run with --device=nvidia.com/gpu=all and must ship
a CUDA 12.x or older runtime: CUDA 13 dropped sm_61.
The driver packages are unfree, so allowlist them in unfreePackages; they
are not cached and the kernel module builds on the host.
Also declare features.cpu.microarchLevel = 1 for this machine: the
Harpertown Xeons have SSE4.1 but no SSE4.2/POPCNT, which switches off
Claude Code through the fleet-wide gate.
Claude Code runs on Node, whose V8 build requires SSE4.2 and POPCNT
(x86-64-v2). On an older x86_64 CPU it does not run, so it must not be
installed there in the first place.
Nix cannot detect the CPU (pure evaluation, hosts often built elsewhere),
so add features.cpu.microarchLevel: the psABI level a host declares about
itself, defaulting to 2. features.claudeCode.enable derives from it, and
home/claude.nix reads that through home-manager's osConfig and installs
nothing -- CLI, CLAUDE.md, output style or memory symlink -- when it is
off. Hosts without the option (Darwin, the standalone homeConfigurations)
keep the tool enabled.
An assertion fails evaluation if a host force-enables the flag below the
required level, so the mistake surfaces in nix flake check rather than as
an illegal-instruction crash on the machine.
gcx stores its OAuth access and refresh tokens in the system keychain
unconditionally -- its config file keeps only opaque `keychain:gcx:v2:...`
handles -- and exposes no plaintext fallback. With nothing owning
org.freedesktop.secrets on this headless WSL box, `gcx login` authenticates
against Grafana and then dies writing its config: "The name is not activatable".
Add services.headlessSecretService: gnome-keyring as a systemd --user service,
unlocking the login keyring at start. home-manager's own services.gnome-keyring
does not fit here on two counts -- it is WantedBy graphical-session-pre.target,
which never activates without a desktop session, and it passes no --unlock, so
writes would block on a GUI prompter that does not exist.
Only the secrets component is started. The ssh component is deliberately off: it
would claim SSH_AUTH_SOCK and displace services.ssh-agent, breaking SSH auth and
signed commits.
The unlock password defaults to a random one generated on first activation under
$XDG_DATA_HOME. The passwordFile option is the seam for supplying it from an
agenix secret instead, once that lands.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Upstream will stop defaulting hardware.asahi.enable to true and currently emits an evaluation warning to that effect. Set it explicitly on the lyrathorpe-mbp (MBP-Asahi) host to silence the warning and be robust to the future default change. Verified the warning no longer appears in the host toplevel eval.
## Summary
Follow-up cleanups from the post-refactor audit (issues #50–#53). All behaviour-preserving except the work-host changes (kube-tmux + Lens removal).
## Changes
- **#51** `refactor(ssh)` — move `services.openssh.enable` + `firewall.allowedTCPPorts = [ 22 ]` into `modules/ssh.nix`; drop the duplicated lines from T400, MacPro31, RPi5.
- **#50** `fix/feat(work)` — load kube-tmux from a pinned `flake = false` input (it is not in nixpkgs) and reference `${inputs.kube-tmux}/kube.tmux` directly, so the status line no longer depends on a manual `$HOME/code/kube-tmux` checkout. (Supersedes the interim file-existence guard.)
- **#52** `chore` — gitignore the untracked `tf-inspect/` scratch project.
- **#53** `chore` — remove the unused Lens package entirely (`pkgs.lens` + its unfree entry; `unfreePackages` is now just `claude-code`), fix the `nil`→`nil_ls` LSP doc, remove the redundant `.editorconfig` block, name the RPi5 Docker subnet in a `let` binding.
## Deferred (from #53, noted in the commit)
- `.gitignore` firmware entry — documented behaviour, low value, left as-is.
- Per-eval `nixpkgs-unstable` overlay import — inherently per-system; no clean single-import hoist.
## Verification
- `nix flake check` passes (treefmt, deadnix, statix, pre-commit, all hosts + Darwin + homeConfigurations).
- Derivation-path diff vs `main`: `lyrathorpe-mbp`, `lyrathorpe-t400`, `lyrathorpe-macpro31`, `lyrathorpe-rpi5` are byte-identical (confirms #51 and the subnet `let` binding change nothing). Only `emmathorpe-edaas` differs — the kube-tmux input (#50) and the Lens removal (#53).
Closes#50, #51, #52, #53.
---------
Co-authored-by: Emma Thorpe <emma.thorpe@citrix.com>
Reviewed-on: #54
## Summary
Separates user identity (data) from the reusable Nix modules and lets a host declare any number of users, replacing the previous one-user-per-host structure. Also restructures the tree and exposes the home config for use off these hosts.
## Changes
- **User registry** (`users/registry.nix`): per-user identity (name, email, groups, authorized + signing keys) as the single source of truth; no user data hardcoded in modules.
- **Multi-user `mkHost`**: a host declares a `users` set keyed by username; per-user identity is injected into each home config via the `identity` module arg.
- **Restructured layout**: `users/`, `home/`, `modules/`, `hosts/`, `lib/` replace the former `lyrathorpe/` and `system/` trees.
- **Portable outputs**: standalone `homeConfigurations."<user>@<system>"` (the portable subset — shell, git, editor, claude) plus an exported `homeModules` for use on machines not managed by this flake, or as an input to other flakes.
- Docs (`README.md`, `home/README.md`) and `.gitignore` updated for the new paths.
## Fixes
- Closes#46 — shared user module authorized one user's SSH key for every account.
- Closes#47 — git committer identity hardcoded as defaults instead of per-user.
- Closes#48 — EDaaS systemd linger hardcoded to a literal username.
## Verification
- `nix flake check` passes: treefmt, deadnix, statix, pre-commit, and evaluation of all NixOS hosts + Darwin + homeConfigurations.
- Derivation-path comparison vs `main`: `lyrathorpe-mbp` and `emmathorpe-edaas` are byte-identical; `lyrathorpe-t400`, `lyrathorpe-macpro31` and `lyrathorpe-rpi5` differ only by de-duplicating a repeated `authorized_keys` entry (confirmed with nix-diff — no other change).
- Standalone `homeConfigurations."lyrathorpe@x86_64-linux".activationPackage` builds.
## Notes
- `emmathorpe` has no personal authorized key yet (it previously inherited Lyra's key via the bug in #46); the registry entry is intentionally empty — add a real key if SSH login as `emmathorpe` is wanted (moot on the WSL host).
- A two-repo (public dotfiles / private systems) split is deferred by design; this internal restructure is the prerequisite for it.
---------
Co-authored-by: Emma Thorpe <emma.thorpe@citrix.com>
Reviewed-on: #49