From 492dcb13fe1bdd7adcc317b60a7652f2cd546aee Mon Sep 17 00:00:00 2001 From: Ryan Yin Date: Wed, 14 Oct 2026 19:50:18 +0800 Subject: [PATCH] fix(ai): match the wired NIC by prefix, not by name Assisted-by: DeepSeek Harness (deepseek-flash) --- home/hosts/linux/idols-ai.nix | 3 ++- home/hosts/linux/idols-ai/noctalia.toml | 7 +++-- outputs/README.md | 8 +++--- .../tests/idols-ai-net/expected.nix | 4 +++ .../x86_64-linux/tests/idols-ai-net/expr.nix | 27 +++++++++++++++++++ vars/networking.nix | 6 +++-- 6 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 outputs/x86_64-linux/tests/idols-ai-net/expected.nix create mode 100644 outputs/x86_64-linux/tests/idols-ai-net/expr.nix diff --git a/home/hosts/linux/idols-ai.nix b/home/hosts/linux/idols-ai.nix index 75f6b43d..fae0ba86 100644 --- a/home/hosts/linux/idols-ai.nix +++ b/home/hosts/linux/idols-ai.nix @@ -34,7 +34,8 @@ in xdg.configFile."niri/niri-hardware.kdl".source = mkSymlink "${config.home.homeDirectory}/nix-config/hosts/idols-ai/niri-hardware.kdl"; - # Keep Noctalia's network widgets on the physical uplink, not the Mihomo TUN. + # Keep Noctalia's network widgets on the physical uplink, not the Mihomo TUN + # (the interface name is in ./idols-ai/noctalia.toml and `hostsAddr.ai.iface`). xdg.configFile."noctalia/host-ai.toml".source = ./idols-ai/noctalia.toml; # The desktop speakers are already loud, so use the broadcast-standard -23 dB. diff --git a/home/hosts/linux/idols-ai/noctalia.toml b/home/hosts/linux/idols-ai/noctalia.toml index 09973d02..e9384d5f 100644 --- a/home/hosts/linux/idols-ai/noctalia.toml +++ b/home/hosts/linux/idols-ai/noctalia.toml @@ -1,6 +1,9 @@ # The default total counts packets on both Mihomo's TUN and the physical uplink. +# Noctalia matches an exact name and takes no pattern, so the wired NIC is named +# here and in `hostsAddr.ai.iface`; `idols-ai-net` asserts each widget separately. +# networkd itself matches the `en*` prefix. [widget.net_rx] -interface = "enp130s0" +interface = "enp4s0" [widget.net_tx] -interface = "enp130s0" +interface = "enp4s0" diff --git a/outputs/README.md b/outputs/README.md index 5d5c6118..e714a4e5 100644 --- a/outputs/README.md +++ b/outputs/README.md @@ -47,10 +47,10 @@ Some tests iterate over every configuration, so a new host is covered automatica them. Others name specific hosts — or a name prefix, like `k3s-api-endpoint`, which covers every `k3s-test-1-*` configuration — and only cover a new host if you add it. -| Kind | Tests | -| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Every configuration | `hostname`, `kernel`, `nix-system-features`, `security-apparmor`, `security-container-groups`, `security-exporters`, `security-firewall`, `security-k3s-kubeconfig`, `security-k3s-pod-firewall`, `security-kernel`, `security-ssh-x11` | -| Named hosts only | `btrbk`, `home-manager`, `home-manager-xdg`, `computer-use-headless`, `k3s-api-endpoint`, `k3s-node-home-manager`, `idols-ai-gpu`, `idols-ai-voice-input`, `idols-aquamarine`, `security-postgres-auth`, `youko-metrics`, `ups-metrics`, `shoukei-logind` | +| Kind | Tests | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Every configuration | `hostname`, `kernel`, `nix-system-features`, `security-apparmor`, `security-container-groups`, `security-exporters`, `security-firewall`, `security-k3s-kubeconfig`, `security-k3s-pod-firewall`, `security-kernel`, `security-ssh-x11` | +| Named hosts only | `btrbk`, `home-manager`, `home-manager-xdg`, `computer-use-headless`, `k3s-api-endpoint`, `k3s-node-home-manager`, `idols-ai-gpu`, `idols-ai-net`, `idols-ai-voice-input`, `idols-aquamarine`, `security-postgres-auth`, `youko-metrics`, `ups-metrics`, `shoukei-logind` | `hostname` expects each configuration's `networking.hostName` to equal its name. Niri desktop configurations are the exception (`ai-niri` → `ai`, `shoukei-niri` → `shoukei`) and are listed in diff --git a/outputs/x86_64-linux/tests/idols-ai-net/expected.nix b/outputs/x86_64-linux/tests/idols-ai-net/expected.nix new file mode 100644 index 00000000..46da1c06 --- /dev/null +++ b/outputs/x86_64-linux/tests/idols-ai-net/expected.nix @@ -0,0 +1,4 @@ +{ + staticAddressMatchesWiredInterface = true; + noctaliaReadsHostIface = true; +} diff --git a/outputs/x86_64-linux/tests/idols-ai-net/expr.nix b/outputs/x86_64-linux/tests/idols-ai-net/expr.nix new file mode 100644 index 00000000..03444bf0 --- /dev/null +++ b/outputs/x86_64-linux/tests/idols-ai-net/expr.nix @@ -0,0 +1,27 @@ +{ + lib, + myvars, + outputs, + ... +}: +let + cfg = outputs.nixosConfigurations.ai-niri.config; + hm = cfg.home-manager.users.${myvars.username}; + inherit (myvars.networking.hostsAddr.ai) iface ipv4; + # Read the file that actually gets installed, not the path in the repository. + noctalia = builtins.readFile hm.xdg.configFile."noctalia/host-ai.toml".source; +in +{ + # A board or PCIe change renames the wired NIC with the PCIe topology, so networkd + # matches the `en*` prefix; a name-based match left the host off the LAN. + staticAddressMatchesWiredInterface = + builtins.elem "${ipv4}/24" cfg.systemd.network.networks."10-lan".networkConfig.Address + && cfg.systemd.network.networks."10-lan".matchConfig.Name == [ "en*" ]; + + # The two consumers that need one exact name — mihomo's DNS hook and Noctalia's + # net-speed widgets — take it from `hostsAddr.ai.iface`; each widget is asserted + # on its own, so a drift in either one fails. + noctaliaReadsHostIface = + lib.hasInfix "[widget.net_rx]\ninterface = \"${iface}\"" noctalia + && lib.hasInfix "[widget.net_tx]\ninterface = \"${iface}\"" noctalia; +} diff --git a/vars/networking.nix b/vars/networking.nix index 252f30d6..15549e81 100644 --- a/vars/networking.nix +++ b/vars/networking.nix @@ -44,8 +44,10 @@ rec { # monitoring skip those entries. hostsAddr = { ai = { - # Desktop PC - iface = "enp130s0"; + # Desktop PC. `iface` is only for the two consumers that need one exact name + # — mihomo's DNS takeover hook and the Noctalia net-speed widget; networkd + # matches the wired interface by its `en*` prefix (hosts/idols-ai/default.nix). + iface = "enp4s0"; ipv4 = "192.168.5.100"; ipv6 = "fe80::10"; # Link-local Address };