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)
Enable the existing remote-builder module on shoryu, shushou and
youko, and authorize their SSH host keys on aquamarine.
Kana and Ruby are VM guests with no need to build; leave their local
store and build behavior unchanged.
Assisted-by: DeepSeek Harness (deepseek-flash)
Run nginx on youko as a loopback pull-through HTTP cache for
cache.nixos.org and cache.numtide.com, exposed by Caddy at
https://nix-cache.writefor.fun.
Assisted-by: DeepSeek Harness (deepseek-flash)
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.
systemd-tmpfiles-setup only orders itself after local-fs.target, but the nofail /data submounts can complete later. It then sees /data as the read-only fallback tmpfs and fails to create the Transmission, Immich, RustFS and restic directories at boot.
Add RequiresMountsFor for the data mounts and cover the generated unit dependency with an eval test.
* feat(nixpak): sandbox six desktop apps
* chore(nixpak): trim OBS plugin set
* fix(nixpak): enable OBS on aarch64
* fix(nixpak): let imv read the full home directory
* fix(home): drop redundant freerdp package
* fix(nixpak): grant OBS access to V4L2 camera devices
* fix(nixpak): expose V4L2 sysfs to OBS
* fix(nixpak): expose NVIDIA encode library to OBS
* docs(nixpak): record OBS interactive validation
* docs(nixpak): record OBS RTMP validation
* fix(nixpak): run MuseScore via XWayland and PulseAudio
* docs(nixpak): explain app sandboxing rationale
* fix(nixpak): route MuseScore ALSA through PipeWire
* refactor(nixpak): split policy checks into per-app suites
Make hardening/tests/policy.nix a small loader that auto-discovers one suite per app under hardening/tests/policy. Shared checks live in policy-lib.nix and common expectations are generated by mkAppPolicy; app files only declare differences and special cases. The loader also discovers wrappers from hardening/nixpaks and fails when wrapper/suite inventory or check names diverge.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(nixpak): reject duplicate per-app policy checks
mkAppPolicy merges generated and custom checks, so an extraChecks key could silently override a standard assertion. Fail explicitly when a per-app check name is defined twice.
Assisted-by: DeepSeek Harness (deepseek-flash)
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)
Switch to the native deb package from nixpkgs-patched (#560225), remove the AppImage/FHS bwrap layer, and enable native Wayland while keeping the X11 fallback.
Keep only WeChat state under one app-scoped HOME and bind the host Documents and Downloads directories like the other desktop apps. User-selected files also go through the XDG FileChooser portal. Remove the launcher, activation script, and compatibility symlinks.
Assisted-by: DeepSeek Harness (deepseek-flash)
* refactor(nixpak): unify desktop app sandboxes
Move common GUI, portal, and network policy into reusable modules and migrate browsers plus Zoom/WeChat/QQ/Telegram to the same boundary.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(nixpak): stop exposing the system D-Bus to apps
* fix(nixpak): bind only the PulseAudio socket
* feat(nixpak): run apps in a new session tied to the launcher
* feat(nixpak): start apps from an allowlisted environment
* feat(nixpak): isolate app networking with pasta
* fix(nixpak): keep Firefox on the host network
Assisted-by: Claude Code (claude-opus-5-5)
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)
* chore(nix): update nixpkgs to e62746b and fix 26.11 breakage
Bump nixpkgs to e62746b and preservation to 00be7f7, then adapt to the removals and renamed options in the new revision: drop the removed boot.initrd.systemd.enable settings, trim the stale emSign CA blacklist on Linux while keeping it for the older Darwin input, and migrate Avahi publishing to settings.publish. Work around sunshine still setting the legacy Avahi options and keep dsh-nu on its tested Nushell 0.115.1. Patch NUR dsh's pnpm call to pass nodejs = null, and update pyclipsync for the stdenv.hostPlatform.system rename.
* chore(nix): refresh flake inputs for 26.11
- update nixpkgs-darwin to the same e62746b revision as nixpkgs
- make dsh-nu follow main nixpkgs and patch its testedWith manifest for the resolved Nushell
- refresh floating inputs; update lanzaboote, tuios, and ponytail
- drop the nixpkgs-2505 and rust-overlay workarounds
- keep pyclipsync on main and simplify the cross-platform CA blacklist
- adopt home-manager's Nushell loadSessionVariables default
* fix(gitea): use ROOT_URL for nixpkgs 26.11
nixpkgs removed the legacy settings.server.DOMAIN option; the existing ROOT_URL already carries the public URL.
* chore(nix): update dsh-nu to 0.7.0
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.