nix.md (17268B)
1 # nix — rebuilding this machine from ~/NixDaemon 2 3 NixOS **and** home-manager are one flake: `~/NixDaemon` (`NH_FLAKE` points at it). 4 home-manager runs as a NixOS module, so **one rebuild does both**; there is no separate `home-manager switch` here. 5 Terminal card: **`nix-cheat`** — sections `layout rebuild remote nh home search update rollback clean inspect add desktop dotfiles secrets repo shell`. 6 7 ## layout — what lives where 8 9 Dendritic: `flake.nix` is `flake-parts.lib.mkFlake … (import-tree ./modules)`; every `*.nix` under `modules/` is a 10 flake-parts module that declares what it owns, and modules name each other through `self` (never by path). 11 12 ```text 13 ~/NixDaemon/flake.nix inputs only · outputs = import-tree ./modules 14 modules/parts.nix systems, the home-manager + wrapper-modules flake modules 15 modules/hosts/laptop/default.nix flake.nixosConfigurations.nixos (modules = [ self.nixosModules.laptop ]) 16 modules/hosts/laptop/configuration.nix nixosModules.laptop: imports laptop-* + features by name; daemon.desktop.* switches 17 modules/hosts/laptop/*.nix nixosModules.laptop-<name>: hardware, nvidia, ssd, nix-settings (nh), sops, toolbox, fan-* 18 modules/features/workstation.nix nixosModules.workstation (Claude, Obsidian, gh, glab) 19 modules/features/home-manager.nix nixosModules.home-manager: HM as a NixOS module → self.homeModules.daemonsec 20 modules/features/desktop/options.nix daemon.desktop.hyprland.enable · daemon.desktop.niri.enable 21 modules/features/desktop/niri.nix packages.niri (wrapped) + nixosModules.desktop-niri 22 modules/features/desktop/noctalia.nix packages.noctalia (wrapped, Rosé Pine) 23 modules/home/default.nix homeModules.daemonsec: imports every homeModules.<name> 24 modules/home/tools.nix packages: the toolbox closure and the general CLI tools ← add packages here 25 modules/home/dotfiles.nix every dotfile from ~/git/daemon-sec-dotfiles, as symlinks ← add dotfile paths here 26 modules/home/hyprland.nix, hypr/*.lua Hyprland config + helpers (gated on daemon.desktop.hyprland) 27 modules/home/caelestia.nix the Caelestia shell, its CLI, wallpaper dir (same gate) 28 modules/hosts/laptop/sops.nix sops-nix (system) · modules/home/sops.nix (user) · secrets/secrets.yaml · .sops.yaml 29 ``` 30 31 ## rebuild — nixos-rebuild, the plain way 32 33 ```sh 34 cd ~/NixDaemon 35 sudo nixos-rebuild switch --flake .#nixos # build, activate now, make it the boot default 36 sudo nixos-rebuild boot --flake .#nixos # build, activate on next boot only (kernel/driver changes) 37 sudo nixos-rebuild test --flake .#nixos # activate now, NOT the boot default (try something out) 38 nixos-rebuild build --flake .#nixos # just build → ./result, no sudo, nothing changes 39 sudo nixos-rebuild switch --flake ~/NixDaemon#nixos # same, from any directory 40 ``` 41 42 - `#nixos` is the configuration name (= hostname) and the only one in the flake (the old `#bootstrap` stage is gone). 43 - **New files must be known to git** or the flake does not see them: `git add path` first (see *repo*). 44 - A rebuild activates home-manager too; its log: `journalctl --user -u home-manager-daemonsec -b` or `systemctl status home-manager-daemonsec`. 45 46 ## remote — build straight from the GitLab repo 47 48 The flake does not have to be checked out: any `nix` command takes a **flake reference** to the repo. 49 The repo is **private**, so use the ssh form (the key in ~/.ssh/config is registered on GitLab); `?ref=main` pins a branch, `?rev=<sha>` a commit. 50 51 ```sh 52 REPO='git+ssh://git@gitlab.com/DAEMON-404/NixDaemon.git' 53 sudo nixos-rebuild switch --flake "$REPO#nixos" # this machine, from the pushed main 54 sudo nixos-rebuild boot --flake "$REPO?ref=main#nixos" 55 nh os switch "$REPO" # nh takes the same reference 56 nix build "$REPO#nixosConfigurations.nixos.config.system.build.toplevel" --no-link --print-out-paths 57 nix flake show "$REPO" # what the repo exports 58 nix flake metadata "$REPO" # which commit nix resolved 59 # root (sudo) fetches with root's ssh: either run the fetch as you first (nix build …, cached), or give 60 # root the key via /root/.ssh/config, or use an https token in ~/.config/nix/netrc (machine gitlab.com …). 61 ``` 62 63 **Fresh machine, from the live ISO** (disk already partitioned and mounted at /mnt): 64 ```sh 65 sudo nixos-generate-config --root /mnt --show-hardware-config > hardware-configuration.nix # compare with modules/hosts/laptop/hardware.nix 66 git clone git@gitlab.com:DAEMON-404/NixDaemon.git ~/NixDaemon && cd ~/NixDaemon # ssh key first (see *secrets*) 67 # paste its body into modules/hosts/laptop/hardware.nix (inside the laptop-hardware wrapper; a raw 68 # hardware-configuration.nix under modules/ would be loaded by import-tree as a flake-parts module), then: 69 sudo nixos-install --flake .#nixos 70 ``` 71 Afterwards: clone `~/git/daemon-sec-dotfiles` (every dotfile links there), restore the age key to 72 `~/.config/sops/age/keys.txt` and `/var/lib/sops-nix/key.txt` (see *secrets*), and the toolbox to `~/.local/bin`. 73 74 A remote reference is fetched fresh each time (nix caches it briefly, `--refresh` forces it); the local 75 checkout at `~/NixDaemon` (`NH_FLAKE`) is the default for day-to-day work. 76 77 ## nh — the same, with a diff and a progress tree 78 79 `nh` wraps nixos-rebuild: builds with nix-output-monitor, shows an **nvd diff** of what changes, then asks for sudo. 80 81 ```sh 82 nh os switch # = sudo nixos-rebuild switch --flake ~/NixDaemon (NH_FLAKE), config picked by hostname 83 nh os boot # build + boot default, activate on reboot 84 nh os test # activate now, not the boot default 85 nh os build # build only, no activation, no sudo 86 nh os switch --dry # show what would happen, do nothing 87 nh os switch --ask # show the diff, then confirm before activating 88 nh os switch -H nixos # pick a configuration by name (-H = hostname/attr); this flake has one 89 nh os switch -u # `nix flake update` first, then switch (updates EVERY input — see *update*) 90 nh os info # list system generations 91 nh os rollback # go back one generation (see *rollback*) 92 nh os switch -- --show-trace # anything after -- goes to nix build (debug an eval error) 93 ``` 94 95 ## home — home-manager in this setup 96 97 - home-manager is **inside** the NixOS build (modules/features/home-manager.nix: user `daemonsec` → `self.homeModules.daemonsec`, i.e. modules/home/). 98 **`nh os switch` rebuilds it**; `nh home switch` / `home-manager switch` do not apply here (no standalone HM profile). 99 - Did my home change apply? `ls -l ~/.config/<thing>` (links point into /nix/store or the dotfiles checkout), 100 `systemctl --user status home-manager-daemonsec`, or `journalctl --user -u home-manager-daemonsec -b`. 101 - HM backs up a file it has to replace as `*.hm-bak` (`backupFileExtension` in modules/features/home-manager.nix). Delete the backup once happy. 102 - Only build the home part (fast check, no sudo): 103 `nix build ~/NixDaemon#nixosConfigurations.nixos.config.home-manager.users.daemonsec.home.activationPackage` 104 105 ## search — finding packages and options 106 107 ```sh 108 ns # fuzzy search everything indexed: nixpkgs packages, NixOS and home-manager options 109 ns kitty # start with a query 110 ``` 111 Inside `ns` (fzf): type to filter, ↑↓ move, the right pane shows the description, version, homepage and 112 the option's default. **Enter** prints the attribute name (paste it into `home.packages` in tools.nix), 113 **ctrl-y** copies it, **ctrl-o** opens the homepage, **ctrl-s** the nixpkgs source, **Esc** quits. 114 The index is built by `nix-search-tv` on first use (a few seconds) and refreshed by itself; by hand: 115 ```sh 116 nix-search-tv print | head # the raw list · nix-search-tv preview firefox # one entry 117 nh search firefox # search.nixos.org from the terminal (needs network) 118 nix search nixpkgs firefox # nix's own search (slow first run: builds an eval cache) 119 nix eval ~/NixDaemon#nixosConfigurations.nixos.pkgs.firefox.version # version THIS flake would install 120 nix shell nixpkgs#firefox # try it without installing · nix run nixpkgs#cowsay -- hi 121 ``` 122 123 ## update — moving the inputs 124 125 ```sh 126 cd ~/NixDaemon 127 nix flake update # all inputs → flake.lock (nixpkgs, home-manager, hyprland, caelestia, llm-agents) 128 nix flake update nixpkgs home-manager # only these inputs 129 nix flake update hyprland # Hyprland alone (uses hyprland.cachix.org, so usually no compile) 130 nix flake lock # (re)write the lock without updating 131 nix flake metadata # which revisions are locked right now 132 nh os boot # then build it; boot = safest for kernel/driver bumps 133 ``` 134 135 Hyprland and Caelestia pin each other (flake.nix comments): update `hyprland` and `caelestia-*` together if the shell breaks. 136 137 ## rollback — when a generation misbehaves 138 139 ```sh 140 nh os rollback # previous generation, now 141 sudo nixos-rebuild switch --rollback # the same, plain 142 nh os info # generation numbers 143 sudo nix-env --switch-generation 5 -p /nix/var/nix/profiles/system && sudo /nix/var/nix/profiles/system/bin/switch-to-configuration switch 144 ``` 145 146 - At power-on the **systemd-boot menu** lists every generation: pick an older one, it boots as it was. 147 - A broken Hyprland login: Ctrl+Alt+F2 for a console, then one of the above. 148 149 ## clean — store and generations 150 151 ```sh 152 nh clean all --keep 5 --keep-since 14d # what the weekly timer runs (nix-settings.nix): drop old generations, GC 153 nh clean all --dry # show what it would remove 154 sudo nix-collect-garbage -d # the blunt version: delete all old generations, then GC 155 nix store gc # GC only (nothing referenced by a generation is touched) 156 du -sh /nix/store # how big is it 157 ``` 158 159 ## inspect — see before you switch 160 161 ```sh 162 nh os build && nvd diff /run/current-system result # what a switch would change 163 nix build ~/NixDaemon#nixosConfigurations.nixos.config.system.build.toplevel --no-link --print-out-paths 164 nix eval ~/NixDaemon#nixosConfigurations.nixos.pkgs.uv.version # version a package would get 165 nix eval ~/NixDaemon#nixosConfigurations.nixos.config.programs.zsh.enable # read an option value 166 ns <query> # fuzzy search nixpkgs + NixOS/HM options, with descriptions (nix-search-tv) 167 nh search <package> # nixpkgs search (search.nixos.org) 168 nix search nixpkgs <package> # local search (first run builds an index) 169 nix shell nixpkgs#<package> # try a tool without installing it 170 nix run nixpkgs#<package> -- --help 171 nix flake check ~/NixDaemon # evaluate every output 172 nix flake show ~/NixDaemon 173 ``` 174 175 ## add — packages, options, dotfiles, modules 176 177 - **A package for the user**: `modules/home/tools.nix` → `home.packages` list → `nh os switch`. 178 - **A system package / service**: `modules/hosts/laptop/configuration.nix` (`environment.systemPackages`, `services.*`). 179 - **A dotfile from the checkout**: `modules/home/dotfiles.nix` → add its path to the list → `nh os switch`. 180 - **A new home module**: `modules/home/<name>.nix` declaring `flake.homeModules.<name> = { pkgs, ... }: { … };`, 181 then `<name>` in the imports list of `modules/home/default.nix`. A NixOS one: `flake.nixosModules.<name>` in 182 `modules/features/<name>.nix`, named in `configuration.nix`. `scripts/wrap.sh FILE ATTR` wraps a plain module file. 183 - **A Hyprland bind**: `modules/home/hypr/bindings.lua` (user) or `defaults.lua` (stock); a helper script goes in `hyprland.nix`. 184 - **A Niri bind**: `modules/features/desktop/niri.nix` → `settings.binds` (validated at build: a typo fails `nix build .#niri`). 185 - **A Caelestia setting**: `modules/home/caelestia.nix` → `programs.caelestia.settings`. 186 - **A Noctalia setting**: tweak it in the GUI, `dump-noctalia-shell`, paste the differing keys into `noctalia.nix` → `settings`. 187 - Then: `git add` anything new, `nh os switch`, and for Hyprland `hyprctl reload`. A running Niri keeps its old 188 store config until you log in again, or: `niri msg action load-config-file --path "$(nix eval --raw ~/NixDaemon#niri)/niri-config.kdl"`. 189 190 ## desktop — Hyprland + Caelestia or Niri + Noctalia 191 192 Both are installed; tuigreet lists both sessions and remembers the last one, so switching is log out → pick the other. 193 194 ```sh 195 nix run ~/NixDaemon#niri # try Niri nested in the current desktop (Alt is the modifier); Noctalia starts inside it 196 nix run ~/NixDaemon#noctalia # the bar alone 197 ``` 198 ```nix 199 # modules/hosts/laptop/configuration.nix — set one to false and `nh os switch` to drop it entirely 200 daemon.desktop = { hyprland.enable = true; niri.enable = true; }; 201 ``` 202 203 ## dotfiles — the checkout is the source of truth 204 205 - `~/git/daemon-sec-dotfiles/home/` is mirrored into `$HOME` by **symlinks** (dotfiles.nix): edit a file there and the live config changes at once — no rebuild. 206 - A rebuild is needed only to add/remove a *path* in dotfiles.nix. `ls -l ~/.config/starship.toml` shows where a link goes. 207 - zsh: `~/.dotfiles/config/*.zsh` → open a new shell (`exec zsh`). Plugins are vendored in `~/.dotfiles/config/plugins`. 208 - Not linked on purpose (hypr, kitty, git, mimeapps, bash rc, .claude …): the list and reasons are at the top of dotfiles.nix. 209 210 ## secrets — sops-nix and secretspec 211 212 Two tools, two jobs. **sops-nix**: secrets that belong in this repo, encrypted with **age**, decrypted at 213 activation to `/run/secrets/NAME` (system) or `~/.config/sops-nix/secrets/NAME` (user). 214 **secretspec**: runtime secrets for a *project*, declared in its `secretspec.toml`, stored in the keyring. 215 216 ```sh 217 # sops — the one key: ~/.config/sops/age/keys.txt (user) = /var/lib/sops-nix/key.txt (system, root copy) 218 sudo install -D -m 600 -o root -g root ~/.config/sops/age/keys.txt /var/lib/sops-nix/key.txt # once per machine 219 age-keygen -y ~/.config/sops/age/keys.txt # the public key (recipient) in .sops.yaml 220 sops secrets/secrets.yaml # edit: opens decrypted in $EDITOR, re-encrypts on save 221 sops set secrets/secrets.yaml '["wifi-psk"]' '"hunter2"' # add/replace one value without the editor 222 sops -d secrets/secrets.yaml # print decrypted 223 sops -d --extract '["example"]' secrets/secrets.yaml 224 sops updatekeys secrets/secrets.yaml # after editing the recipients in .sops.yaml 225 ``` 226 227 Then declare it and rebuild: 228 ```nix 229 # modules/hosts/laptop/sops.nix (system) # modules/home/sops.nix (user) 230 sops.secrets.wifi-psk = { }; sops.secrets.my-token = { }; 231 sops.secrets."ssd.key".path = "/etc/secrets/ssd.key"; # e.g. the LUKS key instead of restoring it by hand 232 ``` 233 `nh os switch` → `/run/secrets/wifi-psk` (root, 0400; `owner = "daemonsec";` to read it as the user). 234 Check: `sudo ls -l /run/secrets/`, `systemctl --user status sops-nix` (user side), `journalctl -b | grep sops`. 235 236 ```sh 237 # secretspec — in a project directory (config: ~/.config/secretspec/config.toml → keyring, profile default) 238 secretspec init # writes secretspec.toml (commit it; it holds names, never values) 239 secretspec add DATABASE_URL # declare a secret (flags: --required/--default/--description) 240 secretspec check # prompts for every missing value, stores it in the keyring 241 secretspec set NAME # (re)store one value 242 secretspec run -- ./server # run with the secrets in the environment 243 secretspec export # print them for another tool (shell `eval`) 244 secretspec claude configure # let Claude Code fetch its API credential through secretspec 245 ``` 246 247 ## repo — committing ~/NixDaemon 248 249 The repo is **jj, colocated with git** (`.jj` + `.git`). Nix flakes only see files git knows about, and jj keeps git's index at `@-`, so a *new* file is invisible to nix until it is added: 250 251 ```sh 252 cd ~/NixDaemon 253 git add modules/home/new.nix # make nix see a new file (modified tracked files are seen as-is) 254 nh os build # or switch 255 jj status # jj snapshots the working copy 256 jj describe -m "what changed" && jj new # seal it (the vault ritual) — or plain: git add -A && git commit 257 jjmsg -c # or let the message write itself from the diff (jjmsg: print · -d describe · -e edit · -c commit) 258 jj log # history 259 ``` 260 261 A dirty tree only prints `warning: Git tree '/home/daemonsec/NixDaemon' is dirty`; it still builds. 262 263 ## shell — after a switch 264 265 - New packages are on PATH in a **new** shell (`exec zsh`); the login shell itself changes at the next login. 266 - Hyprland: `hyprctl reload` re-reads `~/.config/hypr/*.lua`. Caelestia: `caelestia shell -r` restarts the shell. 267 - Everything in the Nix profile: `ls /etc/profiles/per-user/daemonsec/bin`.