Reviewed-on: #71
nixfiles
NixOS / nix-darwin / home-manager configuration for all hosts, built from a single flake.
Hosts
Defined in the host table in flake.nix:
| Configuration | System | Machine |
|---|---|---|
lyrathorpe-mbp |
aarch64-linux |
MacBook Pro (Apple Silicon, Asahi) |
lyrathorpe-t400 |
x86_64-linux |
ThinkPad T400 — install notes |
lyrathorpe-macpro31 |
x86_64-linux |
Mac Pro 3,1, desktop — install notes |
emmathorpe-edaas |
x86_64-linux |
Work WSL box (NixOS-WSL) — notes |
lyrathorpe-rpi5 |
aarch64-linux |
Raspberry Pi 5 headless server: Docker host + nginx reverse proxy — install notes |
lyrathorpe-mac |
aarch64-darwin |
macOS (nix-darwin) — notes |
Shared layers: home (home-manager: shell, git, editor),
modules/common-nixos.nix (all NixOS hosts: fonts, nix-ld, caches),
modules/workstation.nix (physical graphical hosts: audio, thermald,
earlyoom, fwupd), modules/laptop.nix (laptops: Wi-Fi, Bluetooth, power,
lid), modules/desktop.nix (wired desktops: NetworkManager), and
modules/ssh.nix (key-only sshd). The x86 hosts also pull nixos-hardware
profiles. The full module catalogue is below.
Repository layout
flake.nix # inputs, mkHost/mkDarwinHost, the host tables, dev shell + checks
flake.lock # pinned input revisions (Renovate keeps this fresh)
modules/ # reusable NixOS system modules (see "Module catalogue")
home/ # home-manager profile: shell, git, editor, claude, desktop, sway
users/ # identity registry + per-user home extras (see "Users")
hosts/<Name>/ # per-machine config: configuration.nix + hardware-configuration.nix
lib/ # small pure helpers (currently the Catppuccin Mocha palette)
.gitea/workflows/ # CI (nix flake check + per-host eval)
statix.toml # lint config (house-style lints disabled)
.editorconfig # base whitespace style
tf-inspect/ # UNRELATED scratch project (gitignored, its own git repo);
# RouterOS / home-services Terraform, not part of this flake
Each nixosConfiguration / darwinConfiguration is assembled in flake.nix
from three layers: the shared baseModules (or darwinBaseModules), the
per-form-factor and nixos-hardware modules listed in the host table, and the
per-machine hosts/<Name>/configuration.nix. Home-manager is wired in as a
system module; each user's home is composed from the homeModules list in that
host's table entry.
Module catalogue
Reusable NixOS modules under modules/. "Imported by" says how a
module reaches a host: baseModules (every NixOS host, via flake.nix),
host table (listed explicitly per host in flake.nix), or transitively
(pulled in by another module's imports).
| Module | Imported by | What it does / when to use it |
|---|---|---|
common-nixos.nix |
baseModules (all NixOS) | Timezone/locale, store hygiene (auto-optimise, big download buffer, no auto-GC), the nix-community binary cache, nix-ld, base CLI (git, fastfetch), and the fleet-wide font stack. |
users.nix |
baseModules (all NixOS) | Builds users.users from the registry for the host's hostUsers; enables zsh; enables Firefox + Thunderbird only when features.swayDesktop.enable is on. Applies per-user linger. |
features.nix |
baseModules (all NixOS) | Declares feature-flag options (currently features.swayDesktop.enable) so any host can read/set them without importing the heavy implementation module. |
workstation.nix |
transitively (via laptop/desktop) | Form-factor-agnostic base for physical graphical hosts: turns on swayDesktop, Dvorak console, PipeWire, firewall (default-deny), fstrim, earlyoom, fwupd, thermald (x86), redistributable fw. |
laptop.nix |
host table (MBP, T400) | imports workstation.nix, then adds the portable bits: iwd Wi-Fi, lid suspend/lock, Bluetooth + blueman. |
desktop.nix |
host table (Mac Pro) | imports workstation.nix, then swaps Wi-Fi for wired NetworkManager. Pair with portable = false in the host table. |
sway.nix |
host table (graphical hosts) | Implementation of features.swayDesktop: the system Sway package, the greetd/ReGreet (cage) greeter forced to Dvorak, xdg-portal, Wayland utility packages. Home-side Sway config is in home/sway.nix. |
ssh.nix |
host table (T400, Mac Pro, RPi5) | Enables sshd, opens port 22, enforces a key-only policy (no password / keyboard-interactive, no root). Authorized keys come from the registry via users.nix. |
firmware/ |
referenced by MBP host config | Committed Apple peripheral firmware blobs for the Asahi MBP (see "MacBook (Asahi) firmware"). |
Form-factor decision: a laptop imports laptop.nix (default
portable = true); a wired desktop imports desktop.nix and sets
portable = false; a headless server imports neither (leaves
features.swayDesktop.enable at its default false) and adds only what it
serves. portable is threaded through to home/sway.nix, which drops the
battery block and brightness keys on desktops.
Users
Identity is data, kept separate from the reusable modules:
users/registry.nix— one entry per user (display name, email, supplementary groups, authorized + signing keys). This is the single source of identity; no user data is hardcoded in the modules.- Each host's table entry declares a
usersset keyed by username; every entry lists that user's home-module composition (the shared./homebundle plus any per-user modules, e.g.users/emmathorpe/work.nix) and optional per-host-user system bits such aslinger. mkHostbuilds each account from the registry and injects the matching identity into that user's home config as theidentitymodule arg. A host can therefore declare any number of users.
Per-user home extras live under users/<name>/:
users/lyrathorpe/home.nix— personal extras (an ssh host shortcut, gammastep coordinates); imported on Lyra's hosts.users/emmathorpe/work.nix— the work toolchain (kubectl/helm/az/etc.), work-only LSP servers, and the corporate ssh handling; importsusers/emmathorpe/renovate-review.nix, the daily headless Renovate-PR review timer (EDaaS only).
Portable home (off-NixOS / external consumers)
The home config is also exposed for use beyond these hosts:
homeConfigurations."<user>@<system>"— a standalone home-manager profile (the portable subset: shell + git + editor + claude) that can be activated on a machine this flake does not manage:home-manager switch --flake .#"lyrathorpe@x86_64-linux". The desktop/sway modules are intentionally excluded (they rely on a NixOS-provided Sway/Firefox binary).homeModules— the reusable modules exported so another flake can import them (inputs.<this>.homeModules.default). Consumers must supply the module args these expect:inputsalways,identityfor git/desktop,portablefor sway.
Applying
# NixOS
sudo nixos-rebuild switch --flake .#<configuration>
# Darwin
darwin-rebuild switch --flake .#lyrathorpe-mac
On a host whose networking.hostName matches its flake attribute (the WSL box
and the Pi are set up this way), nh os switch resolves the configuration from
the hostname with no --flake/-H flag.
Adding a new host
- Create
hosts/<Name>/. Addconfiguration.nixwith the host-specific bits only:networking.hostName, bootloader (firmware-specific — it is deliberately not set in the shared modules), and any per-machine hardware quirks. Keep anything reusable inmodules/instead. - Hardware config. Generate
hardware-configuration.nixon the real machine withnixos-generate-configand commit it. If the machine does not exist yet, commit a clearly-labelled placeholder so the host still evaluates in CI (see the existing T400 / RPi5 placeholders), and replace it at install. These files are excluded from the formatter and linters. - Add a host-table entry in
flake.nix. Underhosts(NixOS) ordarwinHosts(macOS), setsystem, themoduleslist (host config + form factor + anynixos-hardwareprofiles), and theusersmap (each user'shomeModules). Choose the form factor per the decision note above; a headless host imports neitherlaptop.nixnordesktop.nix. - Users. If the host introduces a new person, add them to
users/registry.nixfirst; otherwise reference an existing username. - Verify.
nix flake checkformats, lints, and evaluates every host — including the new one — so a broken entry fails locally before CI. Thensudo nixos-rebuild switch --flake .#<configuration>on the machine.
No change to CI is needed: the host-eval step discovers hosts from the flake
(attrNames of the configuration sets), so a new entry is picked up
automatically.
Shell environment & keybindings
- Interactive shell features (zsh, tmux, git, ssh, CLI tools, auto-tmux):
home/README.md. - All Sway / tmux / foot / zsh keyboard shortcuts:
home/KEYBINDINGS.md.
Login / greeter
Graphical (Sway) hosts log in through a Wayland greeter — greetd running
ReGreet inside the cage kiosk compositor — implemented in
modules/sway.nix, gated on
features.swayDesktop.enable (the option is declared in
modules/features.nix, so headless hosts
can leave it off without importing modules/sway.nix). The greeter is forced to Dvorak
to match the console and Sway session. Headless hosts (the WSL work box and the
Raspberry Pi server) keep plain TTY login. The target account needs a password
(passwd <user>) before it can log in.
MacBook (Asahi) firmware
The MBP host references modules/firmware/ for Apple peripheral
firmware (Wi-Fi/Bluetooth). These blobs are committed (tracked) even though
.gitignore lists the directory: the flake is git+file, so it only sees
tracked files — untracking them breaks lyrathorpe-mbp evaluation (and the CI
host-eval) because the config can't find the firmware. They are not
redistributable; the repo is private.
To refresh them, copy the firmware extracted during the Asahi install (from
/etc/nixos/firmware, or re-extract per the
Asahi NixOS docs) into
modules/firmware/ and commit with git add -f.
Development
A dev shell and a formatting/lint gate are wired through the flake:
nix develop— shell withdeadnix,statix,treefmt, and the gitpre-commithooks (installed automatically on first entry).nix fmt— formats the tree viatreefmt(nixfmt + shfmt + prettier; generated files andflake.lockare excluded).nix flake check— runs formatting,deadnix,statix, the pre-commit hooks, and evaluates every host..editorconfigcarries the base style;statix.tomldisables the two house-style lints (repeated_keys,empty_pattern).
CI
.gitea/workflows/ci.yaml runs nix flake check
(formatting, deadnix, statix, the pre-commit hooks) and evaluates every
NixOS and Darwin host configuration on push/PR. It always runs (no paths:
filter) so the required check never hangs pending; the heavy Nix steps are
skipped when a PR touches no .nix/lockfile/workflow file, and the job still
reports green.