NixDaemon

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

commit c4613af53be2c0c66ccd80b5d4efa06a4bd1306d
parent 9405cde151ec86950d60f3c81eef403556659d1d
Author: DAEMON-404 <zer0sec.xp@icloud.com>
Date:   Wed,  7 Oct 2026 22:32:50 +0100

Add fan-extras (performance profile oneshot) and README

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
AREADME.md | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mhosts/bootstrap/default.nix | 1+
Mhosts/laptop/default.nix | 1+
Ahosts/laptop/fan-extras.nix | 23+++++++++++++++++++++++
4 files changed, 169 insertions(+), 0 deletions(-)

diff --git a/README.md b/README.md @@ -0,0 +1,144 @@ +# NixDaemon + +NixOS + home-manager for the PCSpecialist Valeon II 17 (TongFang GM7RGxM): +Hyprland (Lua config, uwsm) with Caelestia Shell, the dead-GPU-fan workaround, +and the hand-written toolbox. Built from the vault's +`04Tools/NixDaemon-Migration/` material on 2026-10-07. + +## Layout + +``` +flake.nix inputs: nixpkgs (unstable), nixpkgs-stable (26.05, bootstrap only), + home-manager, hyprland, caelestia-shell, caelestia-cli, llm-agents +hosts/laptop/ the machine + default.nix boot, users, greetd/tuigreet, Hyprland (uwsm), audio, fonts, portals + fan-throttle-guard.nix vault gpu-fan-fix/, imported unchanged; fanfix + stability_guard.py beside it + fan-extras.nix the performance power profile (fanfix install did this by hand on Arch) + uniwill-laptop.nix the `uniwill` hwmon the guard reads, built from Linux 6.19 sources (see below) + nvidia.nix open kernel module, panel on the dGPU, device order for Hyprland + ssd.nix Samsung 980 crypttab + /mnt/ssd (key restored by hand, see below) + nix-settings.nix flakes, hyprland.cachix.org + toolbox.nix envfs (foreign shebangs), ~/.local/bin first on PATH, padx udev rule + fan-reference/ the Arch-era captures (fan-ctl, fan-state, units, confs) and their README +hosts/bootstrap/ stage A: today's GNOME install + fan fix + toolbox prerequisites +home/ home-manager for daemonsec + modules/hyprland.nix Lua config wiring, helper scripts, polkit agent, cliphist, udiskie + modules/caelestia.nix programs.caelestia, the carried shell.json, static Rosé Pine scheme + modules/terminal.nix kitty, DMMono Nerd Font, Rosé Pine colours + modules/tools.nix toolbox runtime closure, python env, dotfiles symlinks (fonts, cursors) + modules/gtk.nix Yaru-purple, cursor, prefer-dark + hypr/*.lua omarchy (shim) · core · defaults (stock Omarchy binds) · bindings · lid · caelestia + caelestia/ scheme.json, rose-pine-dark.txt (CLI scheme), shell-tokens.json +modules/workstation.nix Claude Code, Claude desktop, Obsidian, gh, glab (from the bootstrap script) +``` + +## Applying + +Everything below needs `sudo`, so it is left to the owner. + +**Stage A: fan fix now, on the GNOME install.** Small switch (fan module, +driver, toolbox prerequisites, Hyprland cache). The new kernel modules only +load from the booted system, hence the reboot. + +```sh +sudo nixos-rebuild switch --flake ~/NixDaemon#bootstrap && sudo reboot +``` + +First-boot check (vault README and gpu-fan-fix/README.md): + +```sh +fanfix status # cap 3200 MHz · boost 1 · profile performance · fan line present +sudo fanfix fan status # fan-abnormal=1 is expected; universal-fan-ctrl / custom-tables show the live EC path +fanfix test 30 # all-core stress: expect 0 throttle events, peak < 75 °C +systemctl status motherboard-stability fanfix-fan fanfix-performance-profile +cat /run/motherboard-stability/status.json # limit_mhz 3200, thermal_stage 0, board_gpu_c and main_fan_rpm present +``` + +`fanfix status` will say "cap is not persisted": on NixOS the floor is the +`systemd.tmpfiles.rules` line in the module, not `/etc/tmpfiles.d/99-cpu-freq-cap.conf`. +Treat `fanfix install` / `uninstall` / `fan setup` as no-ops here; change +`capKhz` in the module instead (gpu-fan-fix README). + +**Stage B: the desktop.** Build, then boot into it (everything heavy is already +in the store if the build below finished; otherwise this downloads Hyprland +from its cache and compiles the NVIDIA modules). + +```sh +sudo nixos-rebuild boot --flake ~/NixDaemon#nixos && sudo reboot +``` + +tuigreet appears on tty1; pick `Hyprland (uwsm)` once, it is remembered. +Then the second vault check, the keybind diff: + +```sh +hyprctl binds -j | python3 -I -c 'import json,sys +M={1:"SHIFT",4:"CTRL",8:"ALT",64:"SUPER"} +for x in json.load(sys.stdin): + 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 +cut -d'|' -f1-4 ~/git/NetrunnerVault/04Tools/NixDaemon-Migration/shortcuts/keybinds.txt | sort | diff - /tmp/binds.new +``` + +Expected differences: the keycode binds (workspaces, resize, bar panels, +group windows) show `code:0` in the capture and an empty key here; the keys +caelestia.lua takes over carry their Caelestia descriptions (launcher, +session menu, panels, notifications, media keys); the stock Obsidian and +YouTube lines are gone because vault-open and bakx own those keys; the two +webcam-overlay binds were not carried (keycodes unknown). + +Afterwards delete `hosts/bootstrap/` and the `nixpkgs-stable` input. + +**Samsung SSD key** (vault samsung-ssd.md, section 2; needs the gpg passphrase): + +```sh +cd ~/git/NetrunnerVault/04Tools/NixDaemon-Migration +sudo mkdir -p -m 700 /etc/secrets +gpg -d --pinentry-mode loopback ssd.key.gpg | sudo tee /etc/secrets/ssd.key >/dev/null +sudo chmod 400 /etc/secrets/ssd.key +sudo systemctl restart systemd-cryptsetup@ssd.service mnt-ssd.mount # or just reboot +``` + +Until then the drive stays locked; both units are `nofail`, so boot is unaffected. + +## The toolbox + +`~/.local/bin` is the vault's `bin/` copied flat and `git init`ed (remote +`gitlab` → `DAEMON-404/daemon-bin`, not pushed). NixOS puts it first on PATH +and envfs resolves the `#!/bin/bash` and `#!/usr/bin/python3` shebangs. +`install.sh` there reports the environment. Notes: + +- `dropterm`, `winsnap`, `vault-open`, `lid-control` call + `hyprctl dispatch 'hl.dsp…'`: that is Hyprland 0.56's Lua dispatch syntax, + so they work unchanged. +- `winsnap` looks for an `omarchy-bar` layer to avoid the bar; Caelestia's + layers are `caelestia-*`, so snaps ignore the bar's reserved edge for now. +- `lid-control` still calls a few `omarchy-*` helpers (tolerated: they fail + quietly); `clipboard-backup` and `omarchy-menu-tmux-keybindings` are bound + but not in the carried `bin/`. +- The cheat popups (`omarchy-menu-kitty`, …) pipe into `omarchy-menu-select`, + provided here as a fuzzel wrapper (home/modules/hyprland.nix). + +## Why `uniwill-laptop` is built here + +`stability_guard.py` reads the `uniwill` hwmon (board GPU temperature, main +fan rpm) and falls back to a permanent 1.8 GHz ceiling without it. The driver +was merged upstream in Linux 6.19; nixpkgs' 6.18 kernel predates it and the +7.2 kernel config leaves its Kconfig submenu off. `hosts/laptop/uniwill-laptop/` +holds the v6.19 sources and builds them as an out-of-tree module against +whatever kernel is selected (verified on 6.18.55). Revisit when the default +NixOS kernel ships it. + +## Parked for the owner + +- **ANSI green**: Rosé Pine puts pine (#31748f) in the green slot; the rule + says pine is never ink. kitty uses foam (#9ccfd8) for color2/color10 + meanwhile; one variable in home/modules/terminal.nix. +- **Display manager**: greetd + tuigreet chosen (text greeter, remembers + user and session). sddm would be a one-file swap. +- **GPU / MUX**: configured for what the firmware presents, the panel on the + RTX 3070 Ti (discrete). The hybrid alternative is a commented block in + nvidia.nix; it only applies after changing the MUX in the BIOS. +- **Hostname** stays `nixos` (the installer's). The flake output is also `nixos`. +- Programs some stock Omarchy keys expect but which are not in the toolbox + closure (spotify, lazydocker, btop, tmux, yazi, neovim, 1password, + signal): those keys show a notification saying so. Add packages to + home/modules/tools.nix when wanted. diff --git a/hosts/bootstrap/default.nix b/hosts/bootstrap/default.nix @@ -17,6 +17,7 @@ imports = [ ../laptop/hardware-configuration.nix ../laptop/fan-throttle-guard.nix + ../laptop/fan-extras.nix ../laptop/uniwill-laptop.nix ../laptop/nix-settings.nix ../laptop/toolbox.nix diff --git a/hosts/laptop/default.nix b/hosts/laptop/default.nix @@ -8,6 +8,7 @@ imports = [ ./hardware-configuration.nix ./fan-throttle-guard.nix # dead-GPU-fan workaround (vault gpu-fan-fix/, imported unchanged) + ./fan-extras.nix # the one imperative step fanfix install did: the performance profile ./uniwill-laptop.nix # the `uniwill` hwmon the guard reads: kernel 6.19 driver built for this kernel ./nvidia.nix ./ssd.nix diff --git a/hosts/laptop/fan-extras.nix b/hosts/laptop/fan-extras.nix @@ -0,0 +1,23 @@ +# hosts/laptop/fan-extras.nix +# +# The part of the fan fix that `fanfix install` did imperatively on Arch and +# that fan-throttle-guard.nix (imported unchanged) does not carry: the +# "performance" power profile. power-profiles-daemon persists the choice, so +# this oneshot is idempotent; it runs after ppd is up and then re-applies the +# tmpfiles clock floor, because a profile switch rewrites per-policy boost and +# can lift scaling_max_freq (fanfix re-runs tmpfiles for the same reason; the +# stability guard would also catch it within a second). +{ pkgs, lib, ... }: +{ + systemd.services.fanfix-performance-profile = { + description = "fanfix: select the performance power profile and re-assert the clock floor"; + after = [ "power-profiles-daemon.service" "systemd-tmpfiles-setup.service" ]; + requires = [ "power-profiles-daemon.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.power-profiles-daemon}/bin/powerprofilesctl set performance"; + ExecStartPost = "${pkgs.systemd}/bin/systemd-tmpfiles --create --prefix=/sys/devices/system/cpu"; + }; + }; +}