dsh-nu replaced every shell command with Nushell and refused bash/pwsh
syntax before it spawned; in practice the refusals were too noisy. Drop
the flake input, install plain dsh, and remove the docs describing the
Nushell-only shell.
Assisted-by: DeepSeek Harness (deepseek-flash)
58d1af8f dropped the cache while adding the pull-through caches. Without
it a NUR bump rebuilds its npm deps here, and dsh's npmDepsFetcherVersion
2 fixed output includes the packuments the Huawei npm mirror rewrites, so
the build fails NUR's hash check instead of reusing its CI-built output.
Assisted-by: DeepSeek Harness (deepseek-flash)
dsh 0.2.2-alpha.2 -> 0.2.2-rc.1 and computer-use-linux 0.7.36 ->
0.7.37; cua-driver stays 0.38.0. The nur-packages diff is version bumps
plus regenerated dsh lockfile only, with no module or overlay changes.
Assisted-by: DeepSeek Harness (deepseek-flash)
ICU takes the zone name from the /etc/localtime symlink target path. A bind
mount turns it into a plain file, which keeps the TZif data but loses the
path, so the only identifier left is the ambiguous "CST" abbreviation that
ICU reads from the file and maps to America/Chicago.
Export TZ from time.timeZone and pass TZ/TZDIR through the nixpak env
allowlist.
Assisted-by: DeepSeek Harness (deepseek-flash)
Preserve ~/.raft across reboots so Raft Computer keeps its enrollment, machine identity, agent state and app data. ~/.local/bin and ~/.config/gh were already persisted.
Assisted-by: DeepSeek Harness (deepseek-flash)
Update nur-ryan4yin to d82be0b. Audit: only the prebuilt packages cua-driver (0.36.0 -> 0.38.0) and computer-use-linux (0.7.20 -> 0.7.36) changed; the NUR modules and trust-related settings did not move. Only the kana and ruby system closures change.
Assisted-by: DeepSeek Harness (deepseek-flash)
Only Huawei works with prefetch-npm-deps: npmmirror 404s on its doubled
slash, and Tencent answers 567 to its concurrent fetches (WA-049).
Assisted-by: Claude Code (claude-opus-5-5)
Move standalone repository docs from the root into docs/ and add a documentation index that links the moved docs and the code-local runbooks.
Update Markdown links, source comments, and the root entry points to the new locations. Keep README.md and AGENTS.md at the root as required entry points.
Assisted-by: DeepSeek Harness (deepseek-flash)
At boot whisper-asr could start before the LAN address was configured and
fail with "could not bind on any address". Let whisper-asr.socket bind it
with FreeBind and have uvicorn take fd 3, like the exporters.
Assisted-by: Claude Code (claude-opus-5-5)
A default-node hook selects wivrn.sink/wivrn.source whenever they exist,
so the polling wivrn-audio.nu unit, its saved state and `just vr-audio`
go away. Streams follow via linking.follow-default-target.
Assisted-by: Claude Code (claude-opus-5-5)
`just mihomo-geo` copied geodata into the state dir so a cold start never
fetched it. tmpfiles now links v2ray-rules-dat and the DB-IP lite mmdbs
there, so geo-auto-update moves to the gateway config only.
Assisted-by: Claude Code (claude-opus-5-5)
myNurPkgs evaluated a second nixpkgs just for four packages. Apply the
NUR repo's own overlay instead, so they come from the host's pkgs.
Darwin's pkgs now gets nixpkgsConfig too: it set nixpkgs.pkgs, which
ignores nixpkgs.config, so only myNurPkgs carried the npm mirror there.
Assisted-by: Claude Code (claude-opus-5-5)
Both only touch pkgsStatic builds, which nothing on this host pulls in
since qemu-user binfmt was removed; the toplevel drvPath is unchanged.
Assisted-by: Claude Code (claude-opus-5-5)
agenix's darwin module honours age.secrets.<name>.path and applies owner and
mode itself, so the environment.etc links and the postActivation chown/chmod
that re-applied them go away.
Assisted-by: Claude Code (claude-opus-5-5)
WA-023 warns once nixVersions.stable reaches 2.35; WA-046 fails the build
once nixpkgs' xdg-desktop-portal-wlr is no longer the broken 0.8.4, so both
pins get revisited instead of lingering.
Assisted-by: Claude Code (claude-opus-5-5)
hardware.amdgpu.opencl.enable also adds clr, whose OpenCL 2.2 libOpenCL.so.1
lands in /run/opengl-driver/lib, first in OpenVINO's RUNPATH, and shadows the
ocl-icd loader the GPU plugin links. With clr.icd alone the plugin finds
ocl-icd, so the wrapper and WA-038 go.
Assisted-by: Claude Code (claude-opus-5-5)
It is already the sshd module default, and the shared firewall forces
allowedTCPPorts to [ ] anyway; toplevel drvPaths are unchanged.
Assisted-by: Claude Code (claude-opus-5-5)
systemd's 60-fido-id.rules marks the FIDO hidraw node and the CCID USB
device as security tokens, and 70-uaccess.rules grants them to the
active seat. Drop the hand-written rules and the plugdev membership.
Assisted-by: Claude Code (claude-opus-5-5)
Exporters raced networkd for the LAN address at boot; drop the
network-online ordering and unlimited-restart hack and let a FreeBind
socket unit hold the port, passed in via --web.systemd-socket.
Assisted-by: Claude Code (claude-opus-5-5)
The data dir is the module's own DynamicUser StateDirectory (already
/var/lib/victoriametrics -> private/victoriametrics on youko), so the
victoriametrics-data group, its tmpfiles rule and SupplementaryGroups
did nothing. stateDir was set to its default.
Assisted-by: Claude Code (claude-opus-5-5)
Root is tmpfs, so the backing dirs must exist before the /data bind
mounts. initrd tmpfiles rules on /sysroot do that before switch-root,
replacing the activation script.
Assisted-by: Claude Code (claude-opus-5-5)
services.homepage-dashboard runs the nixpkgs package with DynamicUser,
replacing the :latest container, its system user and the activation
script that rsynced the YAML into place. The YAML stays as is and is
linked into /etc/homepage-dashboard.
Assisted-by: Claude Code (claude-opus-5-5)
A tmpfiles L+ symlink to the store path replaces the activation script
that rsynced it on every switch. Files that were removed upstream no
longer linger.
Assisted-by: Claude Code (claude-opus-5-5)
NixOS's libvirtd-config copies default.xml but never creates its
autostart link, and shoryu has no networks/autostart directory at all,
so virbr0 never starts and the oneshot had nothing to disable.
Assisted-by: Claude Code (claude-opus-5-5)
USB_QUIRK_RESET_RESUME ("b") makes usbcore reset the camera instead of
resuming it, and uvcvideo implements reset_resume, so the resume hook
that ran usbreset after S3 is no longer needed.
Assisted-by: Claude Code (claude-opus-5-5)
Apply hdparm -B 255 -S 0 whenever a disk appears, which also covers the
re-add after a bridge reset, instead of a boot oneshot plus an hourly
timer. Drop -J 0: hdparm has always aborted it without
--please-destroy-my-drive, and wdidle3 already reads disabled on both
disks (it is persistent firmware state).
This was the last user of mylib.systemdHardening.privilegedOneshot, so
remove the preset and its test group.
Assisted-by: Claude Code (claude-opus-5-5)
The boot-time oneshot only fixed the zones present when it ran, and
node_exporter had to Require it. A powercap add rule covers every zone
whenever it appears; the collector reads at scrape time, so no ordering
is needed.
Assisted-by: Claude Code (claude-opus-5-5)
macsmc-battery's "add" uevent fires before the supply accepts writes
(power_supply_register bumps use_cnt after device_add); its deferred
register work then emits "change". Match both instead of polling for 30 s
from a oneshot.
Assisted-by: Claude Code (claude-opus-5-5)
amdgpu registers power1_cap with its hwmon device, so a rule on each
card's hwmon add event replaces the oneshot that polled /sys/class/hwmon
for 60 s waiting for both cards to probe.
Assisted-by: Claude Code (claude-opus-5-5)
On shoukei a DNS takeover that timed out failed mihomo.service, and the
nixpkgs unit has no Restart=, so the proxy stayed down. Prefix the hook
with '-' (fail open, like ai's hook). Also switch the dashboard title
patch to --replace-fail.
Assisted-by: Claude Code (claude-opus-5-5)
A 2024 formatter pass turned `{{…}}` into a YAML flow map, the variable
was never passed to the container, and no widget uses it.
Assisted-by: Claude Code (claude-opus-5-5)
Cleanup runs as ExecStopPost, which a power loss mid-backup skips; the
leftover @restic-snapshot then failed every later run.
Assisted-by: Claude Code (claude-opus-5-5)
It was a precaution for the R9700 links, never an observed problem, and
on BIOS 1013 the FADT already disables ASPM ("ACPI FADT declares the
system doesn't support PCIe ASPM"), so the parameter did nothing.
Assisted-by: Claude Code (claude-opus-5-5)
89d7a9bd made systemd-tmpfiles-setup RequiresMountsFor the five nofail
USB-disk mounts. One failed mount then skipped every tmpfiles rule on the
host, and sysinit.target waited for the disks and the LUKS unlock even on
success.
Leave the boot-wide pass as upstream ships it and add data-tmpfiles:
`systemd-tmpfiles --prefix=/data` with WantsMountsFor on the /data mounts,
ordered before the services that use them.
Assisted-by: Claude Code (claude-opus-5-5)
- WA-017: keyd(1) documents `control = layer(control)` etc. as the
default modifier bindings, so a keyd bump will never keep right-hand
modifiers distinct; the passthrough is intentional config. Say so in
peripherals.nix and drop the row.
- WA-026: installOnHost + restarting microvm@<guest> is the fallback
microvm.nix's own deploy.rebuild uses; only the in-place sshSwitch is
broken, so the removal condition is now about that.
- WA-023: locked versions are 2.35.3 (latest) and 2.34.9 (stable).
Assisted-by: Claude Code (claude-opus-5-5)
Each of these overrode a value nothing else set at a higher priority:
- qemu-guest: btrbk, hardwareTools and cloud-init have no competing
definition on the guests.
- mdns: nixpkgs' sunshine module sets avahi.publish.* with mkDefault.
- computer-use: i18n now sets the time zone with mkDefault.
- darwin terminal: the shared ghostty/kitty font sizes are now mkDefault.
- shoukei: sunshine is mkDefault false in remote-desktop.nix, zram's
memoryPercent is mkDefault 50; supportedFilesystems now merges instead
of discarding the fsType entries other modules add.
- youko: nothing enables docker.
- nixpak: pasta.enable and sockets.pipewire are mkDefault in the shared
modules, sockets.pulse already was.
Every NixOS host's toplevel drvPath is unchanged; on fern and frieren the
terminal font sizes and time zone evaluate to the same values.
Assisted-by: Claude Code (claude-opus-5-5)
nix-darwin runs `brew bundle` as `sudo --preserve-env=PATH ... env ...`,
so the variables the mkBefore'd activation snippet exported never reached
brew; activation was using the default upstream endpoints. Set them via
homebrew.onActivation.extraEnv, which nix-darwin puts on the `env` command
line (evaluated on fern), and drop the snippet.
Assisted-by: Claude Code (claude-opus-5-5)
bb899876 moved the proxy gateway to fake-IP for IPv6 and removed
aquamarine's getaddrinfo precedence table, so the row's removal condition
has been met since 2026-10-14; only the row and a comment pointer lingered.
Assisted-by: Claude Code (claude-opus-5-5)
drivers/nvme/host/core.c defaults default_ps_max_latency_us to 100000, so
the "relaxed" APST cap on ai and its copy on aquamarine set the value the
kernel already uses. Keep ai's history and rollback recipe as a comment,
and stop the aquamarine pcie_aspm=off comment from citing WA-008, which
never covered it. Removes WA-008.
Assisted-by: Claude Code (claude-opus-5-5)
hardware.nvidia.modesetting.enable makes nixpkgs' nvidia module add
nvidia-drm.modeset=1 and, for drivers >= 545, nvidia-drm.fbdev=1 to
moduleParams, which reach modprobe via extraModprobeConfig (evaluated:
"options nvidia-drm fbdev=1 modeset=1" on ai with 595.x).
Assisted-by: Claude Code (claude-opus-5-5)
nixpkgs' security/ca.nix and nix-darwin's security/pki module both append
"Comodo AAA Services root" to the cacert blacklist themselves, so the
explicit entry only duplicated it. Removes WA-040.
Assisted-by: Claude Code (claude-opus-5-5)
nixpkgs packages tuios 0.11.1, the same tag the flake input pinned, so
the extra input (and its nix-fast-build/treefmt-nix lock entries) only
duplicated it. Removes WA-005.
Assisted-by: Claude Code (claude-opus-5-5)
nixpkgs packages smartctl_exporter 0.15.0, which contains PR #329. The
overlay pinned 6274decc ("0.14.0-unstable-2026-09-28"), so it had been
downgrading the package since the last nixpkgs bump. Removes WA-001.
Assisted-by: Claude Code (claude-opus-5-5)
Set the user-level npm/pnpm registry to npmmirror, and make Nix builds use the same mirror through nixpkgs' npmRegistryOverrides and rewriteURL. The configured Nixpkgs policy is also applied to myNurPkgs and Colmena's nodeNixpkgs so NUR packages such as dsh use the mirror while preserving Colmena parity.
WeChat and QQ now use native Wayland, so the X11/Wayland clipboard bridge
is no longer needed. Remove the flake input, home-manager module wiring,
lock entries, and stale documentation reference.
Assisted-by: DeepSeek Harness (deepseek-flash)
Merge the three runtime-validation files into one concise record, update the interactive browser and Zoom results, and document the intentional Chromium host-loopback and Brave/Edge no-media exceptions. Tighten the remaining cookie-crypto workaround and the smoke-test prerequisites.
Assisted-by: DeepSeek Harness (deepseek-flash)
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)