refactor: orion and base

This commit is contained in:
2026-04-24 21:57:43 +02:00
parent 32c12e8797
commit 55fbe82a42
7 changed files with 307 additions and 972 deletions
+2 -150
View File
@@ -1,194 +1,46 @@
{ lib, ... }: { lib, ... }:
let let
userSpecs = { accounts = {
kiri = { kiri = {
realName = "Jelle Spreeuwenberg"; realName = "Jelle Spreeuwenberg";
homeDirectory = "/home/kiri";
emails = { emails = {
personal = { personal = {
address = "mail@jelles.net"; address = "mail@jelles.net";
primary = true; primary = true;
scope = "personal";
type = "mxrouting"; type = "mxrouting";
}; };
old = { old = {
address = "mail@jellespreeuwenberg.nl"; address = "mail@jellespreeuwenberg.nl";
scope = null;
type = "mxrouting"; type = "mxrouting";
}; };
uni = { uni = {
address = "j.spreeuwenberg@student.tue.nl"; address = "j.spreeuwenberg@student.tue.nl";
scope = null;
type = "office365"; type = "office365";
}; };
work = { work = {
address = "jelle.spreeuwenberg@yookr.org"; address = "jelle.spreeuwenberg@yookr.org";
scope = "work";
type = "office365"; type = "office365";
}; };
}; };
sourceControl = { };
}; };
ergon = { ergon = {
realName = "Jelle Spreeuwenberg"; realName = "Jelle Spreeuwenberg";
homeDirectory = "/home/ergon";
emails = { emails = {
personal = { personal = {
address = "mail@jelles.net"; address = "mail@jelles.net";
scope = "personal";
type = "mxrouting"; type = "mxrouting";
}; };
work = { work = {
address = "jelle.spreeuwenberg@yookr.org"; address = "jelle.spreeuwenberg@yookr.org";
primary = true; primary = true;
scope = "work";
type = "office365"; type = "office365";
}; };
}; };
sourceControl.projectScope = "work";
}; };
}; };
accounts = lib.mapAttrs (name: spec: spec // { inherit name; }) userSpecs;
repo = {
contact.email = "mail@jelles.net";
desktop = {
browser = {
command = "vivaldi";
packagePath = [ "vivaldi" ];
};
fileManager = {
command = "nautilus";
packagePath = [ "nautilus" ];
};
terminal = {
command = "kitty";
desktopId = "kitty.desktop";
packagePath = [ "kitty" ];
};
};
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 = {
displayName = "Kanagawa Wave";
name = "kanagawa-wave";
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";
palette = {
background = "#1F1F28";
foreground = "#DCD7BA";
secondaryBackground = "#16161D";
border = "#2A2A37";
selectionBackground = "#2D4F67";
selectionForeground = "#C8C093";
url = "#72A7BC";
cursor = "#C8C093";
muted = "#727169";
accents = {
blue = "#7E9CD8";
green = "#98BB6C";
magenta = "#D27E99";
orange = "#FFA066";
purple = "#957FB8";
red = "#E82424";
yellow = "#E6C384";
cyan = "#7AA89F";
};
niri.border = {
active = "#7E9CD8";
inactive = "#54546D";
urgent = "#E82424";
};
terminal = {
color0 = "#16161D";
color1 = "#C34043";
color2 = "#76946A";
color3 = "#C0A36E";
color4 = "#7E9CD8";
color5 = "#957FB8";
color6 = "#6A9589";
color7 = "#C8C093";
color8 = "#727169";
color9 = "#E82424";
color10 = "#98BB6C";
color11 = "#E6C384";
color12 = "#7FB4CA";
color13 = "#938AA9";
color14 = "#7AA89F";
color15 = "#DCD7BA";
color16 = "#FFA066";
color17 = "#FF5D62";
};
};
};
};
};
in in
{ {
options.meta.lib.repo = lib.mkOption { repo.accounts = accounts;
type = lib.types.attrs;
description = "Internal shared repository metadata.";
internal = true;
readOnly = true;
};
options.meta.lib.accounts = lib.mkOption {
type = lib.types.attrs;
description = "Canonical account attrsets shared by host definitions.";
internal = true;
readOnly = true;
};
config.meta.lib.repo = repo;
config.meta.lib.accounts = accounts;
} }
-386
View File
@@ -1,386 +0,0 @@
{ lib, ... }:
let
nonEmptyStrType = lib.types.addCheck lib.types.str (value: lib.stringLength value > 0);
mkNullableOption =
type:
lib.mkOption {
type = lib.types.nullOr type;
default = null;
};
hasSinglePrimaryEmail =
user: builtins.length (lib.filter (email: email.primary) (builtins.attrValues user.emails)) == 1;
scopeEmailCount =
scope: user:
builtins.length (lib.filter (email: email.scope == scope) (builtins.attrValues user.emails));
hasAtMostOneScopedEmail = scope: user: scopeEmailCount scope user <= 1;
requiredSourceControlScopes =
user:
lib.unique [
"personal"
user.sourceControl.projectScope
];
hasRequiredScopedEmail = scope: user: scopeEmailCount scope user == 1;
scopeEmailAddress =
scope: user:
let
emails = lib.filter (email: email.scope == scope) (builtins.attrValues user.emails);
in
if emails == [ ] then "" else (builtins.head emails).address;
primaryEmailFallback = {
address = "";
primary = false;
scope = null;
type = "mxrouting";
};
emailProviderType = lib.types.enum [
"mxrouting"
"office365"
];
sourceControlScopeType = lib.types.enum [
"personal"
"work"
];
pointerAccelProfileType = lib.types.enum [
"adaptive"
"flat"
];
pointerScrollMethodType = lib.types.enum [
"no-scroll"
"two-finger"
"edge"
"on-button-down"
];
touchpadClickMethodType = lib.types.enum [
"button-areas"
"clickfinger"
];
emailType = lib.types.submodule (
{ ... }:
{
options = {
address = lib.mkOption {
type = lib.types.str;
};
primary = lib.mkOption {
type = lib.types.bool;
default = false;
};
type = lib.mkOption {
type = emailProviderType;
};
scope = mkNullableOption sourceControlScopeType;
};
}
);
sourceControlHostKeyType = lib.types.submodule (
{ ... }:
{
options = {
publicKey = lib.mkOption {
type = lib.types.nullOr nonEmptyStrType;
default = null;
};
privateKeyPath = mkNullableOption nonEmptyStrType;
};
}
);
sourceControlHostUserType = lib.types.submodule (
{ ... }:
{
options = {
personal = mkNullableOption sourceControlHostKeyType;
work = mkNullableOption sourceControlHostKeyType;
};
}
);
hostSourceControlType = lib.types.submodule (
{ ... }:
{
options.users = lib.mkOption {
type = lib.types.attrsOf sourceControlHostUserType;
default = { };
};
}
);
sourceControlType = lib.types.submodule (
{ ... }:
{
options = {
projectScope = lib.mkOption {
type = sourceControlScopeType;
default = "personal";
};
scopes = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule (
{ ... }:
{
options.email = lib.mkOption {
type = lib.types.str;
readOnly = true;
};
}
)
);
readOnly = true;
description = "Derived source-control identities keyed by scope.";
};
};
}
);
userType = lib.types.submodule (
{ config, ... }:
let
primaryEmails = lib.filter (email: email.primary) (builtins.attrValues config.emails);
primaryEmail = if primaryEmails == [ ] then primaryEmailFallback else builtins.head primaryEmails;
in
{
options = {
name = lib.mkOption {
type = lib.types.str;
};
realName = lib.mkOption {
type = lib.types.str;
};
homeDirectory = lib.mkOption {
type = lib.types.str;
};
nixosConfigurationPath = lib.mkOption {
type = lib.types.str;
default = "${config.homeDirectory}/.config/nixos";
};
emails = lib.mkOption {
type = lib.types.attrsOf emailType;
};
primaryEmail = lib.mkOption {
type = emailType;
readOnly = true;
description = "Derived primary email entry for this user.";
};
sourceControl = lib.mkOption {
type = sourceControlType;
default = { };
};
};
config = {
primaryEmail = primaryEmail;
sourceControl.scopes = lib.genAttrs (requiredSourceControlScopes config) (scope: {
email = scopeEmailAddress scope config;
});
};
}
);
displayModeType = lib.types.submodule (
{ ... }:
{
options = {
width = lib.mkOption {
type = lib.types.int;
};
height = lib.mkOption {
type = lib.types.int;
};
refresh = lib.mkOption {
type = lib.types.float;
};
};
}
);
displayType = lib.types.submodule (
{ ... }:
{
options = {
primary = lib.mkOption {
type = lib.types.bool;
default = false;
};
x = lib.mkOption {
type = lib.types.int;
};
y = lib.mkOption {
type = lib.types.int;
};
scale = lib.mkOption {
type = lib.types.nullOr lib.types.float;
default = null;
};
mode = lib.mkOption {
type = lib.types.nullOr displayModeType;
default = null;
};
};
}
);
mouseInputType = lib.types.submodule (
{ ... }:
{
options = {
accelProfile = mkNullableOption (pointerAccelProfileType);
accelSpeed = mkNullableOption lib.types.float;
leftHanded = mkNullableOption lib.types.bool;
middleEmulation = mkNullableOption lib.types.bool;
naturalScrolling = mkNullableOption lib.types.bool;
scrollMethod = mkNullableOption pointerScrollMethodType;
};
}
);
touchpadInputType = lib.types.submodule (
{ ... }:
{
options = {
accelProfile = mkNullableOption (pointerAccelProfileType);
accelSpeed = mkNullableOption lib.types.float;
clickMethod = mkNullableOption touchpadClickMethodType;
disableWhileTyping = mkNullableOption lib.types.bool;
leftHanded = mkNullableOption lib.types.bool;
middleEmulation = mkNullableOption lib.types.bool;
naturalScrolling = mkNullableOption lib.types.bool;
scrollMethod = mkNullableOption pointerScrollMethodType;
tapping = mkNullableOption lib.types.bool;
};
}
);
inputType = lib.types.submodule (
{ ... }:
{
options = {
mouse = lib.mkOption {
type = mouseInputType;
default = { };
};
touchpad = lib.mkOption {
type = touchpadInputType;
default = { };
};
};
}
);
hostType = lib.types.submodule (
{ ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
};
displays = lib.mkOption {
type = lib.types.attrsOf displayType;
default = { };
};
input = lib.mkOption {
type = inputType;
default = { };
};
users = lib.mkOption {
type = lib.types.attrsOf userType;
default = { };
};
sourceControl = lib.mkOption {
type = hostSourceControlType;
default = { };
};
};
}
);
mkUserEmailAssertions =
userName: user:
[
{
assertion = hasSinglePrimaryEmail user;
message = "User `${userName}` must define exactly one primary email entry.";
}
]
++ (map
(scope: {
assertion = hasAtMostOneScopedEmail scope user;
message = "User `${userName}` may define at most one `${scope}` scoped email entry.";
})
[
"personal"
"work"
]
)
++ map (scope: {
assertion = hasRequiredScopedEmail scope user;
message = "User `${userName}` must define exactly one `${scope}` scoped email entry.";
}) (requiredSourceControlScopes user);
in
{
flake.modules.nixos.meta =
{ config, ... }:
{
options.meta.host = lib.mkOption {
type = hostType;
};
config.assertions = lib.flatten (lib.mapAttrsToList mkUserEmailAssertions config.meta.host.users);
};
flake.modules.homeManager.meta =
{ config, ... }:
{
options.meta = {
host = lib.mkOption {
type = lib.types.nullOr hostType;
default = null;
};
user = lib.mkOption {
type = lib.types.nullOr userType;
default = null;
};
};
config.assertions = lib.optionals (config.meta.user != null) (
mkUserEmailAssertions config.meta.user.name config.meta.user
);
};
}
+4 -10
View File
@@ -3,12 +3,6 @@
config, config,
... ...
}: }:
let
hostNames = builtins.attrNames (
inputs.nixpkgs.lib.filterAttrs (_: type: type == "directory") (builtins.readDir ./hosts)
);
nixosModules = config.flake.modules.nixos;
in
{ {
imports = [ imports = [
inputs.flake-parts.flakeModules.modules inputs.flake-parts.flakeModules.modules
@@ -17,13 +11,13 @@ in
systems = [ "x86_64-linux" ]; systems = [ "x86_64-linux" ];
flake.nixosConfigurations = inputs.nixpkgs.lib.genAttrs hostNames ( flake.nixosConfigurations = builtins.mapAttrs (
name: _: machine:
inputs.nixpkgs.lib.nixosSystem { inputs.nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs; }; specialArgs = { inherit inputs; };
modules = [ nixosModules.${name} ]; modules = [ (machine.buildFunction machine) ];
} }
); ) config.repo.machines;
perSystem = perSystem =
{ pkgs, ... }: { pkgs, ... }:
+21 -45
View File
@@ -1,57 +1,27 @@
{ {
inputs, inputs,
config, config,
lib,
... ...
}: }:
let let
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager; hmModules = config.flake.modules.homeManager;
metaLib = config.meta.lib;
in in
{ {
flake.modules.nixos.orion-admin = repo.machines.orion = {
buildFunction = config.repo.helpers.mkHost;
module = nixosModules.orion;
accounts = lib.getAttrs [ "kiri" ] config.repo.accounts;
stateVersion = "24.05";
hmStateVersion = "24.05";
};
flake.modules.nixos.orion =
{ ... }:
{ {
config,
lib,
pkgs,
...
}:
let
terminal = metaLib.resolveRepoTerminal {
inherit pkgs;
};
in
{
assertions = metaLib.mkTerminalAssertions {
inherit terminal;
requireTerminfo = true;
};
users.users.kiri = {
linger = true;
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAU2LydkXRTtNFY7oyX8JQURwXLVhB71DeK8XzrXeFX1 openpgp:0xA490D93A"
];
};
environment.systemPackages = [
]
++ lib.optional terminal.hasTerminfo (lib.getOutput "terminfo" terminal.package);
};
flake.modules.nixos.orion = metaLib.mkHost {
name = "orion";
users = {
kiri = {
account = metaLib.accounts.kiri;
homeImports = [
homeModules.shell
homeModules.git
homeModules.syncthing
];
};
};
imports = [ imports = [
nixosModules.host-base nixosModules.host-base
nixosModules.sops-host-ssh-key nixosModules.sops-host-ssh-key
@@ -59,7 +29,6 @@ in
nixosModules.caddy nixosModules.caddy
nixosModules.server-firewall nixosModules.server-firewall
nixosModules.ssh-agent-auth nixosModules.ssh-agent-auth
nixosModules.orion-admin
nixosModules.vaultwarden nixosModules.vaultwarden
nixosModules.radicale nixosModules.radicale
nixosModules.actual nixosModules.actual
@@ -67,5 +36,12 @@ in
./_hardware.nix ./_hardware.nix
./_disk.nix ./_disk.nix
]; ];
users.users.kiri = {
linger = true;
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAU2LydkXRTtNFY7oyX8JQURwXLVhB71DeK8XzrXeFX1 openpgp:0xA490D93A"
];
};
}; };
} }
-374
View File
@@ -1,374 +0,0 @@
{
config,
lib,
...
}:
let
normalizeHostUser =
spec:
{
homeImports = [ ];
needsPassword = false;
stateVersion = null;
}
// spec;
mkHost =
{
name,
displays ? { },
input ? { },
sourceControl ? { },
users ? { },
imports ? [ ],
stateVersion ? "24.05",
}:
{
config,
pkgs,
...
}:
let
hostUserSpecs = lib.mapAttrs (_: spec: normalizeHostUser spec) users;
hostUsers = lib.mapAttrs (_: spec: spec.account) hostUserSpecs;
passwordUserSpecs = lib.filterAttrs (_: spec: spec.needsPassword) hostUserSpecs;
in
{
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 = userName;
home = spec.account.homeDirectory;
isNormalUser = true;
shell = pkgs.zsh;
extraGroups = [
"wheel"
"networkmanager"
];
}
// lib.optionalAttrs spec.needsPassword {
hashedPasswordFile = config.sops.secrets."hashed-password-${userName}".path;
}
) hostUserSpecs;
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 = if spec.stateVersion == null then stateVersion else spec.stateVersion;
};
}) hostUserSpecs;
};
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;
resolveRepoTerminal =
{
pkgs,
}:
let
terminal = config.meta.lib.repo.desktop.terminal;
package = resolvePackagePath {
inherit pkgs;
path = terminal.packagePath;
};
in
{
inherit
package
;
inherit (terminal)
command
desktopId
packagePath
;
hasPackage = package != null;
hasDesktopEntry =
package != null && builtins.pathExists "${package}/share/applications/${terminal.desktopId}";
hasTerminfo = package != null && lib.elem "terminfo" package.outputs;
};
mkTerminalAssertions =
{
terminal,
requireDesktopEntry ? false,
requireTerminfo ? false,
}:
lib.flatten [
[
{
assertion = terminal.hasPackage;
message = "Unknown terminal package `${lib.showAttrPath terminal.packagePath}` in `meta.lib.repo.desktop.terminal`.";
}
{
assertion = terminal.command == "kitty";
message = "The repo terminal is currently expected to be kitty.";
}
]
(lib.optional requireDesktopEntry {
assertion = terminal.hasDesktopEntry;
message = "Terminal package `${lib.showAttrPath terminal.packagePath}` must provide `${terminal.desktopId}`.";
})
(lib.optional requireTerminfo {
assertion = terminal.hasTerminfo;
message = "Terminal package `${lib.showAttrPath terminal.packagePath}` 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.resolveRepoTerminal = lib.mkOption {
type = lib.types.raw;
description = "Internal helper to resolve and validate the repo-standard terminal.";
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;
};
config.meta.lib = {
inherit
mkInputProfiles
mkCaddyReverseProxy
mkTerminalAssertions
mkHost
resolvePackagePath
resolveRepoTerminal
;
};
}
+72
View File
@@ -0,0 +1,72 @@
{ lib, config, ... }:
let
nixosModules = config.flake.modules.nixos;
hmModules = config.flake.modules.homeManager;
mkHost =
machine:
{ config, pkgs, ... }:
{
imports = [
nixosModules.host-base
nixosModules.meta
machine.module
];
meta.machine = machine;
networking.hostName = machine.name;
system.stateVersion = machine.stateVersion;
# TODO: Move this
programs.zsh.enable = true;
users.users = lib.mapAttrs (
_: account: {
inherit (account) name extraGroups;
isNormalUser = true;
home = account.homeDirectory;
# TODO: Move this
shell = pkgs.zsh;
}
);
home-manager.users = lib.mapAttrs (
_: account: {
imports = [
hmModules.meta
account.baseModule
];
meta = {
inherit machine account;
};
home.homeDirectory = account.homeDirectory;
home.stateVersion = machine.hmStateVersion;
}
);
};
mkWorkstationHost =
machine:
{ ... }:
{
imports = [
(mkHost machine)
nixosModules.workstation-base
];
home-manager.users = lib.mapAttrs (
_: account: {
imports = [ account.workstationModule ];
}
);
};
in
{
options.repo.helpers = lib.mkOption {
type = lib.types.attrsOf lib.types.raw;
internal = true;
readOnly = true;
};
config.repo.helpers = { inherit mkHost mkWorkstationHost; };
}
+201
View File
@@ -0,0 +1,201 @@
{ lib, ... }:
let
# Account types
emailProviderType = lib.types.enum [
"mxrouting"
"office365"
];
emailType = lib.types.submodule (
{ ... }:
{
options = {
address = lib.mkOption {
type = lib.types.str;
};
primary = lib.mkOption {
type = lib.types.bool;
default = false;
};
type = lib.mkOption {
type = emailProviderType;
};
};
}
);
accountType = lib.types.submodule (
{ name, config, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
readOnly = true;
};
realName = lib.mkOption {
type = lib.types.str;
};
homeDirectory = lib.mkOption {
type = lib.types.str;
default = "/home/${config.name}";
};
nixosConfigurationPath = lib.mkOption {
type = lib.types.str;
default = "${config.homeDirectory}/.config/nixos";
};
emails = lib.mkOption {
type = lib.types.attrsOf emailType;
default = { };
};
extraGroups = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
baseModule = lib.mkOption {
type = lib.types.deferredModule;
default = { };
};
workstationModule = lib.mkOption {
type = lib.types.deferredModule;
default = { };
};
primaryEmail = lib.mkOption {
type = lib.types.nullOr emailType;
readOnly = true;
description = "Derived primary email entry for this user.";
default =
let
emails = builtins.attrValues config.emails;
in
lib.findFirst (email: email.primary) null emails;
};
};
}
);
# Machine types
displayType = lib.types.submodule (
{ ... }:
{
options = {
primary = lib.mkOption {
type = lib.types.bool;
default = false;
};
x = lib.mkOption {
type = lib.types.int;
default = 0;
};
y = lib.mkOption {
type = lib.types.int;
default = 0;
};
scale = lib.mkOption {
type = lib.types.nullOr lib.types.float;
default = 1.0;
};
width = lib.mkOption {
type = lib.types.int;
};
height = lib.mkOption {
type = lib.types.int;
};
refresh = lib.mkOption {
type = lib.types.float;
};
};
}
);
machineType = lib.types.submodule (
{ name, config, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
readOnly = true;
};
module = lib.mkOption {
type = lib.types.deferredModule;
default = { };
};
buildFunction = lib.mkOption {
type = lib.types.functionTo lib.types.deferredModule;
};
stateVersion = lib.mkOption {
type = lib.types.str;
};
hmStateVersion = lib.mkOption {
type = lib.types.str;
default = config.stateVersion;
};
displays = lib.mkOption {
type = lib.types.attrsOf displayType;
default = { };
};
accounts = lib.mkOption {
type = lib.types.attrsOf accountType;
default = { };
};
};
}
);
in
{
options.repo = {
accounts = lib.mkOption {
type = lib.types.attrsOf accountType;
default = { };
};
machines = lib.mkOption {
type = lib.types.attrsOf machineType;
default = { };
};
};
config.flake.modules.nixos.meta =
{ config, ... }:
{
options.meta.machine = lib.mkOption {
type = machineType;
};
};
config.flake.modules.homeManager.meta =
{ config, ... }:
{
options.meta = {
machine = lib.mkOption {
type = machineType;
};
account = lib.mkOption {
type = accountType;
};
};
};
}