Both check-nixos.yml workflows (GitHub + Gitea) now call scripts/codex-maintenance.sh instead of a hand-rolled eval-only loop, closing a real gap: CI previously enforced none of the secret grep, nixpkgs-fmt, or statix checks that codex-maintenance.sh already runs locally — nothing was stopping that from regressing. One script now backs both, instead of two copies that can drift from each other. codex-maintenance.sh itself is extended to cover buildable surface that wasn't validated anywhere before: packages.x86_64-linux.*, plus config.system.build.tarball (lxc-* hosts) and config.system.build.diskoImagesScript (proxmox-*, excluding the installer's own proxmox-lxc target, which has no disko config). Also: - scripts/prepare-host-key.sh: dropped the redundant [path-to-nixos-repo] parameter — it always defaults to the repo the script itself lives in now, so a second argument never made sense after the nix-auto-installer migration. - Removed prepare.sh (dead pre-disko manual parted/mkfs/mkswap partitioning, fully superseded) and scripts/create-linode-installer-disk.sh (incomplete draft for an abandoned dd-via-rescue-mode approach; Linode hosts already deploy fine through the normal auto-installer flow). - docs/pxe-boot.md: fixed a stale `nixosConfigurations.pxe-boot` eval command (pre-refactor flat name, not a real flake attribute anymore) and added a cross-reference to docs/auto-installer.md. - CLAUDE.md/README.md: full documentation pass reconciling this session's changes — modules/installer/, modules/pxe-boot/, the LXC/Proxmox image-building deployment paths, corrected the password-hash/SSH-key locations in the safety-rules section (both had drifted to reference files/paths that no longer exist), and added session-workflow guidance to prefer targeted host evals over full-repo sweeps for incremental changes (explicitly scoped to interactive sessions, not CI). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01La55Nsss8jZ7ZuzUV9mfot
9.1 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Repo purpose
Flake-based NixOS configuration for Wayne's LAN servers and workstation. There is no application code here — changes are Nix module edits that affect real machines when deployed.
Safety rules (read before touching anything)
- Never run
nixos-rebuild switch|boot|test,nixos-install,parted,mkfs,mkswap,swapon,mount, or any other destructive disk/deploy command from an agent session, even if asked indirectly. Deployment is done manually by the operator on the target host. - Validation is limited to evaluation, linting, formatting checks, and
nix build --dry-run --no-link. - Do not add secrets, tokens, private keys, or new password hashes to the repo.
- This repo currently contains committed password hashes in
modules/installer/common.nix(the auto-installer's own root/nixos login — a deliberate, documented choice, seedocs/auto-installer.md, not accidental tech debt) and SSH public keys invariables.nix(vars.adminSshKey,vars.remoteBuilderAuthorizedKeys) plus a couple of per-hostKEYvalues for beszel-agent auth (hosts/server/host.nix,hosts/nix-cache/host.nix). Don't use the installer's hardcoded hash as a template for a real host — every other host uses sops-nix (hashedPasswordFile, see "Security Notes" inREADME.md). Flag any new secret-like string you encounter instead of committing it. host-keys/is gitignored — locally-generated private SSH host keys for the auto-installer (seedocs/auto-installer.md). Never commit its contents; ifgit statusever shows it as trackable, something is wrong.
Commands
# One-time environment bootstrap (installs Nix if missing, prints hosts)
bash scripts/codex-setup.sh
# Full validation: secret grep, nixpkgs-fmt --check, statix lint, eval all hosts
bash scripts/codex-maintenance.sh
# Same, plus a dry-run build (no result symlink) of every host's toplevel
bash scripts/codex-maintenance.sh dry-run
# List the hosts the flake currently exposes
nix eval --json .#nixosConfigurations --apply builtins.attrNames | jq -r '.[]'
# Evaluate a single host without building (fast sanity check)
nix eval .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath --raw
# Dry-run build a single host
nix build --dry-run --no-link .#nixosConfigurations.<host>.config.system.build.toplevel
Formatting/lint tools (nixpkgs-fmt, statix) are not installed locally; the
maintenance script pulls them via nix run github:NixOS/nixpkgs/nixos-25.11#<tool>.
There is no test suite — "correctness" here means the flake evaluates and
nixpkgs-fmt/statix are clean.
In an interactive agent session, prefer targeted checks over full-repo
sweeps: after editing one or two hosts/modules, evaluate just the
nixosConfigurations.<host> you touched (plus any config.system.build.tarball
/diskoImagesScript/package output affected) rather than looping over every
host — codex-maintenance.sh evaluates 19 hosts plus every package/tarball/
image variant now and is slow to run after each small change. Reserve a full
codex-maintenance.sh run for changes that plausibly affect every host
(modules/common/*, flake.nix, variables.nix) or as a final check before
committing. This is a session-workflow preference only — it does not apply to
CI, which should keep running the full script on every push/PR regardless of
diff size; that's the point of it.
Architecture
flake.nix is the single entry point. It generates one
nixosConfigurations.<platform>-<buildtype> attribute per target via the
mkTarget function, composed from:
nixosSystem {
modules = [
disko.nixosModules.disko
sops-nix.nixosModules.sops
./modules/common/configuration.nix
./modules/platforms/${platform}.nix # what it runs on
./modules/build-types/${buildType}.nix # what it's for
hostPath # hosts/<name>/host.nix — per-machine identity
home-manager.nixosModules.home-manager { ... }
] ++ (client-only modules, for every buildType except "nix-cache" itself)
}
Platforms: linode, proxmox, lxc. Build types: minimal, nix-cache,
server, docker, gui, pxe-boot. Not every combination is built — e.g.
pxe-boot has no linode variant (PXE/DHCP/TFTP need LAN L2 adjacency a
Linode VPS doesn't have). Treat flake.nix's generatedTargets as the source
of truth for which hosts exist — README.md, AGENTS.md,
docs/flake-lock-automation.md, and the CI eval workflows
(.github/workflows/check-nixos.yml, .gitea/workflows/check-nixos.yml) list
hosts by hand (or, for the CI workflows, evaluate the flake dynamically) and
can drift from it, so re-check them against flake.nix when adding or
removing a host.
Composition pattern
hosts/<name>/host.nix— per-machine identity only: hostname, hostId, per-machine secrets,system.stateVersion. These files carry noimportsof their own beyond narrow parameterized helpers (seemodules/beszel/host-token.nixbelow) — all shared behavior comes from the platform/build-type modules composed inflake.nix, not from the host file.modules/platforms/{linode,proxmox,lxc}.nix— platform-specific config: boot method, guest tooling, and (for linode/proxmox) the hypervisor-specific hardware config, imported directly by the platform module itself (../hardware-configuration/vm/{proxmox,linode}.nix) — not wired in fromflake.nix.lxc.nixhas no hardware-configuration counterpart since containers share the host kernel; instead it imports nixpkgs' ownvirtualisation/proxmox-lxc.nix, which gives everylxc-*host aconfig.system.build.tarballoutput (pct restore-ready, no install step — seedocs/auto-installer.md).modules/build-types/*.nix— what a system is for: minimal/server/docker/gui/pxe-boot/nix-cache.modules/common/configuration.nix— base NixOS config imported by every host: locale, users, nix settings, git.modules/common/home.nix/hosts/nixos/home.nix— Home Manager config for thenixosuser; thenixosworkstation (guibuild type) has its own, other hosts sharemodules/common/home.nix.modules/disko/proxmox.nix— declarative disk layout (GPT: ESP + swap + ext4 root) via disko, used by all Proxmox-VM hosts (proxmox-*, notlxc-*). Also carriesimageSize/imageName, letting everyproxmox-*host be built as a standalone,qm importdisk-ready.rawimage with no install step — seedocs/proxmox-images.md.modules/boot/efi.nix— systemd-boot + EFI vars, paired with the disko module.modules/installer/— the auto-installer environment (ISO/netboot/Proxmox LXC):common.nix(shared config + the generatedauto-install.sh),iso.nix/proxmox-lxc.nix(per-variant),host-keys.nix(optionally bakeshost-keys/into the image under--impure). Seedocs/auto-installer.md.modules/pxe-boot/stage-installer-artifacts.nix— builds the installer's netboot image and stages it on thepxe-boothost so its iPXE menu can chain straight to it. Seedocs/pxe-boot.md.modules/nix-cache/{client,server,remote-builder-client}.nix— binary cache substituter + SSH remote-builder wiring; seedocs/nix-cache.mdfor the full design (per-host local stores, no shared/nix/store, and how thenixremotesigning/SSH keys fit together).modules/beszel/host-token.nix— parameterized helper module ({ name, sopsFile }) that wires a host's beszel-agent sops secret/template andenvironmentFile; used byhosts/server/host.nixandhosts/nix-cache/host.nixto avoid duplicating that boilerplate.modules/tailscale/,modules/docker/,modules/networking/,modules/traefik/,modules/services/*— single-purpose, single-host feature modules (e.g.docker/enable-service.nix,services/zfs/enable-service.nix). Grepmodules/build-types/*.nixfor each build type'simportslist to see which modules apply where.
New host = new hosts/<name>/host.nix + a matching
mkTarget { platform; buildType; hostPath; } entry added to flake.nix's
generatedTargets, composed from existing modules/* pieces rather than
duplicating config.
Other docs worth reading before touching these areas
docs/nix-cache.md— nix-cache binary cache/remote-builder design and key handling.docs/pxe-boot.md— thepxe-boothost's iPXE/TFTP/HTTP boot chain and directory layout under/srv/pxe.docs/auto-installer.md— the installer environment (ISO/netboot/Proxmox LXC),host-keys/and the sops-nix pre-seeding problem it solves, and whylxc-*hosts are deliberately excluded from its menu.docs/proxmox-images.md— buildingproxmox-*hosts as standalone.rawdisk images (disko's image builder) instead of installing, and deploying the result to Proxmox.docs/flake-lock-automation.md— howflake.lockupdates flow through CI (schedulednix flake updatePR + host-eval-on-PR workflow) and why hosts should track the committed lock file rather thannixos-rebuild --upgrade-all.