Personal NixOS + nix-darwin configuration. Four hosts today: a UTM aarch64 VM for refinement (nixos-vm), an AWS EC2 x86_64 box for work-only headless dev (mercury), an HP ProDesk x86_64 desktop (metis), and an Apple Silicon Mac mini (mac-mini, the first nix-darwin host). The same flake-parts tree builds them all; the same set of decisions — module structure, naming, default stances, operational posture — governs every host.
Shared publicly for transparency and so others can lift pieces useful to their own configurations. Not maintained as a generalisable template — decisions reflect one operator's preferences and constraints (PRD §2.2).
flake.nix # flake-parts entry point
parts/ # flake-parts modules (CI, formatter, dev-shells, nixos+darwin configurations)
hosts/<host>/ # per-host instance: hardware, identity, imports of foundation + bundles
modules/{nixos,darwin,shared}/ # system-layer modules (foundation, bundles, standalone)
home/{nixos,darwin,shared}/ # home-manager modules (foundation, bundles, standalone)
lib/ # mk-host wrapper, per-host parameters, helper modules
docs/ # the rationale behind every decision
A host file is short: it imports foundation.nix (identity, admin, posture), opts into capability bundles for what the host does (desktop-env, remote-access, cli-tooling, …), and adds standalone modules for capabilities that haven't yet earned a bundle home. New hosts are short for the same reason. See ADR-027 for the composition model.
Six principles shape every decision; full statements with reasoning at docs/philosophy.md:
- Tight from the start — "good enough for now" becomes permanent for everyone except the current author.
- Declarative > imperative — state what should be true; nix figures out how.
- Explicit > implicit — intent visible in the repo, not in the contributor's head.
- Whitelist > blanket — new things slip through blanket allows; whitelists force a deliberate choice.
- Single source of truth — two records that disagree are the most painful failure mode.
- No premature abstraction — wrappers and flags earn their place by concrete need, not speculation.
These produce the technical stances pinned in CLAUDE.md §"Deliberate stances" (users.mutableUsers = false, key-only SSH, the allowUnfreePredicate whitelist) and the process stances in docs/workflow.md (intent-first issues, doc-before-code for selections, peer-review staged diffs, dependencies via linked issues, squash auto-merge).
For a contributor reading the repo cold:
CLAUDE.md— top-level state, deliberate stances, the operational current.docs/nix-config-prd.md— the design spec for the multi-host configuration.docs/philosophy.md— the technical principles that predict subsequent choices.docs/workflow.md— how work moves through the repo.docs/decisions/— Architecture Decision Records for any tool or design choice worth knowing about.
docs/README.md is the canonical index into the reference materials.
CI builds every host on every PR via nix flake check (see .github/workflows/ci.yaml). Lockfile bumps come via a weekly automated PR (Mondays 04:00 UTC) and merge manually after green CI. PRs land via squash auto-merge after required checks pass. Substantive decisions are recorded as ADRs before they affect main.
MIT — see LICENSE.