{ config, lib, ... }: let mkHost = { name, displays ? { }, input ? { }, sourceControl ? { }, users ? { }, imports ? [ ], stateVersion ? "24.05", }: { config, pkgs, ... }: let hostUsers = lib.mapAttrs (_: spec: spec.account) users; userAssertions = lib.flatten ( lib.mapAttrsToList (userName: spec: [ { assertion = userName == spec.account.name; message = "Host `${name}` declares user `${userName}` with mismatched account name `${spec.account.name}`."; } { assertion = builtins.isList spec.homeImports; message = "Host `${name}` user `${userName}` must define `homeImports` as a list."; } ]) users ); passwordUserSpecs = lib.filterAttrs (_: spec: spec.needsPassword or false) users; in { assertions = userAssertions; meta.host = { inherit displays input name sourceControl ; users = hostUsers; }; inherit imports; networking.hostName = name; system.stateVersion = stateVersion; programs.zsh.enable = true; sops.secrets = lib.mapAttrs' ( userName: _: lib.nameValuePair "hashed-password-${userName}" { neededForUsers = true; } ) passwordUserSpecs; users.users = lib.mapAttrs ( userName: spec: { name = spec.account.name; home = spec.account.homeDirectory; isNormalUser = true; shell = pkgs.zsh; extraGroups = [ "wheel" "networkmanager" ]; } // lib.optionalAttrs (spec.needsPassword or false) { hashedPasswordFile = config.sops.secrets."hashed-password-${userName}".path; } ) users; home-manager.users = lib.mapAttrs (_: spec: { imports = spec.homeImports; meta = { host = config.meta.host; user = spec.account; }; home = { username = spec.account.name; homeDirectory = spec.account.homeDirectory; stateVersion = spec.stateVersion or stateVersion; }; }) users; }; mkCaddyReverseProxy = { domain, port, extraHeaders ? [ ], extraConfigText ? "", }: let headerLines = map (header: " header_up ${header.name} ${header.value}") extraHeaders; extraConfigLines = map (line: " ${line}") ( lib.filter (line: line != "") (lib.splitString "\n" extraConfigText) ); bodyLines = headerLines ++ extraConfigLines; body = lib.concatStringsSep "\n" bodyLines; in { services.caddy.virtualHosts.${domain}.extraConfig = if body == "" then "reverse_proxy :${toString port}" else '' reverse_proxy :${toString port} { ${body} } ''; }; resolvePackagePath = { pkgs, path, }: lib.attrByPath path null pkgs; repo = { contact.email = "mail@jelles.net"; services = { actual = { domain = "finance.jelles.net"; host = "127.0.0.1"; port = 3000; url = "https://finance.jelles.net"; }; gitea = { domain = "git.jelles.net"; host = "127.0.0.1"; port = 3001; url = "https://git.jelles.net/"; }; radicale = { domain = "radicale.jelles.net"; host = "127.0.0.1"; port = 5232; url = "https://radicale.jelles.net/"; }; vaultwarden = { domain = "vault.jelles.net"; host = "127.0.0.1"; port = 8100; url = "https://vault.jelles.net"; }; }; theme = { cursor = { name = "phinger-cursors-light"; packagePath = [ "phinger-cursors" ]; size = 24; }; kanagawa = { gtkThemeName = "Kanagawa-BL-LB"; iconThemeName = "Kanagawa"; owner = "Fausto-Korpsvart"; repo = "Kanagawa-GKT-Theme"; rev = "55ca4ba249eba21f861b9866b71ab41bb8930318"; hash = "sha256-UdMoMx2DoovcxSp/zBZ3PRv/Qpj+prd0uPm1gmdak2E="; version = "unstable-2025-10-23"; }; }; }; resolveUserTerminal = { pkgs, user, }: let package = resolvePackagePath { inherit pkgs; path = user.terminalPackagePath; }; hasPackage = package != null; hasMainProgram = hasPackage && package ? meta.mainProgram; mainProgram = if hasMainProgram then package.meta.mainProgram else null; desktopId = if mainProgram == null then null else "${mainProgram}.desktop"; in { inherit desktopId hasMainProgram hasPackage mainProgram package ; hasDesktopEntry = desktopId != null && builtins.pathExists "${package}/share/applications/${desktopId}"; hasTerminfo = hasPackage && lib.elem "terminfo" package.outputs; }; mkTerminalAssertions = { terminal, user, requireDesktopEntry ? false, requireKitty ? false, requireMainProgram ? true, requireTerminfo ? false, }: lib.flatten [ [ { assertion = terminal.hasPackage; message = "Unknown terminal package `${lib.showAttrPath user.terminalPackagePath}` for user `${user.name}`."; } ] (lib.optional requireMainProgram { assertion = terminal.hasMainProgram; message = "Terminal package `${lib.showAttrPath user.terminalPackagePath}` must define `meta.mainProgram`."; }) (lib.optional requireDesktopEntry { assertion = terminal.hasDesktopEntry; message = "Terminal package `${lib.showAttrPath user.terminalPackagePath}` must provide `${terminal.desktopId}`."; }) (lib.optional requireKitty { assertion = terminal.hasMainProgram && terminal.mainProgram == "kitty"; message = "The terminal feature currently only supports kitty-specific Home Manager configuration."; }) (lib.optional requireTerminfo { assertion = terminal.hasTerminfo; message = "Terminal package `${lib.showAttrPath user.terminalPackagePath}` must provide a `terminfo` output."; }) ]; mapConfiguredAttrs = { settings, schema, }: lib.filterAttrs (_: value: value != null) ( lib.mapAttrs ( _: spec: let value = lib.getAttrFromPath spec.path settings; in if value == null then null else if spec ? values then spec.values.${value} else if spec ? transform then spec.transform value else value ) schema ); mkInputProfiles = input: let scrollMethodValues = { edge = { libinput = "edge"; niri = "edge"; }; "no-scroll" = { libinput = "none"; niri = "no-scroll"; }; "on-button-down" = { libinput = "button"; niri = "on-button-down"; }; "two-finger" = { libinput = "twofinger"; niri = "two-finger"; }; }; mouse = input.mouse; touchpad = input.touchpad; libinputMouse = mapConfiguredAttrs { settings = mouse; schema = { accelProfile.path = [ "accelProfile" ]; accelSpeed = { path = [ "accelSpeed" ]; transform = toString; }; leftHanded.path = [ "leftHanded" ]; middleEmulation.path = [ "middleEmulation" ]; naturalScrolling.path = [ "naturalScrolling" ]; scrollMethod = { path = [ "scrollMethod" ]; values = lib.mapAttrs (_: value: value.libinput) scrollMethodValues; }; }; }; libinputTouchpad = mapConfiguredAttrs { settings = touchpad; schema = { accelProfile.path = [ "accelProfile" ]; accelSpeed = { path = [ "accelSpeed" ]; transform = toString; }; clickMethod = { path = [ "clickMethod" ]; values = { "button-areas" = "buttonareas"; clickfinger = "clickfinger"; }; }; disableWhileTyping.path = [ "disableWhileTyping" ]; leftHanded.path = [ "leftHanded" ]; middleEmulation.path = [ "middleEmulation" ]; naturalScrolling.path = [ "naturalScrolling" ]; scrollMethod = { path = [ "scrollMethod" ]; values = lib.mapAttrs (_: value: value.libinput) scrollMethodValues; }; tapping.path = [ "tapping" ]; }; }; niriMouse = mapConfiguredAttrs { settings = mouse; schema = { "accel-profile".path = [ "accelProfile" ]; "accel-speed".path = [ "accelSpeed" ]; "left-handed".path = [ "leftHanded" ]; "middle-emulation".path = [ "middleEmulation" ]; "natural-scroll".path = [ "naturalScrolling" ]; "scroll-method" = { path = [ "scrollMethod" ]; values = lib.mapAttrs (_: value: value.niri) scrollMethodValues; }; }; }; niriTouchpad = mapConfiguredAttrs { settings = touchpad; schema = { "accel-profile".path = [ "accelProfile" ]; "accel-speed".path = [ "accelSpeed" ]; "click-method".path = [ "clickMethod" ]; dwt.path = [ "disableWhileTyping" ]; "left-handed".path = [ "leftHanded" ]; "middle-emulation".path = [ "middleEmulation" ]; "natural-scroll".path = [ "naturalScrolling" ]; "scroll-method" = { path = [ "scrollMethod" ]; values = lib.mapAttrs (_: value: value.niri) scrollMethodValues; }; tap.path = [ "tapping" ]; }; }; in { hasPointerConfig = libinputMouse != { } || libinputTouchpad != { }; libinput = { mouse = libinputMouse; touchpad = libinputTouchpad; }; niri = { mouse = niriMouse; touchpad = niriTouchpad; }; }; in { options.meta.lib.mkHost = lib.mkOption { type = lib.types.raw; description = "Internal host constructor shared between flake-parts modules."; internal = true; readOnly = true; }; options.meta.lib.mkCaddyReverseProxy = lib.mkOption { type = lib.types.raw; description = "Internal Caddy reverse proxy helper shared between flake-parts modules."; internal = true; readOnly = true; }; options.meta.lib.resolvePackagePath = lib.mkOption { type = lib.types.raw; description = "Internal helper to resolve package attr paths against the local pkgs set."; internal = true; readOnly = true; }; options.meta.lib.resolveUserTerminal = lib.mkOption { type = lib.types.raw; description = "Internal helper to resolve and validate user terminal metadata."; internal = true; readOnly = true; }; options.meta.lib.mkTerminalAssertions = lib.mkOption { type = lib.types.raw; description = "Internal helper for terminal-related assertions shared across modules."; internal = true; readOnly = true; }; options.meta.lib.mkInputProfiles = lib.mkOption { type = lib.types.raw; description = "Internal helper to normalize host input metadata for libinput and Niri."; internal = true; readOnly = true; }; options.meta.lib.users = lib.mkOption { type = lib.types.attrs; description = "Canonical user attrsets shared by host definitions."; internal = true; readOnly = true; }; options.meta.lib.repo = lib.mkOption { type = lib.types.attrs; description = "Internal shared repository metadata."; internal = true; readOnly = true; }; config.meta.lib = { inherit mkInputProfiles mkCaddyReverseProxy mkTerminalAssertions mkHost repo resolvePackagePath resolveUserTerminal ; }; }