Files
nix-config/docs/flake-improvements.md
mjallen18 70002a19e2 hmm
2026-04-07 18:39:42 -05:00

16 KiB
Executable File
Raw Permalink Blame History

Flake Improvement Suggestions

A methodical review of the flake against what Snowfall Lib provides and what the codebase currently does. Suggestions are grouped by theme and ordered roughly from highest to lowest impact.


1. Flake-level: HM module registration — single source of truth via snowfall-lib fix

Root cause discovered: Snowfall Lib's mkFlake previously merged systems.modules.home into homes only for standalone homeConfigurations. The homes attrset passed to create-systems (which builds nixosConfigurations) was the raw unmerged value, so systems.modules.home had no effect on NixOS-integrated homes.

Fix applied: Patched the personal snowfall-lib fork (github:mjallen18/snowfall-lib) to extract the merge into a shared homes-with-system-modules binding and pass it to both create-homes (standalone) and create-systems (NixOS-integrated). flake.lock updated to the new commit.

modules/nixos/home/default.nix no longer needs sharedModulessystems.modules.home in flake.nix is now the single authoritative list for all contexts.


2. Flake-level: Duplicated Darwin HM module registration

Problem: Same issue as above for Darwin. flake.nix:160167 registers Darwin HM modules via systems.modules.darwin, but none of those are actually Home Manager modules — nix-homebrew, home-manager.darwinModules.home-manager, nix-plist-manager, nix-rosetta-builder, nix-index-database, and stylix.darwinModules.stylix are all NixOS-style Darwin system modules, not HM sharedModules. This is the correct place for them. The modules/darwin/home/default.nix module handles the Darwin-side HM bridge.

No change needed here, but add a comment to clarify why this list stays in flake.nix while the modules.home list should move:

# Common darwin system-level modules (not HM sharedModules — those live in modules/darwin/home/)
modules.darwin = with inputs; [ ... ];

3. System-level: Repeated nebula lighthouse config

Problem: Three systems (matt-nixos, allyx, macbook-pro-nixos) each independently spell out the same lighthouse peer config:

# Repeated verbatim in 3 files:
lighthouses = [ "10.1.1.1" ];
staticHostMap = {
  "10.1.1.1" = [ "mjallen.dev:4242" ];
};
port = 4242;

Suggestion: Add defaults to modules/nixos/services/nebula/default.nix options so that non-lighthouse nodes don't need to spell this out. Since this is a personal network with one lighthouse, the defaults can encode that:

# In nebula/default.nix options:
lighthouses = lib.mjallen.mkOpt (types.listOf types.str) [ "10.1.1.1" ]
  "Nebula overlay IPs of lighthouse nodes";

staticHostMap = lib.mjallen.mkOpt (types.attrsOf (types.listOf types.str))
  { "10.1.1.1" = [ "mjallen.dev:4242" ]; }
  "Static host map";

port = lib.mjallen.mkOpt types.port 4242 "Nebula listen port";

Client systems can then reduce to:

services.nebula = {
  enable = true;
  secretsPrefix = "matt-nixos/nebula";
  secretsFile = lib.snowfall.fs.get-file "secrets/desktop-secrets.yaml";
  hostSecretName = "matt-nixos";
};

The lighthouse (pi5) already overrides isLighthouse = true and doesn't set lighthouses/staticHostMap, so it would be unaffected.


4. System-level: systemd-networkd-wait-online scattered disablement

Problem: systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false appears in:

  • systems/x86_64-linux/matt-nixos/default.nix:92
  • systems/x86_64-linux/allyx/default.nix:135

modules/nixos/network/default.nix already disables NetworkManager-wait-online and systemd.network.wait-online, but not systemd-networkd-wait-online. These are the same underlying concern.

Suggestion: Add systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false; unconditionally to modules/nixos/network/default.nix alongside the existing NetworkManager-wait-online disablement (line 89). Remove the per-system overrides.


5. System-level: coolercontrol and GNOME desktop environment variables

Problem: Two systems (matt-nixos:91, allyx:82) share identical config blocks:

programs.coolercontrol.enable = true;

environment.variables = {
  GDK_SCALE = "1";
  EDITOR = "${lib.getExe' pkgs.vscodium "codium"} --wait";
  VISUAL = "${lib.getExe' pkgs.vscodium "codium"} --wait";
};

These belong to a desktop AMD gaming profile, not to the system configs themselves.

Suggestions (pick one or both):

  • A — Add a coolercontrol.enable option to modules/nixos/hardware/amd/default.nix (default false) and wire programs.coolercontrol.enable inside it. Each system opts in with hardware.amd.coolercontrol.enable = true.
  • B — Add vscodium as the default EDITOR/VISUAL to modules/nixos/desktop/gnome/default.nix behind a vscodium.enable option (default false). The two systems that want it set desktop.gnome.vscodium.enable = true.
  • C — Create a shared modules/nixos/desktop/common/default.nix (or profiles/desktop.nix) that both GNOME and Hyprland modules consume, and put GDK_SCALE there.

6. System-level: networking.networkmanager.wifi.backend = "iwd" bypass

