NixDaemon

NixOS pentest workstation as one flake — IceBreaker's successor
git clone https://git.daemon-sec.xyz/NixDaemon.git
Log | Files | Refs | README

README.md (26098B)


      1 # The author's laptop — NixDaemon's reference machine
      2 
      3 This is the manual for `modules/hosts/laptop/` (`nixosConfigurations.nixos`):
      4 one specific PCSpecialist Valeon II 17 with its fan fix, NVIDIA setup, private
      5 dotfiles checkout and sops secrets. **It will not build for anyone else** — it
      6 decrypts secrets with the author's age key and links a private dotfiles repo.
      7 To install NixDaemon on your own machine, use the generic host instead:
      8 [docs/install.md](../../../docs/install.md).
      9 
     10 Personal values (name, email, GPG key, location) are in one file,
     11 [`_identity.nix`](./_identity.nix). `$VAULT` below is the author's notes vault.
     12 
     13 One flake, one machine: the TongFang GM7RGxM (Ryzen 9 6900HX, Radeon 680M,
     14 RTX 3070 Ti, 2560×1440@240). Everything the machine is comes from here:
     15 the kernel modules that keep the dead GPU fan from throttling the CPU, the
     16 NVIDIA setup for Hyprland, the greeter, the compositor's Lua config and
     17 keybinds, Caelestia as bar/launcher/lock, kitty with tmux-style keys, zsh
     18 through the dotfiles checkout, the general tools, and the secrets. Built from
     19 the vault's `04Tools/NixDaemon-Migration/` material on 2026-10-07.
     20 
     21 The companion repo is `daemon-sec-dotfiles` (private, same account):
     22 home-manager links every dotfile from its checkout (`~/git/daemon-sec-dotfiles`)
     23 into `$HOME`, so editing the checkout edits the live config.
     24 
     25 ## Structure
     26 
     27 ```text
     28 NixDaemon/
     29 ├── flake.nix                  inputs: nixpkgs (unstable), flake-parts, import-tree, wrapper-modules, home-manager,
     30 │                              hyprland, caelestia-shell, caelestia-cli, llm-agents, sops-nix, nvf
     31 │                              outputs = flake-parts.lib.mkFlake { inherit inputs; } (import-tree ./modules)
     32 ├── .sops.yaml, secrets/       sops-nix: the age recipient and the encrypted secrets file
     33 ├── scripts/wrap.sh            turns a plain module file into a flake-parts module (how the tree below was made)
     34 └── modules/                   every *.nix here is a flake-parts module; paths containing /_ are skipped
     35     ├── parts.nix              systems; the home-manager and wrapper-modules flake modules
     36     ├── hosts/laptop/          the machine — flake.nixosModules.laptop-* and the nixosConfiguration
     37     │   ├── default.nix        flake.nixosConfigurations.nixos = nixosSystem { modules = [ self.nixosModules.laptop ]; }
     38     │   ├── configuration.nix  self.nixosModules.laptop: imports every module below by name; daemon.desktop.* switches;
     39     │   │                      boot, users (zsh login shell), greetd/tuigreet, audio, fonts, portals, nix-ld, LocalSend port
     40     │   ├── hardware.nix       laptop-hardware (nixos-generate-config output, unchanged)
     41     │   ├── fan-throttle-guard.nix  laptop-fan-throttle-guard: vault gpu-fan-fix/, unchanged; fanfix + stability_guard.py beside it
     42     │   ├── fan-extras.nix     laptop-fan-extras: the performance power profile
     43     │   ├── uniwill-laptop.nix laptop-uniwill: the `uniwill` hwmon the guard reads (uniwill-laptop/_package.nix, sources)
     44     │   ├── nvidia.nix         laptop-nvidia: open kernel module, panel on the dGPU, colon-free DRM names for Hyprland
     45     │   ├── ssd.nix            laptop-ssd: Samsung 980 crypttab + /mnt/ssd
     46     │   ├── nix-settings.nix   laptop-nix-settings: flakes, hyprland.cachix.org, nh + weekly clean, nvd, nom
     47     │   ├── toolbox.nix        laptop-toolbox: envfs, ~/.local/bin first on PATH, padx udev rule
     48     │   ├── sops.nix           laptop-sops: secrets/secrets.yaml → /run/secrets
     49     │   ├── fan-cli.nix        laptop-fan-cli: fan-ec, passwordless sudo for wheel
     50     │   └── fan-reference/     the Arch-era captures and their README
     51     ├── features/              shared NixOS features, by name
     52     │   ├── workstation.nix    workstation: Claude Code, Claude desktop, Obsidian, gh, glab
     53     │   ├── home-manager.nix   home-manager: HM as a NixOS module, users.daemonsec = self.homeModules.daemonsec
     54     │   └── desktop/
     55     │       ├── options.nix    desktop-options: daemon.desktop.hyprland.enable / daemon.desktop.niri.enable (both default true)
     56     │       ├── hyprland.nix   desktop-hyprland: programs.hyprland (uwsm), the GTK portal — gated
     57     │       ├── niri.nix       packages.niri (wrapper-modules: config.kdl from Nix, binds, Rosé Pine) + desktop-niri — gated
     58     │       └── noctalia.nix   packages.noctalia (wrapper-modules: settings.json from Nix, Rosé Pine "Rosepine" scheme)
     59     │   └── pentest/           the offensive toolkit — one NixOS module per category, all toggleable
     60     │       ├── default.nix    nixosModules.pentest: imports every category below by name
     61     │       ├── options.nix    daemon.pentest.enable + the options no category owns
     62     │       ├── _sets.nix      mkCategory: one package list -> gated module + devShell + smoke check
     63     │       ├── _aliases.nix   suffix-free script aliases, collision-guarded (impacket's net/split/ping)
     64     │       ├── _impacket.nix  impacket + its aliases, shared by python.nix and ad.nix
     65     │       ├── _overlay.nix   the nixpkgs fixes the toolkit needs (python 3.12 anyio), each dated
     66     │       ├── _pkgs/         pinned downloads: default.nix (SharpCollection, PEASS, mimikatz, scripts) + assets.nix (the release table: potatoes, nanodump, SharpHound CE, kerbrute, static nmap/socat…)
     67     │       ├── nixpkgs.nix    one package set for the system and for the flake's own checks
     68     │       ├── core.nix       recon.nix  ad.nix  web.nix  pivot.nix  crack.nix  shells.nix
     69     │       ├── wordlists.nix  python.nix  bloodhound.nix  gui.nix  payloads.nix  paths.nix
     70     │       ├── dfir.nix       reversing.nix  cloud.nix  mobile.nix              (off by default)
     71     │       ├── wireless.nix  radio.nix  hardware.nix                          (off; needs hardware)
     72     │       ├── c2.nix        database.nix  osint.nix  social.nix              (off by default)
     73     │       ├── vpn.nix        htbvpn: the HTB tunnel as a systemd template unit
     74     │       ├── time.nix       htb-time: conflict-aware clock skew for Kerberos
     75     │       ├── htb.nix        htbtarget / htbtime: the box you are on, shared across terminals
     76     │       ├── boxes.nix     htbbox: per-box tree + box.json + writeup.md (_htbbox.py)
     77     │       ├── paths.nix     ~/pentesting + the $privesc/$potatoes/$ligolo… vars + htbpaths
     78     │       ├── casts.nix     htbcast: asciinema recordings as raw write-up material
     79     │       ├── devshells.nix  nix develop #pentest, and the all-category collision check
     80     │       └── update.nix     pentest-update: move the _pkgs pins forward, deliberately
     81     └── home/                  flake.homeModules.* — the user's home
     82         ├── default.nix        homeModules.daemonsec: imports every module below by name
     83         ├── hyprland.nix       Lua config wiring, helper scripts, polkit, cliphist — only with daemon.desktop.hyprland
     84         ├── caelestia.nix      programs.caelestia: shell.json, the CLI with both Rosé Pine schemes — same gate
     85         ├── terminal.nix       kitty: DMMono Nerd Font, Rosé Pine dark, tmux-style ctrl+a keys, Neovim copy mode
     86         ├── shell.nix          zsh wiring (ZDOTDIR → dotfiles, fzf bindings, completions), tmux plugins, secretspec
     87         ├── dotfiles.nix       every dotfile from ~/git/daemon-sec-dotfiles as an out-of-store symlink
     88         ├── tools.nix          toolbox runtime closure, python env, the general CLI tools, `ns` (package search)
     89         ├── cheats.nix         the cheat cards: `nix-cheat` and `gpg-cheat` (cheats/*.md)
     90         ├── sops.nix           sops-nix for the user; sops, age, ssh-to-age
     91         ├── neovim.nix         nvf: Neovim with a small Nix-built plugin set, Rosé Pine
     92         ├── prompt.nix         starship and fastfetch
     93         ├── fan.nix            `fan`: status and watch without root; max/auto with the clamp watchdog
     94         ├── htb-shell.nix      $TARGET/$BOX in every terminal (zsh precmd) and in the prompt
     95         ├── ssh.nix, gpg.nix, git.nix   keys from sops, ~/.ssh/config, gpg.conf, git identity and signing
     96         ├── media.nix          mpd, rmpc, mpv
     97         ├── yazi.nix, gtk.nix  yazi with previews and Rosé Pine; Yaru-purple icons, cursor, prefer-dark
     98         ├── hypr/*.lua         omarchy (shim) · core · looknfeel · defaults · bindings · lid · caelestia
     99         ├── kitty/, caelestia/, cheats/, gpg/, rmpc/, mpv/, yazi/   the data those modules read
    100 ```
    101 
    102 ## What runs
    103 
    104 | Layer | Choice | Where |
    105 |---|---|---|
    106 | Boot, login | systemd-boot · greetd + tuigreet (remembers user and session) | modules/hosts/laptop/configuration.nix |
    107 | Compositor | Hyprland 0.56 under uwsm, configured in Lua; springy overshoot animations, square corners, blur | modules/home/hypr/, modules/home/hyprland.nix |
    108 | Shell UI | Caelestia Shell: bar, launcher, dashboard, sidebar, lock, OSD, notifications. Rosé Pine dark, framed bar, 340 px panels | modules/home/caelestia.nix, modules/home/caelestia/ |
    109 | Second desktop | Niri + Noctalia Shell (Rosé Pine "Rosepine"), both as wrapped packages: `nix run ~/NixDaemon#niri` / `#noctalia`. Pick the session in tuigreet; `daemon.desktop.{hyprland,niri}.enable` in configuration.nix drop one | modules/features/desktop/ |
    110 | Terminal | kitty, DMMono Nerd Font, Rosé Pine dark, `ctrl+a` tmux-style prefix, copy mode in Neovim | modules/home/terminal.nix |
    111 | Shell | zsh as login shell; config = the dotfiles' `~/.dotfiles` tree (autosuggestions, syntax highlighting, fzf-tab, zoxide, atuin). Prompt and fetch are Nix-managed | modules/home/shell.nix, prompt.nix |
    112 | Dotfiles | symlinked from `~/git/daemon-sec-dotfiles/home` | modules/home/dotfiles.nix |
    113 | Editor | Neovim via nvf: treesitter, LSP, blink completion, telescope, oil, gitsigns, which-key, Rosé Pine | modules/home/neovim.nix |
    114 | Tools | uv, nodejs, btop, yazi, lazygit, tmux, ripgrep/fd/fzf/bat/eza, delta, tldr, LocalSend, … | modules/home/tools.nix |
    115 | Secrets | sops-nix (age) for the repo; secretspec (keyring) for projects | modules/hosts/laptop/sops.nix, modules/home/sops.nix, shell.nix |
    116 | GPU | NVIDIA open module, panel on the RTX; Aquamarine told the GPUs by colon-free udev symlinks | modules/hosts/laptop/nvidia.nix |
    117 | Fan | `fan-throttle-guard`: 3.2 GHz floor, EC auto fan, `uniwill` hwmon from a 6.19 driver. `fan` command: status, watch, max/auto with a clamp watchdog | modules/hosts/laptop/fan-*.nix, uniwill-laptop/, modules/home/fan.nix |
    118 | Pentest | 23 toggleable categories (`daemon.pentest.<category>.enable`, every one listed in `configuration.nix`): recon, AD, web, pivoting, cracking, shells, wordlists, payloads, BloodHound, GUI on; DFIR, reversing, wireless, radio (SDR/BT/RFID), hardware (JTAG/flash/CAN), C2, database, cloud, OSINT, social, mobile off. Tools go to `environment.systemPackages`, so `sudo nmap -sS` works. Every category carries a smoke check: `nix flake check` | modules/features/pentest/ |
    119 | HTB workflow | `htbvpn up/down/list` (systemd template unit), `htbtarget` (shared across terminals via zsh precmd; writes `/etc/hosts` for Kerberos), `htbtime` (clock skew, restores exactly what it changed), `htbbox new <name> <ip> <os> <difficulty> [hosts]` (positional any-order, or flags, or gum prompts; scaffolds recon/enum/creds/loot/exploit/serve/casts + `box.json` + `writeup.md`; also `htbbox use/set/host/cred/flag/note/ports/json`), `htbcast` (asciinema v3 session recording → `txt` transcript for an agent, `gif` preview via agg), `payload-serve` (binds the tunnel only, refuses when the VPN is down), `pentest-cheat` | modules/features/pentest/{vpn,time,htb,boxes}.nix |
    120 | Arsenal | `~/pentesting/` with permanent `$privesc $potatoes $creds $ad $ligolo $fscan $nmapbin $shells …` vars and `htbpaths` to navigate. ligolo-ng/chisel/fscan/pspy cross-compiled from source for every OS/arch; mimikatz (2 builds), the full potato family, nanodump, SharpHound CE, SharpCollection, static nmap/socat, kerbrute, PEASS — pinned by hash | modules/features/pentest/{payloads,paths}.nix, _pkgs/ |
    121 | Nix | flakes, Hyprland cache, `nh` with weekly `clean all --keep 5 --keep-since 14d` | modules/hosts/laptop/nix-settings.nix |
    122 
    123 ## Setting it up
    124 
    125 ### Day to day (this machine)
    126 
    127 ```sh
    128 nh os switch                 # build, nvd diff, sudo, activate; = sudo nixos-rebuild switch --flake ~/NixDaemon#nixos
    129 nh os boot                   # same, but activate on next boot (kernel / driver changes)
    130 nix-cheat                    # the full card: rebuild, remote, nh, home, search, update, rollback, clean, …
    131 nix-cheat nh                 # one section
    132 ns kitty                     # fuzzy search nixpkgs + NixOS/home-manager options, with descriptions
    133 pentest-cheat                # the offensive toolkit card: htb, recon, ad, pivot, transfer, crack, web, dfir
    134 pentest-cheat ad             # one section
    135 nix flake check              # every pentest category's smoke check, plus the VM tests
    136 nix develop ~/NixDaemon#pentest   # the whole toolkit without installing it
    137 ```
    138 
    139 home-manager is a NixOS module here, so one rebuild does both; there is no
    140 separate `home-manager switch`. New files must be `git add`ed before nix sees
    141 them (the repo is jj, colocated with git; `nix-cheat repo`).
    142 
    143 ### From the repo, without a checkout
    144 
    145 The repo is private, so the reference is the ssh form:
    146 
    147 ```sh
    148 REPO='git+ssh://git@gitlab.com/DAEMON-404/NixDaemon.git'
    149 sudo nixos-rebuild switch --flake "$REPO#nixos"
    150 nh os switch "$REPO"
    151 nix flake show "$REPO"
    152 ```
    153 
    154 ### Fresh install
    155 
    156 1. Boot the NixOS live ISO, partition and mount at `/mnt`; generate
    157    `hardware-configuration.nix` with `nixos-generate-config --root /mnt` and
    158    compare it with `modules/hosts/laptop/hardware.nix` (UUIDs).
    159 2. `git clone git@gitlab.com:DAEMON-404/NixDaemon.git && cd NixDaemon`, paste
    160    the generated file's body into `modules/hosts/laptop/hardware.nix` (inside
    161    the `flake.nixosModules.laptop-hardware =` wrapper). Do not drop a raw
    162    `hardware-configuration.nix` into `modules/`: import-tree would load it as a
    163    flake-parts module and evaluation would fail.
    164 3. `sudo nixos-install --flake .#nixos`, reboot, log in at tuigreet, pick
    165    **Hyprland (UWSM)** once.
    166 4. Clone the dotfiles to `~/git/daemon-sec-dotfiles` (the links in
    167    `modules/home/dotfiles.nix` point there) and the toolbox to `~/.local/bin`.
    168 5. Restore the age key to `~/.config/sops/age/keys.txt` and copy it for the
    169    system (see Secrets), then the Samsung SSD key (below).
    170 
    171 ### The staged migration this repo was built for (2026-10-07, done)
    172 
    173 History, kept as a record. Stage A (`#bootstrap`, built from `nixpkgs-stable`)
    174 no longer exists: the dendritic rewrite of 2026-10-08 removed it, so none of
    175 the `#bootstrap` commands below work any more.
    176 
    177 **Stage A: fan fix now, on the GNOME install.** Small switch (fan module,
    178 driver, toolbox prerequisites, Hyprland cache). The new kernel modules only
    179 load from the booted system, hence the reboot.
    180 
    181 ```sh
    182 sudo nixos-rebuild switch --flake ~/NixDaemon#bootstrap && sudo reboot   # or: nh os switch -H bootstrap
    183 ```
    184 
    185 First-boot check (vault README and gpu-fan-fix/README.md):
    186 
    187 ```sh
    188 fanfix status            # cap 3200 MHz · boost 1 · profile performance · fan line present
    189 sudo fanfix fan status   # fan-abnormal=1 is expected; universal-fan-ctrl / custom-tables show the live EC path
    190 fanfix test 30           # all-core stress: expect 0 throttle events, peak < 75 °C
    191 systemctl status motherboard-stability fanfix-fan fanfix-performance-profile
    192 cat /run/motherboard-stability/status.json   # limit_mhz 3200, thermal_stage 0, board_gpu_c and main_fan_rpm present
    193 ```
    194 
    195 `fanfix status` will say "cap is not persisted": on NixOS the floor is the
    196 `systemd.tmpfiles.rules` line in the module, not `/etc/tmpfiles.d/99-cpu-freq-cap.conf`.
    197 Treat `fanfix install` / `uninstall` / `fan setup` as no-ops here; change
    198 `capKhz` in the module instead (gpu-fan-fix README). `fanfix-fan` is expected
    199 inactive: its manual fan mode makes the EC clamp all cores to 399 MHz under
    200 load; EC auto fan with the 3.2 GHz floor passed `fanfix test 30` at 61 °C.
    201 
    202 **Stage B: the desktop.**
    203 
    204 ```sh
    205 sudo nixos-rebuild boot --flake ~/NixDaemon#nixos && sudo reboot          # or: nh os boot
    206 ```
    207 
    208 tuigreet appears on tty1; pick `Hyprland (UWSM)` once, it is remembered.
    209 Then the keybind diff against the vault capture:
    210 
    211 ```sh
    212 hyprctl binds -j | python3 -I -c 'import json,sys
    213 M={1:"SHIFT",4:"CTRL",8:"ALT",64:"SUPER"}
    214 for x in json.load(sys.stdin):
    215     print(x.get("submap",""),"|","+".join(n for v,n in sorted(M.items()) if x["modmask"]&v),"|",x["key"],"|",x.get("description",""))' | sort > /tmp/binds.new
    216 cut -d'|' -f1-4 $VAULT/04Tools/NixDaemon-Migration/shortcuts/keybinds.txt | sort | diff - /tmp/binds.new
    217 ```
    218 
    219 Expected differences: the keycode binds (workspaces, resize, bar panels,
    220 group windows) show `code:0` in the capture and an empty key here; the keys
    221 caelestia.lua takes over carry their Caelestia descriptions; the stock
    222 Obsidian and YouTube lines are gone because vault-open and bakx own those
    223 keys; the two webcam-overlay binds were not carried (keycodes unknown).
    224 
    225 `hosts/bootstrap/` and the `nixpkgs-stable` input were deleted on 2026-10-08.
    226 
    227 **Samsung SSD key** (vault samsung-ssd.md, section 2; needs the gpg passphrase):
    228 
    229 ```sh
    230 cd $VAULT/04Tools/NixDaemon-Migration
    231 sudo mkdir -p -m 700 /etc/secrets
    232 gpg -d --pinentry-mode loopback ssd.key.gpg | sudo tee /etc/secrets/ssd.key >/dev/null
    233 sudo chmod 400 /etc/secrets/ssd.key
    234 sudo systemctl restart systemd-cryptsetup@ssd.service mnt-ssd.mount   # or just reboot
    235 ```
    236 
    237 Until then the drive stays locked; both units are `nofail`, so boot is
    238 unaffected. The sops way: `sops set secrets/secrets.yaml '["ssd.key"]' "\"$(gpg -d ssd.key.gpg)\""`,
    239 then `sops.secrets."ssd.key".path = "/etc/secrets/ssd.key";` in modules/hosts/laptop/sops.nix.
    240 
    241 ## The shell
    242 
    243 zsh is the login shell (modules/hosts/laptop/configuration.nix) and its configuration is
    244 the dotfiles checkout's `home/.dotfiles` tree, reached through a Nix-managed
    245 `~/.zshenv` that sets `ZDOTDIR=~/.dotfiles` (modules/home/shell.nix). The
    246 plugins (autosuggestions, syntax highlighting, zsh-completions, fzf-tab,
    247 history-substring-search, you-should-use) are the copies vendored in that
    248 tree; starship, zoxide, atuin and fzf come from nixpkgs. Edit
    249 `~/git/daemon-sec-dotfiles/home/.dotfiles/config/*.zsh` and open a new shell.
    250 
    251 Things the shell modules want that this build provides: `~/.fzf.zsh` (fzf's
    252 key bindings from the store, core.zsh only knows the Arch paths), the tool
    253 configs linked into `~/.config` by dotfiles.nix (bat theme, atuin, crossfetch
    254 for the splash animation, cheats, eza, btop, yazi, lazygit, carapace, and the
    255 rest of the checkout's .config; the starship prompt and the fastfetch card are
    256 Nix-managed in modules/home/prompt.nix), `~/.tmux.conf` with
    257 its plugins from nixpkgs behind a TPM stand-in, and the binaries modern.zsh
    258 aliases (btop, dust, duf, procs, delta, tldr, hyperfine, glow, onefetch,
    259 lazygit, yazi, neovim). Not carried: `mise` (its downloaded runtimes
    260 duplicate nixpkgs; `nix-ld` is on so `uv`'s managed Pythons work), and the
    261 repo's git config (it turns on commit signing with a key that is not on this
    262 machine; `~/.gitconfig` stays).
    263 
    264 ## The dotfiles
    265 
    266 `modules/home/dotfiles.nix` mirrors `~/git/daemon-sec-dotfiles/home/` into
    267 `$HOME` with out-of-store symlinks: editing the checkout edits the live
    268 config, and a rebuild is only needed to add or remove a path in the list.
    269 The header of that file names what is deliberately not linked (hypr and
    270 kitty are Nix-managed, the omarchy trees, the systemd units, mimeapps,
    271 git's signing config, the bash rc files, `.claude`/`.codex`, the toolbox
    272 `bin` directories) and why.
    273 
    274 ## The toolbox
    275 
    276 `~/.local/bin` is the vault's `bin/` copied flat and `git init`ed (remote
    277 `gitlab` → `DAEMON-404/daemon-bin`, not pushed). NixOS puts it first on PATH
    278 and envfs resolves the `#!/bin/bash` and `#!/usr/bin/python3` shebangs.
    279 `install.sh` there reports the environment. Notes:
    280 
    281 - `dropterm`, `winsnap`, `vault-open`, `lid-control` call
    282   `hyprctl dispatch 'hl.dsp…'`: that is Hyprland 0.56's Lua dispatch syntax,
    283   so they work unchanged.
    284 - `winsnap` looks for an `omarchy-bar` layer to avoid the bar; Caelestia's
    285   layers are `caelestia-*`, so snaps ignore the bar's reserved edge for now.
    286 - `lid-control` still calls a few `omarchy-*` helpers (tolerated: they fail
    287   quietly); `clipboard-backup` and `omarchy-menu-tmux-keybindings` are bound
    288   but not in the carried `bin/`.
    289 - The cheat popups (`omarchy-menu-kitty`, …) pipe into `omarchy-menu-select`,
    290   provided here as a fuzzel wrapper (modules/home/hyprland.nix). `nix-cheat`
    291   and `gpg-cheat` are this repo's own cards, in the same style.
    292 
    293 ## Secrets
    294 
    295 Two tools. **sops-nix** keeps secrets *in this repo*, encrypted with age
    296 (`.sops.yaml` names the recipient, `secrets/secrets.yaml` holds the values)
    297 and decrypts them at activation: `/run/secrets/NAME` for the system
    298 (modules/hosts/laptop/sops.nix), `~/.config/sops-nix/secrets/NAME` for the user
    299 (modules/home/sops.nix). The age key is `~/.config/sops/age/keys.txt`,
    300 created on 2026-10-08 and **not in the repo**; back it up (vault) and give
    301 the system its copy once:
    302 
    303 ```sh
    304 sudo install -D -m 600 -o root -g root ~/.config/sops/age/keys.txt /var/lib/sops-nix/key.txt
    305 ```
    306 
    307 Edit with `sops secrets/secrets.yaml`, declare with `sops.secrets.NAME = { };`,
    308 rebuild. `nix-cheat secrets` has the commands.
    309 
    310 What the file holds today: `ssh_id_ed25519` (the SSH private key, used by
    311 modules/home/ssh.nix), `gpg_main_secret` (the armored GPG secret key, still
    312 under its own passphrase, imported by modules/home/gpg.nix on first
    313 activation), and `example`. So a fresh install needs exactly one secret
    314 restored by hand, the age key; ssh, gpg and git then come up from the flake.
    315 
    316 **secretspec** is for a project's runtime secrets: declared next to the
    317 project in `secretspec.toml`, values in the system keyring
    318 (`~/.config/secretspec/config.toml`: provider `keyring`, profile `default`;
    319 gnome-keyring is unlocked at login by PAM). `secretspec init`, `secretspec
    320 add NAME`, `secretspec check`, `secretspec run -- cmd`.
    321 
    322 ## Look and keys
    323 
    324 - **Caelestia in Rosé Pine dark** (main), translucent over blur, with its
    325   framed bar: a 10 px border with 25 px rounded inner corners, clock and
    326   tray in pills, filled occupied workspaces, the Nix snowflake as the logo.
    327   Sidebar, utilities and notification panels are narrower than stock
    328   (`modules/home/caelestia/shell-tokens.json`). A Dawn mapping is registered too:
    329   `caelestia scheme set -n rose-pine -f rose-pine-dawn`.
    330 - **Bar shows the program**, not the window title: a small patch to the
    331   shell's ActiveWindow component (`modules/home/caelestia/active-window-program-name.patch`,
    332   applied in caelestia.nix) makes compact mode use the desktop entry's name
    333   for the window class. The shell compiles locally because of it.
    334 - **More shell**: desktop clock on the wallpaper (bottom right), audio
    335   visualiser along the bottom while something plays, weather on the
    336   dashboard (location in `_identity.nix`), audio and microphone status icons, lock screen over
    337   the wallpaper, a toast on track change, vim keys in the launcher, and
    338   idle: lock after 15 min, screen off after 20 (audio or an inhibitor holds
    339   both off).
    340 - **Springy windows**: `modules/home/hypr/looknfeel.lua` carries the dotfiles'
    341   animation rice (overshoot curves on move/resize/open, shadows, blur
    342   tuning, drag snapping, swallow, 3-finger workspace swipe, 4-finger-up
    343   fullscreen). Loaded after core.lua.
    344 - **kitty, tmux-style** (`ctrl+a` prefix; table in modules/home/terminal.nix):
    345   `c` tab, `-`/`|` splits, `hjkl` panes, `z` zoom, `[` copy mode in Neovim
    346   (`v` select, `y` copy, Enter copy and leave, `q`), `]` paste, `1..9` tabs,
    347   `ctrl+a ctrl+a` sends a real ctrl+a to the shell.
    348 - `CTRL+SUPER+SPACE` opens the Caelestia launcher in its wallpaper grid
    349   (helper `wallpaper-picker`); `>wallpaper name` filters. The directory is
    350   `paths.wallpaperDir` in modules/home/caelestia.nix.
    351 - Keys to try first: SUPER+RETURN terminal, SUPER+SPACE launcher,
    352   SUPER+ESCAPE session menu, SUPER+K keybindings list, SUPER+M window mode,
    353   SUPER+` dropdown terminal, PRINT screenshot.
    354 
    355 ## Why `uniwill-laptop` is built here
    356 
    357 `stability_guard.py` reads the `uniwill` hwmon (board GPU temperature, main
    358 fan rpm) and falls back to a permanent 1.8 GHz ceiling without it. The driver
    359 was merged upstream in Linux 6.19; nixpkgs' 6.18 kernel predates it and the
    360 7.2 kernel config leaves its Kconfig submenu off. `modules/hosts/laptop/uniwill-laptop/`
    361 holds the v6.19 sources and builds them as an out-of-tree module against
    362 whatever kernel is selected (verified on 6.18.55). Revisit when the default
    363 NixOS kernel ships it.
    364 
    365 ## Parked for the owner
    366 
    367 - **ANSI green**: Rosé Pine puts pine (#31748f) in the green slot; the rule
    368   says pine is never ink. kitty uses foam (#9ccfd8) for color2/color10
    369   meanwhile; one variable in modules/home/terminal.nix.
    370 - **Display manager**: greetd + tuigreet chosen (text greeter, remembers
    371   user and session). sddm would be a one-file swap.
    372 - **GPU / MUX**: configured for what the firmware presents, the panel on the
    373   RTX 3070 Ti (discrete). The hybrid alternative is a commented block in
    374   nvidia.nix; it only applies after changing the MUX in the BIOS.
    375 - **Hostname** stays `nixos` (the installer's). The flake output is also `nixos`.
    376 - Stock Omarchy keys whose program is still not installed (spotify,
    377   1password, signal) show a notification saying so. Add packages to
    378   modules/home/tools.nix when wanted.
    379 
    380 ---
    381 
    382 <p align="center">☧ · <a href="https://rosepinetheme.com/">Rosé Pine</a> all the way down · built with Claude Code</p>