Audited the working tree and full git history for committed secrets
(gitleaks + trufflehog + manual grep, see secrets-inventory.md, kept
local/gitignored per the spec). Found: a password hash shared by root
and the nixos user across every host, two live Beszel monitoring
tokens, and a GitHub fine-grained PAT embedded in a home-manager
nix.conf.
Migrates all of them to sops-nix:
- .sops.yaml + secrets/*.yaml, encrypted for admin + the age keys
derived (via ssh-to-age) from each live host's existing SSH host
key — no new key material transferred to any machine.
- users.users.{root,nixos}.hashedPasswordFile replaces the inline
hashedPassword shared by every target.
- The GitHub PAT moves from a home-manager-managed, store-visible
nix.conf to a sops.templates-rendered file included via nix.conf's
native !include, system-wide instead of per-user.
- Beszel TOKEN moves from `environment` (store-visible) to
`environmentFile` (runtime-only via sops.templates); the dead
commented-out docker token is removed from the tree entirely.
Added a tracked pre-commit hook (gitleaks protect --staged, wired via
core.hooksPath) so a secret can't be committed by accident again, and
documented the sops workflow in README.md.
Structural verification only: all 17 flake targets evaluate, and
`nix build --dry-run --no-link` succeeds for the three currently
deployed hosts. Per CLAUDE.md, actual `nixos-rebuild switch` — the
step that confirms secrets decrypt and services start on a real
machine — is left for manual verification.
Git history still contains the original plaintext secrets; scrubbing
history (Milestone 3) and rotating every credential (Milestone 4) are
separate, deliberately gated steps per remove-sensetive-info-refactor.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
NixOS LAN Configurations
Flake-based NixOS configuration repository for Wayne's LAN servers and workstation.
Hosts
Targets are named <platform>-<buildtype>, generated from two orthogonal
pieces composed in flake.nix:
- Platforms (what it runs on):
linode,proxmox,lxc - Build types (what it's for):
minimal,nix-cache,server,docker,gui,pxe-boot
Not every combination exists — pxe-boot has no linode variant, since
PXE/DHCP/TFTP need LAN L2 adjacency that a Linode VPS doesn't have. The full
list:
| Target | Purpose |
|---|---|
linode-minimal |
Minimal NixOS host profile on a Linode VPS (real, deployed) |
proxmox-minimal |
Minimal NixOS host profile on Proxmox (real, deployed — previously the flat nix-minimal target) |
lxc-minimal |
Minimal NixOS host profile in a Proxmox LXC container |
linode-nix-cache / proxmox-nix-cache / lxc-nix-cache |
Local Nix binary cache and remote builder (proxmox-nix-cache is the real, deployed one — previously the flat nix-cache target) |
linode-server / proxmox-server / lxc-server |
Storage, NFS, backup, and monitoring exporter host (proxmox-server is the real, deployed one — previously the flat server target) |
linode-docker / proxmox-docker / lxc-docker |
Docker host for the main container stack (proxmox-docker is the real, deployed one — previously the flat docker target) |
linode-gui / proxmox-gui / lxc-gui |
Cinnamon desktop workstation (proxmox-gui is the real, deployed one — previously the flat nixos target) |
proxmox-pxe-boot / lxc-pxe-boot |
HTTP/iPXE boot asset host (proxmox-pxe-boot is the real, deployed one — previously the flat pxe-boot target) |
Each buildtype's hosts/<name>/host.nix carries the per-machine identity
(hostname, hostId, per-machine secrets, system.stateVersion) that must stay
fixed regardless of which platform it's built for — see
flake-target-refactor-spec.md for the full rationale. Every deployed host
stamps its own active target name into /etc/flake-target at build time, so
nixos-rebuild switch --flake .#$(cat /etc/flake-target) always picks up the
right one even after a platform migration changes the flake attribute name.
List hosts with:
nix eval --json .#nixosConfigurations --apply builtins.attrNames | jq -r '.[]'
Layout
| Path | Purpose |
|---|---|
flake.nix |
Flake inputs, the mkTarget platform × build-type generator, and nixosConfigurations outputs |
hosts/<name>/host.nix |
Per-machine identity: hostname, hostId, per-machine secrets, system.stateVersion |
hosts/nixos/home.nix |
Workstation-specific Home Manager config (used by the gui build type) |
modules/platforms/ |
Platform-specific config: virtualisation guest tools, boot method, hardware config (linode.nix, proxmox.nix, lxc.nix) |
modules/build-types/ |
Build-type-specific config: what makes a system minimal/server/docker/gui/pxe-boot/nix-cache |
modules/common/ |
Shared NixOS config, Home Manager, aliases imported by every host |
modules/nix-cache/ |
Binary cache and remote builder client/server modules |
docs/ |
Operational notes for cache, builders, lock updates, and boot services |
scripts/ |
Codex setup and validation helpers |
Validation
Safe validation commands for Codex and local review:
bash scripts/codex-setup.sh
bash scripts/codex-maintenance.sh dry-run
bash scripts/codex-maintenance.sh
For individual host evaluation:
nix eval .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath --raw
Use nix build --dry-run --no-link when build planning is needed. Do not run
deployment, install, disk formatting, mount, or reboot commands from automated
review sessions.
Operations
- Host rebuilds should consume the committed
flake.lock. - Routine dependency updates should happen through the flake lock automation
described in
docs/flake-lock-automation.md. nix-cacheserves substitutes over HTTP and can act as a remote builder for client hosts.pxe-bootserves iPXE boot files over HTTP from/srv/pxe.
Security Notes
Do not commit tokens, private keys, live credentials, or new password hashes
as plaintext. Secrets are managed with sops-nix:
encrypted files live under secrets/, recipients (per-host age keys derived
from each host's existing SSH host key, plus an admin key) are declared in
.sops.yaml. To add or edit a secret:
nix-shell -p sops --run "sops secrets/<file>.yaml"
then reference it from a module via config.sops.secrets."<name>".path
(or sops.templates for values that need to be embedded in a rendered
config file, e.g. nix.conf's access-tokens). Never write a secret value
directly into a tracked .nix file. A pre-commit hook (.githooks/,
enabled via git config core.hooksPath .githooks, done automatically by
scripts/codex-setup.sh) runs gitleaks protect --staged to catch mistakes
before they're committed.
This repository's git history still contains secrets committed before this
migration (see remove-sensetive-info-refactor.md) — those are being
scrubbed and rotated separately; don't treat the repo as safe to make public
until that's finished.