Problem: matt-nixos:100 and allyx:140 set networking.networkmanager.wifi.backend = "iwd" directly, bypassing the ${namespace}.network.iwd.enable option that the network module already provides.

Looking at modules/nixos/network/default.nix:143154, enabling cfg.iwd.enable does set this value via mkForce, but it also forces networkmanager.enable = mkForce false — which is unwanted on these systems that use NetworkManager with the iwd backend.

Root cause: The module conflates "use iwd" (the WiFi daemon) with "disable NetworkManager" (the connection manager). These are separate concerns. NetworkManager can use iwd as its WiFi backend while still being the connection manager.

Suggestion: Restructure the network module's iwd handling:

# Instead of forcing NM off when iwd is enabled:
networking = {
  wireless.iwd.enable = cfg.iwd.enable;
  networkmanager = mkIf cfg.networkmanager.enable {
    enable = true;
    wifi.backend = mkIf cfg.iwd.enable "iwd";
    # ... rest of NM config
  };
};

Then the per-system lines become:

${namespace}.network = {
  hostName = "matt-nixos";
  iwd.enable = true;
  networkmanager.enable = true;
};

7. System-level: fileSystems."/etc".neededForBoot not in impermanence module

Problem: fileSystems."/etc".neededForBoot = true is set manually in four system configs (nuc-nixos, pi5, jallen-nas, graphical). This is a prerequisite of impermanence (tmpfs root), not a per-system choice.

Suggestion: Add to modules/nixos/impermanence/default.nix:

config = mkIf cfg.enable {
  fileSystems."/etc".neededForBoot = true;
  # ... existing config
};

Then remove the manual setting from each system. (macbook-pro-nixos and matt-nixos may already have this in their filesystems.nix — verify and remove duplicates there too.)


8. System-level: system.stateVersion and time.timeZone should be module options

Problem: In modules/nixos/system/default.nix:

  • Line 3: timezone = "America/Chicago" is hardcoded
  • Line 54: system.stateVersion = "23.11" is hardcoded

Both are set unconditionally for every system with no way to override without using lib.mkForce.

Suggestions:

# modules/nixos/system/default.nix
{ config, lib, namespace, pkgs, system, ... }:
let
  cfg = config.${namespace}.system;
in
{
  options.${namespace}.system = {
    timezone = lib.mkOption {
      type = lib.types.str;
      default = "America/Chicago";
      description = "System timezone";
    };
    stateVersion = lib.mkOption {
      type = lib.types.str;
      default = "23.11";
      description = "NixOS state version. Should match the version used when the system was first installed.";
    };
  };

  config = {
    time.timeZone = cfg.timezone;
    system.stateVersion = cfg.stateVersion;
    # ... packages
  };
}

This maintains the current default for all systems (no change required) while allowing any system to say ${namespace}.system.stateVersion = "24.05" cleanly.


9. Module-level: Darwin and NixOS nix modules share ~90% of their content

Problem: modules/darwin/nix/default.nix and modules/nixos/nix/default.nix differ only in:

  • Darwin lacks daemonCPUSchedPolicy/daemonIOSchedClass/daemonIOSchedPriority
  • Darwin lacks the systemd.services.nix-gc.serviceConfig block
  • Darwin lacks cudaSupport/rocmSupport in nixpkgs.config
  • Darwin's substituters list omits attic.xuyh0120.win/lantian

Everything else — substituters, trusted keys, warn-dirty, experimental-features, trusted-users, builders-use-substitutes, connect-timeout, fallback, log-lines, max-free, min-free, GC settings, optimise — is identical.

Suggestion: Extract a shared Nix attrset into lib/nix-settings/default.nix (or a plain .nix file imported by both):

# lib/nix-settings/default.nix
{ lib }:
{
  commonSubstituters = [
    "http://jallen-nas.local:9012/nas-cache"
    "https://nixos-apple-silicon.cachix.org"
    "https://nixos-raspberrypi.cachix.org"
    "https://nix-community.cachix.org"
    "https://cache.nixos.org/"
  ];
  commonTrustedPublicKeys = [ ... ];
  commonSettings = { warn-dirty = ...; experimental-features = ...; ... };
  commonGc = { automatic = true; options = "--delete-older-than 30d"; };
}

Both modules import and spread this. The NixOS module adds scheduler policies and systemd GC service tweaks on top.


10. Module-level: Home SOPS configuration is inconsistent across homes

Problem: Three different patterns are used to configure SOPS in home configs:

  1. ${namespace}.sops.enable = true — uses the module at modules/home/sops/default.nix (macbook-pro-nixos home, jallen-nas home)
  2. Inline SOPS config — sets sops.* directly (allyx home, pi5 home)
  3. Nothing — some homes don't configure sops at all (matt-nixos home relies on system-level secrets only)

The modules/home/sops/default.nix module already handles the age.keyFile path, defaultSopsFile, and SSH key setup. The inline patterns duplicate this.

Suggestion: Migrate all homes that configure sops inline to use ${namespace}.sops.enable = true. If the home needs a different defaultSopsFile (e.g. pi5 uses secrets/pi5-secrets.yaml), that should be a module option:

