# 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 `sharedModules` — `systems.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:160–167` 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:

```nix
# 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:

```nix
# 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:

```nix
# 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:

```nix
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:

```nix
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:143–154`, 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:

```nix
# 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:

```nix
${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`:

```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**:

```nix
# 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):

```nix
# 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:

```nix
# 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.nix` — `home-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 265–270).

**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 ~88–260) 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:218–221` defines:

```nix
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:10–13` 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:277–280` 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 |
