refactor(ssh): restructure inventory and modularize helper logic

Relocate SSH inventory and rbw socket wiring from `modules/lib/` to
dedicated `modules/ssh/` modules to improve logical grouping.

- Move `ssh-inventory.nix` to `modules/ssh/inventory.nix`.
- Move `rbw-ssh-socket.nix` to `modules/ssh/rbw.nix`.
- Relocate `users-merge.nix` logic into `modules/system/users.nix` to
  reduce dependency on generic library modules.
- Update `docs/conventions.md` to reflect new module paths.
- Refactor `modules/deploy/navi.nix` to use a local helper for
  generating Navi hive configurations.
- Add Pi5 specific Niri KDL configuration snippets.
This commit is contained in:
2026-07-15 23:27:03 -03:00
parent a787840ce2
commit c90d368432
13 changed files with 314 additions and 293 deletions
+138 -27
View File
@@ -8,18 +8,115 @@
{ config, options, lib, ... }:
let
cfg = config.chiasson.users;
usersLib = self.lib.usersMerge lib;
usersLib =
let
userHm = user: user.homeManager or { };
userSsh = user: user.ssh or { };
in
rec {
resolveHomeManagerModule =
moduleSpec:
let
t = builtins.typeOf moduleSpec;
in
if t == "path" || t == "string" then import moduleSpec else moduleSpec;
selectedUsersAttr =
{ catalog, enabled, hostOverrides }:
lib.listToAttrs (
map (name: {
inherit name;
value = lib.recursiveUpdate catalog.${name} (hostOverrides.${name} or { });
}) enabled
);
missingEnabledNames =
catalog: enabled: builtins.filter (name: !(builtins.hasAttr name catalog)) enabled;
strayHomeUserKeys =
homeUsers: enabled: builtins.filter (k: !(builtins.elem k enabled)) (builtins.attrNames homeUsers);
mkNixosUser =
name: user:
{
isNormalUser = user.isNormalUser or true;
description = user.description or name;
extraGroups = user.extraGroups or [ ];
}
// lib.optionalAttrs (user ? hashedPasswordFile && user.hashedPasswordFile != null) {
hashedPasswordFile = user.hashedPasswordFile;
};
hmWiredNames =
selectedUsers:
lib.attrNames (
lib.filterAttrs (
_: user:
let
hm = userHm user;
in
(hm.enable or false) && (hm.module or null) != null
) selectedUsers
);
rbwOutboundSnippet =
name: user:
let
outboundCfg = ((userSsh user).outbound or { }).rbw or { };
in
lib.mkIf (outboundCfg.enable or false) {
chiasson.ssh.outbound.rbw.enable = true;
chiasson.ssh.outbound.rbw.user = name;
chiasson.ssh.outbound.rbw.hosts =
if (outboundCfg.hosts or "all") == "all" then [ "all" ] else outboundCfg.hosts;
chiasson.ssh.outbound.rbw.extraIdentities = outboundCfg.extraIdentities or [ ];
};
mkHmUserModule =
{ name, user, hostExtraModules }:
let
hm = userHm user;
hmModule = resolveHomeManagerModule hm.module;
in
lib.mkMerge (
[
hmModule
(rbwOutboundSnippet name user)
]
++ (hm.extraModules or [ ])
++ hostExtraModules
);
inboundAuthorizedOrNull =
user:
let
inboundCfg = (userSsh user).inbound or { };
in
if !(inboundCfg.enable or false) then
null
else if (inboundCfg.authorizedHosts or "all") == "all" then
"all"
else
inboundCfg.authorizedHosts;
inboundHostsAttr =
selectedUsers:
lib.pipe selectedUsers [
(lib.mapAttrs (_: inboundAuthorizedOrNull))
(lib.filterAttrs (_: v: v != null))
];
};
inventory = self.lib.sshInventory;
olivierEnabled = lib.elem "olivier" cfg.enabled;
# Merge catalog + per-host overrides into the final user attrs.
selectedUsers = lib.listToAttrs (map
(name: {
selectedUsers = lib.listToAttrs (
map (name: {
inherit name;
value = lib.recursiveUpdate cfg.catalog.${name} (cfg.hostOverrides.${name} or { });
})
cfg.enabled);
}) cfg.enabled
);
names = usersLib.hmWiredNames selectedUsers;
missing = usersLib.missingEnabledNames cfg.catalog cfg.enabled;
@@ -27,26 +124,27 @@
hmAvailable = lib.hasAttrByPath [ "home-manager" "users" ] options;
inboundUsersAttr = usersLib.inboundHostsAttr selectedUsers;
hmUsersAttr = lib.listToAttrs (map
(name: {
hmUsersAttr = lib.listToAttrs (
map (name: {
inherit name;
value = usersLib.mkHmUserModule {
inherit name;
user = selectedUsers.${name};
hostExtraModules = cfg.extraModules.${name} or [ ];
};
})
names);
}) names
);
# Fish shell wiring: HM's fish module declares its package, but
# /etc/passwd + /etc/shells need updating so login shells work.
# Only fires for HM users that actually enable fish.
hmFishUsers =
if !hmAvailable then { }
if !hmAvailable then
{ }
else
lib.filterAttrs
(name: hmUser: (hmUser.programs.fish.enable or false) && builtins.elem name names)
config.home-manager.users;
lib.filterAttrs (
name: hmUser: (hmUser.programs.fish.enable or false) && builtins.elem name names
) config.home-manager.users;
in
{
imports = [
@@ -108,12 +206,18 @@
isNormalUser = true;
description = "Olivier";
extraGroups = [
"networkmanager" "wheel" "docker" "fuse" "uinput" "kvm"
"networkmanager"
"wheel"
"docker"
"fuse"
"uinput"
"kvm"
# `video` lets brightnessctl/light udev rules own /sys/backlight without sudo.
# Harmless on headless hosts (no devices).
"video"
# DRI render + input for gamescope / Steam on Wayland, no sudo.
"render" "input"
"render"
"input"
];
hashedPasswordFile = lib.mkIf olivierEnabled (
config.sops.secrets."users/olivier/hashedPassword".path
@@ -147,14 +251,20 @@
# `.pub` filter for each of these and emits a `Match user`
# block, so `ssh server@r5500` / `ssh builder@nix-server`
# route the right private key from the rbw agent.
ssh.outbound.rbw.extraIdentities = [ "server" "builder" ];
ssh.outbound.rbw.extraIdentities = [
"server"
"builder"
];
};
server = {
isNormalUser = true;
description = "Server user";
extraGroups = [ "wheel" ];
homeManager = { enable = false; module = null; };
homeManager = {
enable = false;
module = null;
};
ssh.inbound.enable = true;
ssh.inbound.authorizedHosts = "all";
ssh.outbound.rbw.enable = false;
@@ -166,7 +276,10 @@
description = "Navi fleet deploy (push + activate only)";
extraGroups = [ ];
createHome = false;
homeManager = { enable = false; module = null; };
homeManager = {
enable = false;
module = null;
};
ssh.inbound.enable = true;
ssh.inbound.authorizedHosts = "all";
};
@@ -195,9 +308,7 @@
];
# NixOS user accounts (no HM, raw).
users.users = lib.mapAttrs
(name: user: usersLib.mkNixosUser name user)
selectedUsers;
users.users = lib.mapAttrs (name: user: usersLib.mkNixosUser name user) selectedUsers;
}
(lib.optionalAttrs hmAvailable {
"home-manager".useGlobalPkgs = lib.mkIf (cfg.homeManager.autoWire && names != [ ]) true;
@@ -216,12 +327,12 @@
# /etc/shells with `mkForce`. `mkNixosUser` doesn't emit `shell`, so
# the force is uncontested.
(lib.mkIf (hmFishUsers != { }) {
environment.shells = lib.mkAfter (lib.mapAttrsToList
(_: hmUser: lib.getExe hmUser.programs.fish.package)
hmFishUsers);
users.users = lib.mapAttrs
(name: hmUser: { shell = lib.mkForce (lib.getExe hmUser.programs.fish.package); })
hmFishUsers;
environment.shells = lib.mkAfter (
lib.mapAttrsToList (_: hmUser: lib.getExe hmUser.programs.fish.package) hmFishUsers
);
users.users = lib.mapAttrs (name: hmUser: {
shell = lib.mkForce (lib.getExe hmUser.programs.fish.package);
}) hmFishUsers;
})
];
};