nixpkgs' transmission 4.1.4 profile grants rw on the daemon's config directory
but not k, while 4.1.4 takes a fcntl lock on
~/.config/transmission-daemon/lock, so the daemon exited at startup with
"Another process is already using". Include rwk for that one file only.
WA-044, drop once the nixpkgs profile carries the permission.
Assisted-by: Claude Code (claude-opus-5-5)
NixOS copies mode-carrying /etc entries such as authorized_keys.d during
stage-2 activation, which runs before systemd starts. preservation mounted the
persisted /etc/ssh from preservation.target afterwards and hid that copy, so a
key change only took effect on `switch`: revoking a key and rebooting silently
restored the old one.
Prepare the mount in initrd, before activation runs, and cover it with a VM
test that seeds a stale authorized_keys file, reboots, and asserts the declared
key replaced it. The same test fails on that assertion with inInitrd disabled.
Verified on all four hosts: /persistent/etc/ssh/authorized_keys.d/root is now
rewritten at boot instead of keeping the timestamp of the last switch.
Assisted-by: Claude Code (claude-opus-5-5)
The homelab hosts accept root SSH with long-lived file keys, run several
exposed services with almost no systemd sandboxing, and keep no audit trail of
privileged actions. Close those three gaps, weighing each measure against its
cost instead of adopting a policy wholesale.
SSH
- Turn off KbdInteractiveAuthentication: nixpkgs defaults it to true and PAM
then still accepts the Unix password despite PasswordAuthentication = false.
- authorizedKeysInHomedir = false: only keys declared in Nix take effect;
youko keeps Gitea's forced-command file through `Match User gitea`.
- Pin aquamarine's passphrase-free remote-builder keys to
`restrict,command="nix-daemon --stdio --store daemon"`, so builds run in the
system daemon instead of an audited root login session.
systemd sandboxing
- Add the mylib.systemdHardening preset and apply it to the exposed, weakly
sandboxed services: caddy 7.8 -> 1.5, vmalert 8.2 -> 1.4, rustfs 6.6 -> 1.4,
whisper-asr 7.7 -> 1.6. An eval test pins the key restrictions.
- Rejected: a global all-keys policy (<=0.2 score gains on services nixpkgs
already sandboxes) and per-service confinement (bind paths to maintain).
auditd on all four hosts, reusing nixpkgs' loader and unit
- Audit login sessions only: a first `-a never,task -F auid=unset` rule keeps
services out of the per-syscall audit path. A compromised service is
therefore unrecorded until it opens a session.
- Record root commands, credential and host-key access, writes to SSH, account,
PAM, Nix-trust and audit configuration, module loads and session denials.
- Rejected: global denial logging, service-UID watches and
--loginuid-immutable (the kernel already records loginuid changes).
Verified on all four hosts after deployment: zero failed units, k3s 6/6 Ready,
and the exposed services healthy.
Assisted-by: Claude Code (claude-opus-5-5)
Turn on adaptive sync at the output only when a game window opts in, keeping desktop compositing fixed-rate to avoid idle/low-load VRR flicker. The Steam/Proton game rule enables VRR alongside the fullscreen rule.
Assisted-by: DeepSeek Harness (deepseek-flash)
XWayland/Proton windows can commit before they set a title. Excluding empty titles made Phantom Blade Zero miss the fullscreen rule and stay composited. Keep excluding the launcher announcement popup by title.
VRR is intentionally not enabled here: on-demand VRR caused display corruption in this game and is tracked separately.
Assisted-by: DeepSeek Harness (deepseek-flash)
Clarify that minimality means the smallest best-practice change, not a local patch around an upstream defect.
Assisted-by: DeepSeek Harness (deepseek-flash)
The nvidia exporter binds the host's static LAN IPv4, but unlike the node and smartctl exporters it was not ordered after network-online.target. On boot it lost the race with networkd, hit the systemd start limit, and stayed failed until manually restarted.
Update the mysecrets lock to 7f93490, which renames secrets.nix to agenix-rules.nix, drops the deprecated ./ key prefixes for agenix 0.18, and re-encrypts the access-token secret. Update the docs and agenix skill to the new filename and path convention.
k8s-gitops#54 merged and reconciled, a full cold start of all six guests
brought the VIP back without manual action, and a probe from a pod shows
2379 denied while 2381 stays open. SECURITY.md records the fix and the
probe; the workaround row and its TODO entry are gone.
Assisted-by: DeepSeek Harness (gpt-6-astra)
Baseline sysctls, exporter binds and the #399/#400/#401 rules verified
live on 13 hosts; kiali self-escalation and the list-vs-get probe blind
spot recorded; WA-043 registers the k3s-test-1 Cilium/kube-vip bootstrap
cycle (WA-040 is the Root CA blacklist row); the TODO table gains the
cluster-recovery line. The same section records the manual recovery and
its fix in k8s-gitops, plus the 2026-10-24 full cold start that brought
the VIP back without manual action; WA-043 drops when #54 merges.
Assisted-by: DeepSeek Harness (gpt-6-astra)
The 2026-10-22 audit found the live socket on *:9835: #373 bound
node/smartctl in monitoring.nix, but the nvidia exporter enabled in
hardware-nvidia.nix kept the nixpkgs default, so the socket-level
defence-in-depth layer never existed for 9835.
Extend the security-exporters eval check to every enabled exporter's
listenAddress so a future exporter without a bind fails the test.
Assisted-by: DeepSeek Harness (gpt-6-astra)
The etcd client API listens on the masters' static LAN address, and the
pod-source deny lists omitted it. Add 2379 to both nft drop lines and
assert it in the security-k3s-pod-firewall eval test. 2381 (etcd
metrics, scraped by in-cluster monitoring) stays reachable by design.
Pairs with the matching Cilium egressDeny port in k8s-gitops.
Assisted-by: DeepSeek Harness (gpt-6-astra)
Nothing imported it and the riscv64 output was already commented out; riscv64 hosts live in ryan4yin/nixos-config-sbc.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-flash)
The backup unit publishes `restic_backup_last_success_timestamp_seconds` for the node_exporter textfile collector, and a rejected alert now fails the notify unit instead of exiting 0.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-flash)
On its next run the unit deletes every snapshot whose name stamp is older than 14 days, whatever
its mtime.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (claude-opus-5-5)
Colmena deployments now ship the same toplevel as nixosConfigurations: nodes gain the nixpkgs
registry, nixPath and dated version, and akane no longer gets the x86_64 dsh.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (claude-opus-5-5)
The DPMS wake failure is the monitor no-signal auto power-off (HPD cycling during idle), not the refresh rate: the 120 Hz pin did not fix it. Disabled in the OSD and recorded as WA-041; pin 144.000 explicitly so niri stops silently falling back.
Assisted-by: DeepSeek Harness (deepseek-flash)
The 4090 reports the panel 120 Hz mode as 119.880, not 120, so niri fell back to 144 Hz; that DSC link stays dark after a DPMS wake.
Assisted-by: DeepSeek Harness (deepseek-flash)
The guest configurations import modules/base/overlays.nix, whose overlays
require myNurPkgs, so every VM host failed to evaluate its microVM definitions.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-flash)
Hosts without Home Manager (aquamarine, the VM hosts, k3s guests) had no
interpreter. The system profile is separate from the per-user one, so desktops
keep their Home Manager copy in PATH.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-flash)
The lock bump moves the input from 47c813c to b8e30ec. The guide now says which Nushell the bundle builds against and that its pin warns on divergence, names direct script execution as the replacement for a refused shell handoff, and uses a Nushell stderr redirect in the verification snippet.
Assisted-by: DeepSeek Harness (deepseek-flash)
Loading the output preset replaces the GUI-managed Excluded Apps list, so the
exclusions are declared in the preset. Meeting apps apply their own gain
control, and local media playback is better left unprocessed.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-flash)
EasyEffects 8 keeps its KConfig settings database plus user presets, autoload
profiles and imported models under $HOME, which the tmpfs root drops on
reboot; bind-mount both directories from /persistent.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-flash)
CPU warns at 90 C, GPU at 95 C and other components at 75 C; per-core probes
are excluded so a full-core compile no longer raises 26 alerts.
Assisted-by: DeepSeek Harness (deepseek-flash)
The Explore link named a datasource that does not exist, and rules without an
alertgroup label rendered an empty value.
Assisted-by: DeepSeek Harness (deepseek-flash)
changes() produced one series per pg_settings_* metric with identical labels,
so VictoriaMetrics rejected the rule with "duplicate output timeseries" whenever
a setting changed.
Assisted-by: DeepSeek Harness (deepseek-flash)
CPUTIN and AUXTIN* read 101 C or saturate at -128/127 C with no sensor wired,
so the alert fired while the CPU package stayed near 53 C.
Assisted-by: DeepSeek Harness (deepseek-flash)
disko creates the swapfile only when it formats the pool, so the live 96G files
keep their size until they are recreated with btrfs filesystem mkswapfile; that
frees 64G per host.
Assisted-by: DeepSeek Harness 0.2.0-rc.2 (deepseek-flash)
Enabling the module runs `openrgb --server` at boot and installs its udev rules,
so the GUI and the CLI reach the MSI controller without root.
Assisted-by: DeepSeek Harness (deepseek-flash)
The module loads i2c-dev and grants /dev/i2c-* to the i2c group and locally
logged-in users, so ddcutil no longer needs root.
Assisted-by: DeepSeek Harness (deepseek-flash)
A deploy from an aarch64 machine built the x86_64 system locally and died on the first platform-specific derivation; colmena 0.5.0 can build on the target instead.
Assisted-by: DeepSeek Harness (deepseek-flash)
The second NVMe and its LUKS volume leave with the Z890 board, so the data-disk disko module is deleted; `ai` must activate this change before the disk is pulled, or its initrd blocks on the missing volume.
Assisted-by: DeepSeek Harness (deepseek-flash)
The Z890 board and its root SSD move here from `ai`, and the retired VM's name and 192.168.5.101 are reused.
Assisted-by: DeepSeek Harness (deepseek-flash)
Without it mihomo skips the TUN's v6 half when no NIC has an IPv6 address at startup, yet still installs its strict-route v6 blackhole, which locks the host out of IPv6.
Assisted-by: DeepSeek Harness (qwen3.8-flash-iq3s-text)
No host defines a *-nixos-tests output; the pod firewall drops 5900-5903
from pod sources; the mihomo core keeps geodata and cache in its
StateDirectory; WA-020 was used twice and the cited WA-028 had no row.
microvm.nix's sshSwitch step runs nix-store --load-db against the guest's
read-only virtiofs /nix/store and dies there, so the recipe installs the
runner on the physical host and restarts microvm@<guest> instead.
Clause-level extensions only, no new sections. Publishing gains modals in both directions: the old sentence read as forbidding pushes to the user's own private repositories while leaving the disclosure direction a preference. The diagnosis scenario moves to Smoke because a cross-cutting rule has no area for the Extended trigger to select, and the guide separates cheap checks from behavioral runs so a modal-only edit does not cost a scenario suite.
Restore the IPv4 pin removed by 1e6702db (feat(mihomo): answer AAAA and keep the tailnet off the TUN). Fake IPv6 SSH connections can authenticate and then hang.
LAN clients using DoH dial the IPs they resolved, and a node may not reach an IP a
foreign resolver handed out. The gateway recovers the name from the handshake; on the
desktop the fake-ip table already names every flow. Its binary is a static release
build, not a package: update it by hand and validate the live config with the new core.
The v6 fake pool is a lookup key, not the egress path, so it can be a public range: the
shipped ULA default reads as "local" to browsers and prompts for local-network access on
every site. The gvisor stack override goes with it -- mips is upstream's default, and
nftables rules out the kernel stack here.
`just mihomo-gen` runs generate.nu from the module and validates with the core
the service runs. ~/.config/mihomo keeps only sources.yaml and config.yaml.
fake-ip needs one pool per family, and the family a client picks does not pick the
egress family. Exclude the cluster ULA and the Tailscale ranges from the TUN; the ssh
pin and the WA-028 record cover the fake-AAAA stall.
The ISP retires delegated prefixes, so a guest that registers its WAN prefix
loses it and Cilium never refreshes the node address. Derive the identity from
the IPv4 octets inside fd05:5::/64 and trust that ULA in the guest firewall.