Compare commits

..

3 Commits

31 changed files with 662 additions and 535 deletions
+3 -3
View File
@@ -35,8 +35,8 @@ In this repo, `flake.nix` imports `./modules` recursively via `inputs.import-tre
- `modules/hosts/<name>/default.nix`: host features that assemble NixOS aspects into `flake.modules.nixos.<name>`. - `modules/hosts/<name>/default.nix`: host features that assemble NixOS aspects into `flake.modules.nixos.<name>`.
- `modules/secrets/`: secret-related features shared by hosts. - `modules/secrets/`: secret-related features shared by hosts.
- `modules/flake-parts.nix`: flake-parts entrypoint; defines systems, formatter, and `flake.nixosConfigurations`. - `modules/flake-parts.nix`: flake-parts entrypoint; defines systems, formatter, and `flake.nixosConfigurations`.
- `modules/lib.nix`: shared constructors in `config.meta.lib`, especially `mkHost`, `mkHostUser`, and `mkCaddyReverseProxy`. - `modules/lib.nix`: shared constructors and helpers in `config.meta.lib`, especially `mkHost` and `mkCaddyReverseProxy`.
- `modules/users.nix`: canonical user attrsets exposed through `meta.lib.users`. - `modules/data.nix`: canonical shared repo data and account attrsets exposed through `meta.lib.repo` and `meta.lib.accounts`.
- `modules/features/meta.nix`: shared metadata schema for `meta.host` and `meta.user`. - `modules/features/meta.nix`: shared metadata schema for `meta.host` and `meta.user`.
## How Features Are Applied Here ## How Features Are Applied Here
@@ -45,7 +45,7 @@ In this repo, `flake.nix` imports `./modules` recursively via `inputs.import-tre
- Reusable Home Manager concerns are published as `flake.modules.homeManager.<name>`. - Reusable Home Manager concerns are published as `flake.modules.homeManager.<name>`.
- Hosts are aspects too. `orion`, `polaris`, and `zenith` are `nixos` aspects assembled from smaller aspects. - Hosts are aspects too. `orion`, `polaris`, and `zenith` are `nixos` aspects assembled from smaller aspects.
- Host modules should use `config.meta.lib.mkHost` to define `meta.host`, base imports, hostname, and state version. - Host modules should use `config.meta.lib.mkHost` to define `meta.host`, base imports, hostname, and state version.
- Per-user Home Manager composition should use `config.meta.lib.mkHostUser` so `meta.host` and `meta.user` stay available to Home Manager features. - Per-host user declarations should stay inline under `users.<name>` using canonical accounts from `meta.lib.accounts`, so host-local defaults stay close to the host and `mkHost` can wire `meta.host` and `meta.user` into Home Manager consistently.
- Features may rely on the `meta` contract. Existing modules already read `config.meta.host`, `config.meta.user`, and `config.meta.lib`. - Features may rely on the `meta` contract. Existing modules already read `config.meta.host`, `config.meta.user`, and `config.meta.lib`.
## Preferred Aspect Patterns ## Preferred Aspect Patterns
+194
View File
@@ -0,0 +1,194 @@
{ lib, ... }:
let
userSpecs = {
kiri = {
realName = "Jelle Spreeuwenberg";
homeDirectory = "/home/kiri";
emails = {
personal = {
address = "mail@jelles.net";
primary = true;
scope = "personal";
type = "mxrouting";
};
old = {
address = "mail@jellespreeuwenberg.nl";
scope = null;
type = "mxrouting";
};
uni = {
address = "j.spreeuwenberg@student.tue.nl";
scope = null;
type = "office365";
};
work = {
address = "jelle.spreeuwenberg@yookr.org";
scope = "work";
type = "office365";
};
};
sourceControl = { };
};
ergon = {
realName = "Jelle Spreeuwenberg";
homeDirectory = "/home/ergon";
emails = {
personal = {
address = "mail@jelles.net";
scope = "personal";
type = "mxrouting";
};
work = {
address = "jelle.spreeuwenberg@yookr.org";
primary = true;
scope = "work";
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
{
options.meta.lib.repo = lib.mkOption {
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;
}
+2 -2
View File
@@ -3,7 +3,7 @@
... ...
}: }:
let let
metaLib = config.meta.lib; metaRepo = config.meta.lib.repo;
in in
{ {
flake.modules.homeManager.bitwarden = flake.modules.homeManager.bitwarden =
@@ -19,7 +19,7 @@ in
programs.rbw = { programs.rbw = {
enable = true; enable = true;
settings = { settings = {
base_url = metaLib.repo.services.vaultwarden.url; base_url = metaRepo.services.vaultwarden.url;
email = user.primaryEmail.address; email = user.primaryEmail.address;
pinentry = pkgs.pinentry-gnome3; pinentry = pkgs.pinentry-gnome3;
}; };
-4
View File
@@ -3,12 +3,10 @@
flake.modules.homeManager.git = flake.modules.homeManager.git =
{ {
config, config,
lib,
... ...
}: }:
let let
user = config.meta.user; user = config.meta.user;
usesScopedIdentity = user != null && user.sourceControl.profiles != { };
in in
{ {
programs.git = { programs.git = {
@@ -20,8 +18,6 @@
]; ];
settings = { settings = {
init.defaultBranch = "main"; init.defaultBranch = "main";
}
// lib.optionalAttrs (!usesScopedIdentity) {
user = { user = {
name = user.realName; name = user.realName;
email = user.primaryEmail.address; email = user.primaryEmail.address;
+16 -4
View File
@@ -1,12 +1,23 @@
{ config, ... }:
let
metaRepo = config.meta.lib.repo;
in
{ {
flake.modules.homeManager.local-apps = flake.modules.homeManager.local-apps =
{ pkgs, ... }: { pkgs, ... }:
let
browserPackage = config.meta.lib.resolvePackagePath {
inherit pkgs;
path = metaRepo.desktop.browser.packagePath;
};
in
{ {
home.sessionVariables.BROWSER = "vivaldi"; home.sessionVariables.BROWSER = metaRepo.desktop.browser.command;
home.packages = with pkgs; [ home.packages =
with pkgs;
[
brave brave
vivaldi
postman postman
spotify spotify
calcure calcure
@@ -14,7 +25,8 @@
unzip unzip
gimp gimp
dbeaver-bin dbeaver-bin
]; ]
++ [ browserPackage ];
programs.imv.enable = true; programs.imv.enable = true;
programs.sioyek.enable = true; programs.sioyek.enable = true;
+87 -32
View File
@@ -1,5 +1,7 @@
{ lib, ... }: { lib, ... }:
let let
nonEmptyStrType = lib.types.addCheck lib.types.str (value: lib.stringLength value > 0);
mkNullableOption = mkNullableOption =
type: type:
lib.mkOption { lib.mkOption {
@@ -10,12 +12,40 @@ let
hasSinglePrimaryEmail = hasSinglePrimaryEmail =
user: builtins.length (lib.filter (email: email.primary) (builtins.attrValues user.emails)) == 1; 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 = { primaryEmailFallback = {
address = ""; address = "";
primary = false; primary = false;
type = ""; scope = null;
type = "mxrouting";
}; };
emailProviderType = lib.types.enum [
"mxrouting"
"office365"
];
sourceControlScopeType = lib.types.enum [ sourceControlScopeType = lib.types.enum [
"personal" "personal"
"work" "work"
@@ -48,11 +78,14 @@ let
primary = lib.mkOption { primary = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false;
}; };
type = lib.mkOption { type = lib.mkOption {
type = lib.types.str; type = emailProviderType;
}; };
scope = mkNullableOption sourceControlScopeType;
}; };
} }
); );
@@ -62,13 +95,11 @@ let
{ {
options = { options = {
publicKey = lib.mkOption { publicKey = lib.mkOption {
type = lib.types.str; type = lib.types.nullOr nonEmptyStrType;
};
privateKeyPath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null; default = null;
}; };
privateKeyPath = mkNullableOption nonEmptyStrType;
}; };
} }
); );
@@ -93,26 +124,30 @@ let
} }
); );
sourceControlProfileType = lib.types.submodule (
{ ... }:
{
options = { };
}
);
sourceControlType = lib.types.submodule ( sourceControlType = lib.types.submodule (
{ ... }: { ... }:
{ {
options = { options = {
profiles = lib.mkOption {
type = lib.types.attrsOf sourceControlProfileType;
default = { };
};
projectScope = lib.mkOption { projectScope = lib.mkOption {
type = sourceControlScopeType; type = sourceControlScopeType;
default = "personal"; 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.";
};
}; };
} }
); );
@@ -137,10 +172,6 @@ let
type = lib.types.str; type = lib.types.str;
}; };
terminalPackagePath = lib.mkOption {
type = lib.types.listOf lib.types.str;
};
nixosConfigurationPath = lib.mkOption { nixosConfigurationPath = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "${config.homeDirectory}/.config/nixos"; default = "${config.homeDirectory}/.config/nixos";
@@ -162,7 +193,12 @@ let
}; };
}; };
config.primaryEmail = primaryEmail; config = {
primaryEmail = primaryEmail;
sourceControl.scopes = lib.genAttrs (requiredSourceControlScopes config) (scope: {
email = scopeEmailAddress scope config;
});
};
} }
); );
@@ -293,6 +329,29 @@ let
}; };
} }
); );
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 in
{ {
flake.modules.nixos.meta = flake.modules.nixos.meta =
@@ -302,10 +361,7 @@ in
type = hostType; type = hostType;
}; };
config.assertions = lib.mapAttrsToList (userName: user: { config.assertions = lib.flatten (lib.mapAttrsToList mkUserEmailAssertions config.meta.host.users);
assertion = hasSinglePrimaryEmail user;
message = "User `${userName}` must define exactly one primary email entry.";
}) config.meta.host.users;
}; };
flake.modules.homeManager.meta = flake.modules.homeManager.meta =
@@ -323,9 +379,8 @@ in
}; };
}; };
config.assertions = lib.optional (config.meta.user != null) { config.assertions = lib.optionals (config.meta.user != null) (
assertion = hasSinglePrimaryEmail config.meta.user; mkUserEmailAssertions config.meta.user.name config.meta.user
message = "User `${config.meta.user.name}` must define exactly one primary email entry."; );
};
}; };
} }
@@ -0,0 +1,45 @@
{ themeName }:
''
require("kanagawa").setup({
colors = {
theme = {
all = {
ui = {
bg_gutter = "none"
}
}
}
},
overrides = function(colors)
local theme = colors.theme
local makeDiagnosticColor = function(color)
local c = require("kanagawa.lib.color")
return { fg = color, bg = c(color):blend(theme.ui.bg, 0.95):to_hex() }
end
return {
TelescopeTitle = { fg = theme.ui.special, bold = true },
TelescopePromptNormal = { bg = theme.ui.bg_p1 },
TelescopePromptBorder = { fg = theme.ui.bg_p1, bg = theme.ui.bg_p1 },
TelescopeResultsNormal = { fg = theme.ui.fg_dim, bg = theme.ui.bg_m1 },
TelescopeResultsBorder = { fg = theme.ui.bg_m1, bg = theme.ui.bg_m1 },
TelescopePreviewNormal = { bg = theme.ui.bg_dim },
TelescopePreviewBorder = { bg = theme.ui.bg_dim, fg = theme.ui.bg_dim },
Pmenu = { fg = theme.ui.shade0, bg = theme.ui.bg_p1 }, -- add `blend = vim.o.pumblend` to enable transparency
PmenuSel = { fg = "NONE", bg = theme.ui.bg_p2 },
PmenuSbar = { bg = theme.ui.bg_m1 },
PmenuThumb = { bg = theme.ui.bg_p2 },
DiagnosticVirtualTextHint = makeDiagnosticColor(theme.diag.hint),
DiagnosticVirtualTextInfo = makeDiagnosticColor(theme.diag.info),
DiagnosticVirtualTextWarn = makeDiagnosticColor(theme.diag.warning),
DiagnosticVirtualTextError = makeDiagnosticColor(theme.diag.error),
}
end,
})
vim.cmd.colorscheme("${themeName}")
''
+9 -48
View File
@@ -1,3 +1,7 @@
{ config, ... }:
let
repoTheme = config.meta.lib.repo.theme.kanagawa;
in
{ {
flake.modules.homeManager.neovim = flake.modules.homeManager.neovim =
{ {
@@ -109,56 +113,13 @@
# Hostname/ConfigDir needed for nixd # Hostname/ConfigDir needed for nixd
nixdExtras = { nixdExtras = {
nixpkgs = "import ${pkgs.path} {}"; nixpkgs = "import ${pkgs.path} {}";
nixos_options = ''(builtins.getFlake "path://${config.home.homeDirectory}/.config/nixos").nixosConfigurations.${config.meta.host.name}.options''; nixos_options = ''(builtins.getFlake "path://${config.meta.user.nixosConfigurationPath}").nixosConfigurations.${config.meta.host.name}.options'';
home_manager_options = ''(builtins.getFlake "path://${config.home.homeDirectory}/.config/nixos").nixosConfigurations.${config.meta.host.name}.options.home-manager.users.type.getSubOptions []''; home_manager_options = ''(builtins.getFlake "path://${config.meta.user.nixosConfigurationPath}").nixosConfigurations.${config.meta.host.name}.options.home-manager.users.type.getSubOptions []'';
}; };
# TODO: Put in separate theme file themeSetup = import ./_kanagawa-theme.nix {
themeSetup = # lua themeName = repoTheme.name;
'' };
require("kanagawa").setup({
colors = {
theme = {
all = {
ui = {
bg_gutter = "none"
}
}
}
},
overrides = function(colors)
local theme = colors.theme
local makeDiagnosticColor = function(color)
local c = require("kanagawa.lib.color")
return { fg = color, bg = c(color):blend(theme.ui.bg, 0.95):to_hex() }
end
return {
TelescopeTitle = { fg = theme.ui.special, bold = true },
TelescopePromptNormal = { bg = theme.ui.bg_p1 },
TelescopePromptBorder = { fg = theme.ui.bg_p1, bg = theme.ui.bg_p1 },
TelescopeResultsNormal = { fg = theme.ui.fg_dim, bg = theme.ui.bg_m1 },
TelescopeResultsBorder = { fg = theme.ui.bg_m1, bg = theme.ui.bg_m1 },
TelescopePreviewNormal = { bg = theme.ui.bg_dim },
TelescopePreviewBorder = { bg = theme.ui.bg_dim, fg = theme.ui.bg_dim },
Pmenu = { fg = theme.ui.shade0, bg = theme.ui.bg_p1 }, -- add `blend = vim.o.pumblend` to enable transparency
PmenuSel = { fg = "NONE", bg = theme.ui.bg_p2 },
PmenuSbar = { bg = theme.ui.bg_m1 },
PmenuThumb = { bg = theme.ui.bg_p2 },
DiagnosticVirtualTextHint = makeDiagnosticColor(theme.diag.hint),
DiagnosticVirtualTextInfo = makeDiagnosticColor(theme.diag.info),
DiagnosticVirtualTextWarn = makeDiagnosticColor(theme.diag.warning),
DiagnosticVirtualTextError = makeDiagnosticColor(theme.diag.error),
}
end,
})
vim.cmd.colorscheme("kanagawa-wave")
'';
}; };
-1
View File
@@ -3,7 +3,6 @@
networking = { networking = {
firewall.enable = true; firewall.enable = true;
firewall.allowPing = false; firewall.allowPing = false;
nftables.enable = true;
}; };
}; };
+2 -1
View File
@@ -1,4 +1,5 @@
{ {
browserCommand,
lib, lib,
terminalPackage, terminalPackage,
}: }:
@@ -8,7 +9,7 @@
hotkey-overlay.title = "Terminal"; hotkey-overlay.title = "Terminal";
}; };
"Mod+B" = { "Mod+B" = {
action.spawn = "vivaldi"; action.spawn = browserCommand;
hotkey-overlay.title = "Browser"; hotkey-overlay.title = "Browser";
}; };
"Mod+Space" = { "Mod+Space" = {
+20 -13
View File
@@ -5,6 +5,7 @@
}: }:
let let
metaLib = config.meta.lib; metaLib = config.meta.lib;
metaRepo = metaLib.repo;
in in
{ {
flake.modules.nixos.niri = flake.modules.nixos.niri =
@@ -30,6 +31,12 @@ in
... ...
}: }:
let let
repoTheme = metaRepo.theme.kanagawa;
browserCommand = metaRepo.desktop.browser.command;
fileManagerPackage = metaLib.resolvePackagePath {
inherit pkgs;
path = metaRepo.desktop.fileManager.packagePath;
};
outputs = lib.mapAttrs ( outputs = lib.mapAttrs (
_: display: _: display:
{ {
@@ -41,23 +48,21 @@ in
// lib.optionalAttrs (display.primary or false) { // lib.optionalAttrs (display.primary or false) {
"focus-at-startup" = true; "focus-at-startup" = true;
} }
// lib.optionalAttrs (display ? scale) { // lib.optionalAttrs (display.scale != null) {
inherit (display) scale; inherit (display) scale;
} }
// lib.optionalAttrs (display ? mode) { // lib.optionalAttrs (display.mode != null) {
inherit (display) mode; inherit (display) mode;
} }
) config.meta.host.displays; ) config.meta.host.displays;
inputProfiles = metaLib.mkInputProfiles config.meta.host.input; inputProfiles = metaLib.mkInputProfiles config.meta.host.input;
terminal = metaLib.resolveUserTerminal { terminal = metaLib.resolveRepoTerminal {
inherit pkgs; inherit pkgs;
user = config.meta.user;
}; };
in in
{ {
assertions = metaLib.mkTerminalAssertions { assertions = metaLib.mkTerminalAssertions {
inherit terminal; inherit terminal;
user = config.meta.user;
}; };
home.sessionVariables.NIXOS_OZONE_WL = "1"; home.sessionVariables.NIXOS_OZONE_WL = "1";
@@ -68,12 +73,14 @@ in
}; };
}; };
home.packages = with pkgs; [ home.packages =
with pkgs;
[
playerctl playerctl
nautilus
brightnessctl brightnessctl
xwayland-satellite xwayland-satellite
]; ]
++ [ fileManagerPackage ];
programs.niri.settings = { programs.niri.settings = {
inherit outputs; inherit outputs;
@@ -81,7 +88,6 @@ in
spawn-at-startup = [ spawn-at-startup = [
{ command = [ "xwayland-satellite" ]; } { command = [ "xwayland-satellite" ]; }
{ command = [ "noctalia-shell" ]; } { command = [ "noctalia-shell" ]; }
{ command = [ "qbittorrent" ]; }
]; ];
prefer-no-csd = true; prefer-no-csd = true;
hotkey-overlay.skip-at-startup = true; hotkey-overlay.skip-at-startup = true;
@@ -106,9 +112,9 @@ in
border = { border = {
enable = true; enable = true;
width = 3; width = 3;
active.color = "#7E9CD8"; active.color = repoTheme.palette.niri.border.active;
inactive.color = "#54546D"; inactive.color = repoTheme.palette.niri.border.inactive;
urgent.color = "#E82424"; urgent.color = repoTheme.palette.niri.border.urgent;
}; };
}; };
@@ -146,9 +152,10 @@ in
}; };
binds = binds =
if terminal.hasMainProgram then if terminal.hasPackage then
import ./_bindings.nix { import ./_bindings.nix {
inherit inherit
browserCommand
lib lib
; ;
terminalPackage = terminal.package; terminalPackage = terminal.package;
+45 -30
View File
@@ -6,6 +6,17 @@
let let
homeModules = config.flake.modules.homeManager; homeModules = config.flake.modules.homeManager;
metaLib = config.meta.lib; metaLib = config.meta.lib;
mkNoctaliaSettings =
{
lib,
terminalPackage,
}:
import ./_noctalia-config.nix {
inherit
lib
terminalPackage
;
};
mkPortableSettings = mkPortableSettings =
baseSettings: baseSettings:
lib.recursiveUpdate baseSettings { lib.recursiveUpdate baseSettings {
@@ -29,37 +40,55 @@ let
}; };
in in
{ {
flake.modules.homeManager.noctalia = flake.modules.homeManager.noctalia-base =
{ {
inputs,
config, config,
lib, lib,
pkgs, pkgs,
... ...
}: }:
let let
terminal = metaLib.resolveUserTerminal { terminal = metaLib.resolveRepoTerminal {
inherit pkgs; inherit pkgs;
user = config.meta.user;
}; };
baseSettings = baseSettings =
if terminal.hasMainProgram then if terminal.hasPackage then
import ./_noctalia-config.nix { mkNoctaliaSettings {
inherit inherit lib;
lib
;
terminalPackage = terminal.package; terminalPackage = terminal.package;
} }
else else
{ }; { };
in in
{ {
imports = [ inputs.noctalia.homeModules.default ]; options.meta.lib.noctaliaBaseSettings = lib.mkOption {
type = lib.types.attrs;
internal = true;
readOnly = true;
};
config = {
meta.lib.noctaliaBaseSettings = baseSettings;
assertions = metaLib.mkTerminalAssertions { assertions = metaLib.mkTerminalAssertions {
inherit terminal; inherit terminal;
user = config.meta.user;
}; };
};
};
flake.modules.homeManager.noctalia =
{
config,
inputs,
lib,
pkgs,
...
}:
{
imports = [
homeModules.noctalia-base
inputs.noctalia.homeModules.default
];
programs.noctalia-shell = { programs.noctalia-shell = {
enable = true; enable = true;
@@ -69,7 +98,7 @@ in
} }
); );
settings = baseSettings; settings = config.meta.lib.noctaliaBaseSettings;
}; };
}; };
@@ -77,29 +106,15 @@ in
{ {
config, config,
lib, lib,
pkgs,
... ...
}: }:
let
terminal = metaLib.resolveUserTerminal {
inherit pkgs;
user = config.meta.user;
};
baseSettings =
if terminal.hasMainProgram then
import ./_noctalia-config.nix {
inherit
lib
;
terminalPackage = terminal.package;
}
else
{ };
in
{ {
imports = [ homeModules.noctalia ]; imports = [ homeModules.noctalia ];
programs.noctalia-shell.settings = lib.mkForce ( programs.noctalia-shell.settings = lib.mkForce (
if terminal.hasMainProgram then mkPortableSettings baseSettings else { } if config.meta.lib.noctaliaBaseSettings == { } then
{ }
else
mkPortableSettings config.meta.lib.noctaliaBaseSettings
); );
}; };
} }
+2 -2
View File
@@ -3,7 +3,7 @@
... ...
}: }:
let let
metaLib = config.meta.lib; metaRepo = config.meta.lib.repo;
in in
{ {
flake.modules.homeManager.pim = flake.modules.homeManager.pim =
@@ -51,7 +51,7 @@ in
}; };
remote = { remote = {
url = metaLib.repo.services.radicale.url; url = metaRepo.services.radicale.url;
type = "caldav"; type = "caldav";
userName = config.home.username; userName = config.home.username;
passwordCommand = [ passwordCommand = [
+9 -1
View File
@@ -7,8 +7,16 @@
}; };
flake.modules.homeManager.qbittorrent-client = flake.modules.homeManager.qbittorrent-client =
{ pkgs, ... }: {
lib,
pkgs,
...
}:
{ {
home.packages = [ pkgs.qbittorrent ]; home.packages = [ pkgs.qbittorrent ];
programs.niri.settings.spawn-at-startup = lib.mkAfter [
{ command = [ "qbittorrent" ]; }
];
}; };
} }
+2 -1
View File
@@ -1,7 +1,8 @@
{ config, ... }: { config, ... }:
let let
metaRepo = config.meta.lib.repo;
metaLib = config.meta.lib; metaLib = config.meta.lib;
service = metaLib.repo.services.actual; service = metaRepo.services.actual;
in in
{ {
flake.modules.nixos.actual = flake.modules.nixos.actual =
+2 -2
View File
@@ -1,12 +1,12 @@
{ config, ... }: { config, ... }:
let let
metaLib = config.meta.lib; metaRepo = config.meta.lib.repo;
in in
{ {
flake.modules.nixos.caddy = { flake.modules.nixos.caddy = {
services.caddy = { services.caddy = {
enable = true; enable = true;
email = metaLib.repo.contact.email; email = metaRepo.contact.email;
openFirewall = true; openFirewall = true;
}; };
}; };
+2 -1
View File
@@ -1,7 +1,8 @@
{ config, ... }: { config, ... }:
let let
metaRepo = config.meta.lib.repo;
metaLib = config.meta.lib; metaLib = config.meta.lib;
service = metaLib.repo.services.gitea; service = metaRepo.services.gitea;
in in
{ {
flake.modules.nixos.gitea = flake.modules.nixos.gitea =
+2 -1
View File
@@ -1,7 +1,8 @@
{ config, ... }: { config, ... }:
let let
metaRepo = config.meta.lib.repo;
metaLib = config.meta.lib; metaLib = config.meta.lib;
service = metaLib.repo.services.radicale; service = metaRepo.services.radicale;
in in
{ {
flake.modules.nixos.radicale = flake.modules.nixos.radicale =
+2 -1
View File
@@ -1,7 +1,8 @@
{ config, ... }: { config, ... }:
let let
metaRepo = config.meta.lib.repo;
metaLib = config.meta.lib; metaLib = config.meta.lib;
service = metaLib.repo.services.vaultwarden; service = metaRepo.services.vaultwarden;
in in
{ {
flake.modules.nixos.vaultwarden = flake.modules.nixos.vaultwarden =
+28 -69
View File
@@ -13,51 +13,26 @@ in
host = config.meta.host; host = config.meta.host;
user = config.meta.user; user = config.meta.user;
sourceControl = user.sourceControl; sourceControl = user.sourceControl;
sourceControlScopes = sourceControl.scopes;
hostSourceControlUsers = host.sourceControl.users; hostSourceControlUsers = host.sourceControl.users;
hostUserSourceControl = hostUserSourceControl = hostSourceControlUsers.${user.name} or { };
if lib.hasAttr user.name hostSourceControlUsers then hostSourceControlUsers.${user.name} else { };
profileNames = builtins.attrNames sourceControl.profiles;
parsedProfiles = map ( scopeConfig = scope: hostUserSourceControl.${scope} or null;
name: scopeIdentity = scope: sourceControlScopes.${scope} or null;
let
matches = builtins.match "(github|gitlab)-(personal|work)" name;
in
{
inherit matches name;
isValid = matches != null;
scope = if matches == null then null else builtins.elemAt matches 1;
}
) profileNames;
validProfiles = builtins.filter (profile: profile.isValid) parsedProfiles;
invalidProfileNames = map (profile: profile.name) (
builtins.filter (profile: !profile.isValid) parsedProfiles
);
emailNamesForScope = {
personal = [
"personal"
"main"
];
work = [ "work" ];
};
scopeEmails =
scope:
map (name: user.emails.${name}) (
builtins.filter (name: lib.hasAttr name user.emails) emailNamesForScope.${scope}
);
emailForScope = emailForScope =
scope: scope:
let let
emails = scopeEmails scope; identity = scopeIdentity scope;
in in
if builtins.length emails == 1 then (builtins.head emails).address else null; if identity == null then null else identity.email;
scopeConfig = scopeHasSigningKey =
scope: if lib.hasAttr scope hostUserSourceControl then hostUserSourceControl.${scope} else null; scope:
let
keyConfig = scopeConfig scope;
in
keyConfig != null && keyConfig.publicKey != null;
privateKeyPathForScope = privateKeyPathForScope =
scope: scope:
@@ -69,35 +44,34 @@ in
else else
keyConfig.privateKeyPath; keyConfig.privateKeyPath;
scopePublicKey = publicKeyForScope =
scope: scope:
let let
keyConfig = scopeConfig scope; keyConfig = scopeConfig scope;
in in
if keyConfig == null then null else keyConfig.publicKey; if keyConfig == null then null else keyConfig.publicKey;
scopesInUse = lib.unique ( scopesInUse = builtins.attrNames sourceControlScopes;
[
"personal" allowedSignersLines = map (scope: "${emailForScope scope} ${publicKeyForScope scope}") (
sourceControl.projectScope builtins.filter (scope: emailForScope scope != null && scopeHasSigningKey scope) scopesInUse
]
++ map (profile: profile.scope) validProfiles
); );
missingKeyScopes = builtins.filter (scope: scopePublicKey scope == null) scopesInUse; gitConfigForScope =
invalidEmailScopes = builtins.filter (scope: emailForScope scope == null) scopesInUse; scope:
allowedSignersLines = map (scope: "${emailForScope scope} ${scopePublicKey scope}") ( lib.recursiveUpdate
builtins.filter (scope: emailForScope scope != null && scopePublicKey scope != null) scopesInUse {
);
gitConfigForScope = scope: {
gpg.ssh.allowedSignersFile = "${config.xdg.configHome}/git/allowed_signers";
user = { user = {
name = user.realName; name = user.realName;
email = emailForScope scope; email = emailForScope scope;
signingKey = "${privateKeyPathForScope scope}.pub";
};
}; };
}
(
lib.optionalAttrs (scopeHasSigningKey scope) {
gpg.ssh.allowedSignersFile = "${config.xdg.configHome}/git/allowed_signers";
user.signingKey = "${privateKeyPathForScope scope}.pub";
}
);
gitRoots = [ gitRoots = [
{ {
@@ -113,21 +87,6 @@ in
{ {
imports = [ homeModules.git ]; imports = [ homeModules.git ];
assertions = [
{
assertion = invalidProfileNames == [ ];
message = "Invalid source control profiles for `${user.name}`: ${lib.concatStringsSep ", " invalidProfileNames}. Expected `<service>-<scope>` using github/gitlab and personal/work.";
}
{
assertion = missingKeyScopes == [ ];
message = "Missing source control keys for `${user.name}` scopes: ${lib.concatStringsSep ", " missingKeyScopes}.";
}
{
assertion = invalidEmailScopes == [ ];
message = "Expected exactly one email selected by name for `${user.name}` scopes: ${lib.concatStringsSep ", " invalidEmailScopes}. Personal uses `personal` or `main`; work uses `work`.";
}
];
xdg.configFile."git/allowed_signers".text = lib.concatStringsSep "\n" ( xdg.configFile."git/allowed_signers".text = lib.concatStringsSep "\n" (
allowedSignersLines ++ [ "" ] allowedSignersLines ++ [ "" ]
); );
+33 -33
View File
@@ -1,6 +1,7 @@
{ config, ... }: { config, ... }:
let let
metaLib = config.meta.lib; metaLib = config.meta.lib;
metaRepo = metaLib.repo;
in in
{ {
flake.modules.homeManager.terminal = flake.modules.homeManager.terminal =
@@ -11,17 +12,16 @@ in
... ...
}: }:
let let
terminal = metaLib.resolveUserTerminal { repoTheme = metaRepo.theme.kanagawa;
palette = repoTheme.palette;
terminal = metaLib.resolveRepoTerminal {
inherit pkgs; inherit pkgs;
user = config.meta.user;
}; };
in in
{ {
assertions = metaLib.mkTerminalAssertions { assertions = metaLib.mkTerminalAssertions {
inherit terminal; inherit terminal;
user = config.meta.user;
requireDesktopEntry = true; requireDesktopEntry = true;
requireKitty = true;
}; };
xdg.terminal-exec = { xdg.terminal-exec = {
@@ -44,43 +44,43 @@ in
update_check_interval = 0; update_check_interval = 0;
}; };
extraConfig = '' extraConfig = ''
## name: Kanagawa ## name: ${repoTheme.displayName}
## license: MIT ## license: MIT
## author: Tommaso Laurenzi ## author: Tommaso Laurenzi
## upstream: https://github.com/rebelot/kanagawa.nvim/ ## upstream: https://github.com/rebelot/kanagawa.nvim/
background #1F1F28 background ${palette.background}
foreground #DCD7BA foreground ${palette.foreground}
selection_background #2D4F67 selection_background ${palette.selectionBackground}
selection_foreground #C8C093 selection_foreground ${palette.selectionForeground}
url_color #72A7BC url_color ${palette.url}
cursor #C8C093 cursor ${palette.cursor}
active_tab_background #1F1F28 active_tab_background ${palette.background}
active_tab_foreground #C8C093 active_tab_foreground ${palette.selectionForeground}
inactive_tab_background #1F1F28 inactive_tab_background ${palette.background}
inactive_tab_foreground #727169 inactive_tab_foreground ${palette.muted}
color0 #16161D color0 ${palette.terminal.color0}
color1 #C34043 color1 ${palette.terminal.color1}
color2 #76946A color2 ${palette.terminal.color2}
color3 #C0A36E color3 ${palette.terminal.color3}
color4 #7E9CD8 color4 ${palette.terminal.color4}
color5 #957FB8 color5 ${palette.terminal.color5}
color6 #6A9589 color6 ${palette.terminal.color6}
color7 #C8C093 color7 ${palette.terminal.color7}
color8 #727169 color8 ${palette.terminal.color8}
color9 #E82424 color9 ${palette.terminal.color9}
color10 #98BB6C color10 ${palette.terminal.color10}
color11 #E6C384 color11 ${palette.terminal.color11}
color12 #7FB4CA color12 ${palette.terminal.color12}
color13 #938AA9 color13 ${palette.terminal.color13}
color14 #7AA89F color14 ${palette.terminal.color14}
color15 #DCD7BA color15 ${palette.terminal.color15}
color16 #FFA066 color16 ${palette.terminal.color16}
color17 #FF5D62 color17 ${palette.terminal.color17}
''; '';
}; };
}; };
+3 -2
View File
@@ -4,6 +4,7 @@
}: }:
let let
metaLib = config.meta.lib; metaLib = config.meta.lib;
metaRepo = metaLib.repo;
in in
{ {
flake.modules.nixos.theme = flake.modules.nixos.theme =
@@ -12,7 +13,7 @@ in
... ...
}: }:
let let
repoTheme = metaLib.repo.theme; repoTheme = metaRepo.theme;
cursorTheme = repoTheme.cursor // { cursorTheme = repoTheme.cursor // {
package = metaLib.resolvePackagePath { package = metaLib.resolvePackagePath {
inherit pkgs; inherit pkgs;
@@ -34,7 +35,7 @@ in
flake.modules.homeManager.theme = flake.modules.homeManager.theme =
{ config, pkgs, ... }: { config, pkgs, ... }:
let let
repoTheme = metaLib.repo.theme; repoTheme = metaRepo.theme;
cursorTheme = repoTheme.cursor // { cursorTheme = repoTheme.cursor // {
package = metaLib.resolvePackagePath { package = metaLib.resolvePackagePath {
inherit pkgs; inherit pkgs;
+32 -18
View File
@@ -1,44 +1,58 @@
{ config, ... }:
let
metaRepo = config.meta.lib.repo;
in
{ {
flake.modules.homeManager.vicinae = flake.modules.homeManager.vicinae =
{ pkgs, inputs, ... }: {
pkgs,
inputs,
...
}:
let
repoTheme = metaRepo.theme.kanagawa;
palette = repoTheme.palette;
in
{ {
programs.vicinae = { programs.vicinae = {
enable = true; enable = true;
systemd.enable = true; systemd.enable = true;
themes.kanagawa-wave = { themes.${repoTheme.name} = {
meta = { meta = {
version = 1; version = 1;
name = "Kanagawa Wave"; name = repoTheme.displayName;
description = "A dark theme inspired by the colors of the famous painting by Katsushika Hokusai."; description = "A dark theme inspired by the colors of the famous painting by Katsushika Hokusai.";
variant = "dark"; variant = "dark";
inherits = "vicinae-dark"; inherits = "vicinae-dark";
}; };
colors = { colors = {
core = { core = {
background = "#1F1F28"; background = palette.background;
foreground = "#DCD7BA"; foreground = palette.foreground;
secondary_background = "#16161D"; secondary_background = palette.secondaryBackground;
border = "#2A2A37"; border = palette.border;
accent = "#7E9CD8"; accent = palette.accents.blue;
}; };
accents = { accents = {
blue = "#7E9CD8"; inherit (palette.accents)
green = "#98BB6C"; blue
magenta = "#D27E99"; cyan
orange = "#FFA066"; green
purple = "#957FB8"; magenta
red = "#E82424"; orange
yellow = "#E6C384"; purple
cyan = "#7AA89F"; red
yellow
;
}; };
input.border_focus = "colors.core.accent"; input.border_focus = "colors.core.accent";
}; };
}; };
settings.theme = { settings.theme = {
light.name = "kanagawa-wave"; light.name = repoTheme.name;
dark.name = "kanagawa-wave"; dark.name = repoTheme.name;
}; };
extensions = with inputs.vicinae-extensions.packages.${pkgs.stdenv.hostPlatform.system}; [ extensions = with inputs.vicinae-extensions.packages.${pkgs.stdenv.hostPlatform.system}; [
+7 -1
View File
@@ -15,7 +15,6 @@ in
nixosModules.networking nixosModules.networking
nixosModules.niri nixosModules.niri
nixosModules.printing nixosModules.printing
nixosModules.qbittorrent-client
nixosModules.sddm nixosModules.sddm
nixosModules.sops-admin-key-file nixosModules.sops-admin-key-file
nixosModules.standard-boot nixosModules.standard-boot
@@ -30,6 +29,13 @@ in
environment.localBinInPath = true; environment.localBinInPath = true;
}; };
flake.modules.nixos.workstation-host = {
imports = [
nixosModules.workstation-base
nixosModules.qbittorrent-client
];
};
flake.modules.homeManager.workstation-base = { flake.modules.homeManager.workstation-base = {
imports = [ imports = [
homeModules.ai homeModules.ai
+20 -3
View File
@@ -1,7 +1,20 @@
{ config, ... }:
let
metaLib = config.meta.lib;
metaRepo = metaLib.repo;
in
{ {
flake.modules.homeManager.xdg = flake.modules.homeManager.xdg =
{ config, pkgs, ... }: { config, pkgs, ... }:
let let
browserPackage = metaLib.resolvePackagePath {
inherit pkgs;
path = metaRepo.desktop.browser.packagePath;
};
fileManagerPackage = metaLib.resolvePackagePath {
inherit pkgs;
path = metaRepo.desktop.fileManager.packagePath;
};
homeDir = config.home.homeDirectory; homeDir = config.home.homeDirectory;
localDir = "${homeDir}/.local"; localDir = "${homeDir}/.local";
mediaDir = "${homeDir}/media"; mediaDir = "${homeDir}/media";
@@ -33,12 +46,16 @@
mimeApps = { mimeApps = {
enable = true; enable = true;
defaultApplicationPackages = with pkgs; [ defaultApplicationPackages =
with pkgs;
[
sioyek sioyek
imv imv
vivaldi
neovim neovim
nautilus ]
++ [
browserPackage
fileManagerPackage
]; ];
}; };
}; };
+7 -6
View File
@@ -4,15 +4,16 @@
... ...
}: }:
let let
hostNames = [ hostNames = builtins.attrNames (
"orion" inputs.nixpkgs.lib.filterAttrs (_: type: type == "directory") (builtins.readDir ./hosts)
"polaris" );
"zenith"
];
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
in in
{ {
imports = [ inputs.flake-parts.flakeModules.modules ]; imports = [
inputs.flake-parts.flakeModules.modules
./data.nix
];
systems = [ "x86_64-linux" ]; systems = [ "x86_64-linux" ];
+2 -4
View File
@@ -17,15 +17,13 @@ in
... ...
}: }:
let let
terminal = metaLib.resolveUserTerminal { terminal = metaLib.resolveRepoTerminal {
inherit pkgs; inherit pkgs;
user = config.meta.host.users.kiri;
}; };
in in
{ {
assertions = metaLib.mkTerminalAssertions { assertions = metaLib.mkTerminalAssertions {
inherit terminal; inherit terminal;
user = config.meta.host.users.kiri;
requireTerminfo = true; requireTerminfo = true;
}; };
@@ -45,7 +43,7 @@ in
name = "orion"; name = "orion";
users = { users = {
kiri = { kiri = {
account = metaLib.users.kiri; account = metaLib.accounts.kiri;
homeImports = [ homeImports = [
homeModules.shell homeModules.shell
homeModules.git homeModules.git
+12 -22
View File
@@ -7,6 +7,13 @@ let
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager; homeModules = config.flake.modules.homeManager;
metaLib = config.meta.lib; metaLib = config.meta.lib;
workstationHomeImports = [ homeModules.workstation-base ];
kiriHomeImports = workstationHomeImports ++ [
homeModules.syncthing
homeModules.qbittorrent-client
homeModules.noctalia
];
ergonHomeImports = workstationHomeImports ++ [ homeModules.noctalia ];
in in
{ {
flake.modules.nixos.polaris = metaLib.mkHost { flake.modules.nixos.polaris = metaLib.mkHost {
@@ -29,39 +36,22 @@ in
mouse.accelSpeed = 0.4; mouse.accelSpeed = 0.4;
}; };
sourceControl.users = {
kiri.personal.publicKey = "";
ergon = {
personal.publicKey = "";
work.publicKey = "";
};
};
users = { users = {
kiri = { kiri = {
account = metaLib.users.kiri; account = metaLib.accounts.kiri;
needsPassword = true; needsPassword = true;
homeImports = [ homeImports = kiriHomeImports;
homeModules.workstation-base
homeModules.syncthing
homeModules.qbittorrent-client
homeModules.noctalia
];
}; };
ergon = { ergon = {
account = metaLib.users.ergon; account = metaLib.accounts.ergon;
needsPassword = true; needsPassword = true;
homeImports = [ homeImports = ergonHomeImports;
homeModules.workstation-base
homeModules.noctalia
];
}; };
}; };
imports = [ imports = [
nixosModules.workstation-base nixosModules.workstation-host
nixosModules.steam nixosModules.steam
./_hardware.nix ./_hardware.nix
] ]
+13 -15
View File
@@ -7,6 +7,14 @@ let
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager; homeModules = config.flake.modules.homeManager;
metaLib = config.meta.lib; metaLib = config.meta.lib;
workstationHomeImports = [ homeModules.workstation-base ];
portableNoctalia = homeModules.noctalia-portable;
kiriHomeImports = workstationHomeImports ++ [
homeModules.syncthing
homeModules.qbittorrent-client
portableNoctalia
];
ergonHomeImports = workstationHomeImports ++ [ portableNoctalia ];
in in
{ {
flake.modules.nixos.zenith = metaLib.mkHost { flake.modules.nixos.zenith = metaLib.mkHost {
@@ -31,8 +39,6 @@ in
}; };
sourceControl.users = { sourceControl.users = {
kiri.personal.publicKey = "";
ergon = { ergon = {
personal.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPdR3KP2U84i7f7MlRqcML/3YyMw8JL3hdm637SkMUwO ergon@zenith#personal"; personal.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPdR3KP2U84i7f7MlRqcML/3YyMw8JL3hdm637SkMUwO ergon@zenith#personal";
work.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIHJz5uHKm0/TiMNh/cmzrODHNZ8NgEEZe+47XnJwQGk ergon@zenith#work"; work.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIHJz5uHKm0/TiMNh/cmzrODHNZ8NgEEZe+47XnJwQGk ergon@zenith#work";
@@ -41,28 +47,20 @@ in
users = { users = {
kiri = { kiri = {
account = metaLib.users.kiri; account = metaLib.accounts.kiri;
needsPassword = true; needsPassword = true;
homeImports = [ homeImports = kiriHomeImports;
homeModules.workstation-base
homeModules.syncthing
homeModules.qbittorrent-client
homeModules.noctalia-portable
];
}; };
ergon = { ergon = {
account = metaLib.users.ergon; account = metaLib.accounts.ergon;
needsPassword = true; needsPassword = true;
homeImports = [ homeImports = ergonHomeImports;
homeModules.workstation-base
homeModules.noctalia-portable
];
}; };
}; };
imports = [ imports = [
nixosModules.workstation-base nixosModules.workstation-host
nixosModules.laptop-power nixosModules.laptop-power
{ {
hardware.enableRedistributableFirmware = true; hardware.enableRedistributableFirmware = true;
+39 -120
View File
@@ -4,6 +4,15 @@
... ...
}: }:
let let
normalizeHostUser =
spec:
{
homeImports = [ ];
needsPassword = false;
stateVersion = null;
}
// spec;
mkHost = mkHost =
{ {
name, name,
@@ -20,26 +29,11 @@ let
... ...
}: }:
let let
hostUsers = lib.mapAttrs (_: spec: spec.account) users; hostUserSpecs = lib.mapAttrs (_: spec: normalizeHostUser spec) users;
hostUsers = lib.mapAttrs (_: spec: spec.account) hostUserSpecs;
userAssertions = lib.flatten ( passwordUserSpecs = lib.filterAttrs (_: spec: spec.needsPassword) hostUserSpecs;
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 in
{ {
assertions = userAssertions;
meta.host = { meta.host = {
inherit inherit
displays displays
@@ -67,7 +61,7 @@ let
users.users = lib.mapAttrs ( users.users = lib.mapAttrs (
userName: spec: userName: spec:
{ {
name = spec.account.name; name = userName;
home = spec.account.homeDirectory; home = spec.account.homeDirectory;
isNormalUser = true; isNormalUser = true;
shell = pkgs.zsh; shell = pkgs.zsh;
@@ -76,10 +70,10 @@ let
"networkmanager" "networkmanager"
]; ];
} }
// lib.optionalAttrs (spec.needsPassword or false) { // lib.optionalAttrs spec.needsPassword {
hashedPasswordFile = config.sops.secrets."hashed-password-${userName}".path; hashedPasswordFile = config.sops.secrets."hashed-password-${userName}".path;
} }
) users; ) hostUserSpecs;
home-manager.users = lib.mapAttrs (_: spec: { home-manager.users = lib.mapAttrs (_: spec: {
imports = spec.homeImports; imports = spec.homeImports;
@@ -90,9 +84,9 @@ let
home = { home = {
username = spec.account.name; username = spec.account.name;
homeDirectory = spec.account.homeDirectory; homeDirectory = spec.account.homeDirectory;
stateVersion = spec.stateVersion or stateVersion; stateVersion = if spec.stateVersion == null then stateVersion else spec.stateVersion;
}; };
}) users; }) hostUserSpecs;
}; };
mkCaddyReverseProxy = mkCaddyReverseProxy =
@@ -129,119 +123,59 @@ let
}: }:
lib.attrByPath path null pkgs; lib.attrByPath path null pkgs;
repo = { resolveRepoTerminal =
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, pkgs,
user,
}: }:
let let
terminal = config.meta.lib.repo.desktop.terminal;
package = resolvePackagePath { package = resolvePackagePath {
inherit pkgs; inherit pkgs;
path = user.terminalPackagePath; path = terminal.packagePath;
}; };
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 in
{ {
inherit inherit
desktopId
hasMainProgram
hasPackage
mainProgram
package package
; ;
inherit (terminal)
command
desktopId
packagePath
;
hasPackage = package != null;
hasDesktopEntry = hasDesktopEntry =
desktopId != null && builtins.pathExists "${package}/share/applications/${desktopId}"; package != null && builtins.pathExists "${package}/share/applications/${terminal.desktopId}";
hasTerminfo = hasPackage && lib.elem "terminfo" package.outputs; hasTerminfo = package != null && lib.elem "terminfo" package.outputs;
}; };
mkTerminalAssertions = mkTerminalAssertions =
{ {
terminal, terminal,
user,
requireDesktopEntry ? false, requireDesktopEntry ? false,
requireKitty ? false,
requireMainProgram ? true,
requireTerminfo ? false, requireTerminfo ? false,
}: }:
lib.flatten [ lib.flatten [
[ [
{ {
assertion = terminal.hasPackage; assertion = terminal.hasPackage;
message = "Unknown terminal package `${lib.showAttrPath user.terminalPackagePath}` for user `${user.name}`."; 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 requireMainProgram {
assertion = terminal.hasMainProgram;
message = "Terminal package `${lib.showAttrPath user.terminalPackagePath}` must define `meta.mainProgram`.";
})
(lib.optional requireDesktopEntry { (lib.optional requireDesktopEntry {
assertion = terminal.hasDesktopEntry; assertion = terminal.hasDesktopEntry;
message = "Terminal package `${lib.showAttrPath user.terminalPackagePath}` must provide `${terminal.desktopId}`."; message = "Terminal package `${lib.showAttrPath terminal.packagePath}` 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 { (lib.optional requireTerminfo {
assertion = terminal.hasTerminfo; assertion = terminal.hasTerminfo;
message = "Terminal package `${lib.showAttrPath user.terminalPackagePath}` must provide a `terminfo` output."; message = "Terminal package `${lib.showAttrPath terminal.packagePath}` must provide a `terminfo` output.";
}) })
]; ];
@@ -406,9 +340,9 @@ in
readOnly = true; readOnly = true;
}; };
options.meta.lib.resolveUserTerminal = lib.mkOption { options.meta.lib.resolveRepoTerminal = lib.mkOption {
type = lib.types.raw; type = lib.types.raw;
description = "Internal helper to resolve and validate user terminal metadata."; description = "Internal helper to resolve and validate the repo-standard terminal.";
internal = true; internal = true;
readOnly = true; readOnly = true;
}; };
@@ -427,29 +361,14 @@ in
readOnly = 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 = { config.meta.lib = {
inherit inherit
mkInputProfiles mkInputProfiles
mkCaddyReverseProxy mkCaddyReverseProxy
mkTerminalAssertions mkTerminalAssertions
mkHost mkHost
repo
resolvePackagePath resolvePackagePath
resolveUserTerminal resolveRepoTerminal
; ;
}; };
} }
-73
View File
@@ -1,73 +0,0 @@
{ ... }:
let
kiri = {
name = "kiri";
realName = "Jelle Spreeuwenberg";
homeDirectory = "/home/kiri";
terminalPackagePath = [ "kitty" ];
emails = {
personal = {
address = "mail@jelles.net";
primary = true;
type = "mxrouting";
};
old = {
address = "mail@jellespreeuwenberg.nl";
primary = false;
type = "mxrouting";
};
uni = {
address = "j.spreeuwenberg@student.tue.nl";
primary = false;
type = "office365";
};
work = {
address = "jelle.spreeuwenberg@yookr.org";
primary = false;
type = "office365";
};
};
sourceControl = {
profiles = {
github-personal = { };
gitlab-personal = { };
};
};
};
ergon = {
name = "ergon";
realName = "Jelle Spreeuwenberg";
homeDirectory = "/home/ergon";
terminalPackagePath = [ "kitty" ];
emails = {
personal = {
address = "mail@jelles.net";
primary = false;
type = "mxrouting";
};
work = {
address = "jelle.spreeuwenberg@yookr.org";
primary = true;
type = "office365";
};
};
sourceControl = {
profiles = {
github-personal = { };
github-work = { };
gitlab-personal = { };
};
projectScope = "work";
};
};
in
{
meta.lib.users = {
inherit
ergon
kiri
;
};
}