From a1678f084e757c5464d167a937ac06460d9529df Mon Sep 17 00:00:00 2001 From: Tom van der Lee Date: Thu, 24 Sep 2026 09:43:13 +0000 Subject: Added CLAUDE.md --- CLAUDE.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8223b54 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,43 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +A single Nix flake that provisions both NixOS hosts and non-NixOS systems (any Linux, macOS via nix-darwin later) from one shared set of home-manager modules. Two users of the shared config exist: real NixOS machines (`nixosConfigurations`) and standalone home-manager-only machines (`homeConfigurations`). + +## Commands + +- `nix fmt` — format all `.nix` files with `nixfmt-rfc-style`. +- `nix develop` — shell with `statix` and `deadnix` (linters) available. +- `nix flake check` — validates `homeConfigurations."tom@linux"`, `apps.bootstrap-host`, and other flake outputs. There are no real `nixosConfigurations` checked here unless a host has been scaffolded under `hosts/`. +- `nix eval '.#homeConfigurations."tom@linux".config.home.packages'` (or `"tom@darwin"`) — inspect the resolved package set for a given platform without building it. +- `nix build '.#homeConfigurations."tom@linux".activationPackage'` — build a standalone home-manager activation. +- `nix build '.#nixosConfigurations..config.system.build.toplevel'` — build a full NixOS system closure for an existing host directory. +- `nix run .#bootstrap-host -- --root --state-version ` — scaffold `hosts//` from `hosts/template/` (see `scripts/bootstrap-host.sh --help`). This is the only supported way to add a new NixOS host; never hand-edit `flake.nix` to add one. + +## Architecture + +**Host discovery is automatic.** Any directory under `hosts/` other than `hosts/template` that contains a `system` file becomes a real `nixosConfigurations.` output (see `flake.nix`). `hosts/template/` is a copy-source only, excluded by name, and is never built directly — `scripts/bootstrap-host.sh` copies it into a new `hosts//`. + +**`lib/mkNixos.nix` and `lib/mkHome.nix`** are the two entry points that turn a hostname/username into a flake output: +- `mkNixos` builds a `nixosSystem`: shared `modules/nixos/common.nix` + the host's own `hosts//configuration.nix` + the home-manager NixOS module (wired with `useGlobalPkgs`/`useUserPackages`, importing each user's `users/.nix`). +- `mkHome` builds a standalone `homeManagerConfiguration` from just `users/.nix` — no NixOS involved. +- Both apply the same `nixpkgs.config.allowUnfreePredicate`, sourced from `lib/unfree-allowlist.nix` (a flat list of package names — add unfree packages here, never flip `allowUnfree = true` globally). + +**`modules/home/` is the shared, cross-platform profile** — the same modules are consumed whether activated via a NixOS host's home-manager integration or a standalone `home-manager switch`. Because of this dual use, any module that needs to know whether it's running on a real NixOS/GNOME system (vs. a bare Linux or macOS home-manager-only box) must not assume Linux implies NixOS or GNOME: + - Platform-only differences (`pkgs.stdenv.hostPlatform.isLinux`/`isDarwin`) are fine and used throughout (see `modules/home/dotfiles.nix`'s `platformDefaultExcludes`, `modules/home/desktop-apps.nix`'s `linuxOnlyApps`). + - Package availability differences are handled via `lib.meta.availableOn pkgs.stdenv.hostPlatform`, filtered over a candidate list (`modules/home/desktop-apps.nix`) — this lets an unsupported platform's package silently drop out rather than fail evaluation. + - Anything that actually depends on NixOS-level config (e.g. whether GNOME is enabled) must read the `osConfig` special arg, which home-manager's NixOS integration sets to the full NixOS `config` — but which is absent when a module is evaluated standalone via `mkHome`. Modules that reference it need `osConfig ? null` in their argument list and should treat a missing/false value as "not applicable" rather than erroring. + +**`modules/nixos/common.nix`** holds everything applied unconditionally to every NixOS host: flakes enabled, the `tom` user (SSH keys pinned as a static literal, not fetched at build time), locale/timezone defaults (`mkDefault`, so a host can override), nix gc policy, sshd (key-only auth, firewall open only on 22), and the GNOME desktop stack (GDM, NetworkManager, PipeWire/rtkit, CUPS+Avahi). Per-host files under `hosts//configuration.nix` layer on top of this, not instead of it. + +**`users/.nix`** is the identity/profile file consumed by both `mkNixos` (as a home-manager user module) and `mkHome` (as the top-level module) — it's the one place that's the same regardless of which entry point is used. + +**Personal dotfiles integration** (`modules/home/dotfiles.nix`) is opt-in via `home.dotfiles.repoUrl`. When set, it clones a separate personal dotfiles git repo to `~/dotfiles` and GNU-Stow-symlinks every top-level, non-hidden directory into `$HOME`, skipping platform-mismatched packages (built-in defaults in `platformDefaultExcludes`, plus anything listed in `home.dotfiles.excludePackages`) and skipping (with a warning, not a failure) any package whose target path conflicts with an existing file. An existing `~/dotfiles` checkout is never re-cloned or modified. Because `~/dotfiles/git/.gitconfig` and `~/dotfiles/bash/.bashrc` are the sole source of git/bash config once stowed, this repo's own `programs.git`/`programs.bash` home-manager management has been deliberately removed. + +**Neovim** (`modules/home/editor.nix`) sets `programs.neovim.sideloadInitLua = true` specifically so home-manager never writes `~/.config/nvim/init.lua` — that path is owned exclusively by the dotfiles repo's `neovim` Stow package. Do not add `programs.neovim.extraConfig`/`plugins`/anything else that writes into `~/.config/nvim`; it will silently lose to the dotfiles automation's skip-on-conflict behavior and break plugin loading. + +## Planning changes with OpenSpec + +This repo uses OpenSpec (`openspec/`) for structured change proposals: `openspec/specs//spec.md` are the current-truth capability specs, `openspec/changes/` holds in-flight change proposals (proposal/specs-delta/design/tasks), and `openspec/changes/archive/` holds completed ones. Use the `openspec-propose` / `openspec-update-change` / `openspec-apply-change` / `openspec-archive-change` skills (or their `/opsx:*` equivalents) rather than hand-editing these — they keep proposal, delta spec, design, and tasks coherent and validate via `openspec validate`. Read `openspec/changes/archive/*/proposal.md` for examples of this project's expected proposal depth (verified license/platform facts, explicit non-goals, precise file-level impact). -- cgit v1.3