# modules/home/sops/default.nix — add option:
options.${namespace}.sops = {
  enable = lib.mkEnableOption "home sops";
  defaultSopsFile = lib.mkOption {
    type = lib.types.nullOr lib.types.path;
    default = null;  # falls back to global secrets.yaml
    description = "Override the default SOPS file for this home";
  };
};

11. Module-level: modules/nixos/home/default.nixhome-manager input key coupling

Problem: systems.modules.nixos in flake.nix:147 explicitly includes home-manager.nixosModules.home-manager. However Snowfall Lib automatically injects the home-manager NixOS module when the home-manager input is present and there are home configurations (Snowfall Lib system/default.nix lines 265270).

Suggestion: Verify (by temporarily removing the explicit entry) whether home-manager.nixosModules.home-manager can be dropped from systems.modules.nixos. If Snowfall Lib handles this automatically, removing it eliminates the manual coupling.


12. System-level: nuc-nixos — large monolithic default.nix

Problem: systems/x86_64-linux/nuc-nixos/default.nix is over 330 lines and contains everything inline: disk config, networking, Home Assistant dashboard definitions (~170 lines of inline Nix), kernel config, user setup, and services. Every other complex system (jallen-nas) already uses a split structure with apps.nix, services.nix, nas-defaults.nix, etc.

Suggestion: Extract into separate files following the jallen-nas pattern:

systems/x86_64-linux/nuc-nixos/
├── default.nix          # thin: imports + top-level options
├── boot.nix             # disk/luks/filesystem config
├── dashboard.nix        # Home Assistant dashboard card definitions
├── services.nix         # postgres, redis, HA, OTBR etc.
└── sops.nix             # (or reuse the shared module)

The dashboard in particular (currently lines ~88260) should be isolated so HA configuration changes don't require touching system-level config.


13. System-level: Verify admin@jallen-nas steam-rom-manager double-import

Problem: homes/x86_64-linux/admin@jallen-nas/default.nix:16 explicitly imports steam-rom-manager.homeManagerModules.default. This same module is injected globally via modules/nixos/home/default.nix:92 for all x86_64 systems (the ARM guard is !isArm, and jallen-nas is x86_64).

Suggestion: Remove the explicit import from admin@jallen-nas/default.nix. If it was added for standalone home-manager switch builds (without NixOS), document that reason in a comment rather than keeping a potentially conflicting double-import.


14. Flake-level: pi5 host entry with empty modules list

Problem: flake.nix:218221 defines:

pi5 = {
  modules = [ ];
};

An empty modules list is the default behavior — this entry has no effect and can be removed. The comment # disko is already in systems.modules.nixos above is incorrect (disko is global for all systems, not specific to pi5). The comment itself is misleading.

Suggestion: Remove the pi5 host entry from flake.nix entirely. If the comment is meant to remind future maintainers that disko is global, move that context to AGENTS.md or a comment near the global systems.modules.nixos list.


15. Flake-level: home-manager-stable input is pulled in but never used

Problem: flake.nix:1013 defines home-manager-stable but home-manager = home-manager-unstable is the alias (line 21). No system or module references home-manager-stable directly. It adds to lock file churn and evaluation time.

Suggestion: Remove home-manager-stable unless there is a concrete plan to use it for a stable-channel system. If stable Home Manager support is desired in the future, add it back at that point.


16. Flake-level: Consider using Snowfall Lib alias for formatter output

Problem: The outputs-builder in flake.nix:277280 is used only to register the treefmt formatter. Snowfall Lib supports an alias mechanism and also allows outputs-builder to be used, but this is the only use of outputs-builder in the entire flake.

Suggestion: This is fine as-is, but note that outputs-builder output can be overridden by auto-discovery. Since the formatter isn't auto-discovered, outputs-builder is the correct approach. No change needed — but the comment on line 279 about the mjallen-lib overlay being auto-discovered is accurate and good to keep.


Summary Table

# Location Type Effort Impact
1 flake.nix Deduplication Low High — removes confusing double-registration
2 flake.nix Documentation Low Low
3 nebula/default.nix Better defaults Low Medium — 3 systems simplified
4 network/default.nix Consolidation Low Medium — remove per-system workarounds
5 hardware/amd + desktop/gnome New options Medium Medium — DRY gaming desktop profile
6 network/default.nix Bug fix / refactor Medium High — current iwd handling is incorrect
7 impermanence/default.nix Consolidation Low Medium — remove 4 manual entries
8 system/default.nix New options Low Medium — allows per-system overrides cleanly
9 lib/ + darwin/nix + nixos/nix Extraction Medium Medium — single source of truth for nix config
10 homes/*/ + modules/home/sops Consistency Low Low — consistency improvement
11 flake.nix Simplification Low Low — possible dead entry
12 systems/nuc-nixos/ Refactor Medium High — maintainability
13 homes/admin@jallen-nas Bug fix Trivial Low — potential double-import
14 flake.nix Cleanup Trivial Low — dead code
15 flake.nix Cleanup Trivial Low — reduces lock churn
16 flake.nix N/A None No change needed