feat: migrate from modules to features

This commit is contained in:
2026-03-26 23:10:56 +01:00
parent 76e74f4939
commit 8c6fefb95b
52 changed files with 657 additions and 871 deletions

22
features/calibre.nix Normal file
View File

@@ -0,0 +1,22 @@
{
nixos =
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.calibre ];
# udev rules for kindle and mtp device access
# NOTE:(@janezicmatej) uses services.udev.packages instead of extraRules
# because extraRules writes to 99-local.rules which is too late for uaccess
# see https://github.com/NixOS/nixpkgs/issues/308681
services.udev.packages = [
pkgs.libmtp
(pkgs.writeTextFile {
name = "kindle-udev-rules";
text = ''
ACTION!="remove", SUBSYSTEM=="usb", ATTRS{idVendor}=="1949", TAG+="uaccess"
'';
destination = "/etc/udev/rules.d/70-kindle.rules";
})
];
};
}

10
features/claude.nix Normal file
View File

@@ -0,0 +1,10 @@
{
home =
{ pkgs, ... }:
{
home.packages = [
pkgs.claude-code
pkgs.mcp-nixos
];
};
}

85
features/desktop.nix Normal file
View File

@@ -0,0 +1,85 @@
{
nixos =
{ pkgs, inputs, ... }:
{
imports = [ inputs.stylix.nixosModules.stylix ];
# audio
services.pipewire = {
enable = true;
pulse.enable = true;
};
# bluetooth
hardware.bluetooth.enable = true;
services.blueman.enable = true;
security.polkit.enable = true;
services.dbus.enable = true;
services.playerctld.enable = true;
xdg.portal = {
enable = true;
xdgOpenUsePortal = true;
extraPortals = [
pkgs.xdg-desktop-portal-wlr
pkgs.xdg-desktop-portal-gtk
];
};
fonts.packages = with pkgs; [
font-awesome
nerd-fonts.jetbrains-mono
];
# theming
stylix = {
enable = true;
polarity = "dark";
image = "${inputs.assets}/wallpaper.png";
base16Scheme = "${pkgs.base16-schemes}/share/themes/gruvbox-material-dark-medium.yaml";
};
programs.thunderbird.enable = true;
programs._1password.enable = true;
programs._1password-gui.enable = true;
environment.systemPackages = with pkgs; [
easyeffects
ghostty
google-chrome
zathura
pavucontrol
bolt-launcher
libnotify
bibata-cursors
vesktop
rocketchat-desktop
telegram-desktop
slack
jellyfin-media-player
cider-2
mpv
ffmpeg
wf-recorder
wl-mirror
protonmail-bridge
ledger-live-desktop
];
# internal CA
security.pki.certificateFiles = [
inputs.self.outputs.packages.${pkgs.stdenv.hostPlatform.system}.ca-matheo-si
];
xdg.mime.defaultApplications = {
"application/pdf" = "org.pwmt.zathura.desktop";
};
};
home =
{ inputs, ... }:
{
home.file.".assets".source = inputs.assets;
};
}

30
features/dev.nix Normal file
View File

@@ -0,0 +1,30 @@
{
home =
{ pkgs, inputs, ... }:
let
packages = inputs.self.outputs.packages.${pkgs.stdenv.hostPlatform.system};
in
{
home.packages = [
pkgs.git
packages.git-linearize
packages.ggman
pkgs.go
pkgs.python3
pkgs.mdbook
pkgs.marksman
pkgs.mdformat
pkgs.google-cloud-sdk
pkgs.google-cloud-sql-proxy
packages.ahab
pkgs.just
pkgs.presenterm
pkgs.osc
];
programs.direnv = {
enable = true;
nix-direnv.enable = true;
};
};
}

12
features/docker.nix Normal file
View File

@@ -0,0 +1,12 @@
{
nixos =
{ user, ... }:
{
virtualisation.docker = {
enable = true;
logDriver = "json-file";
};
users.users.${user}.extraGroups = [ "docker" ];
};
}

9
features/gnupg.nix Normal file
View File

@@ -0,0 +1,9 @@
{
nixos = _: {
programs.gnupg.agent = {
enable = true;
enableSSHSupport = true;
enableExtraSocket = true;
};
};
}

28
features/greeter.nix Normal file
View File

@@ -0,0 +1,28 @@
{
nixos =
{ lib, inputs, ... }:
{
programs.regreet = {
enable = true;
# single output to avoid stretching across monitors
cageArgs = [
"-s"
"-m"
"last"
];
font = {
name = lib.mkForce "JetBrainsMono Nerd Font";
size = lib.mkForce 14;
};
settings = {
background = {
path = lib.mkForce "${inputs.assets}/wallpaper.png";
fit = lib.mkForce "Cover";
};
GTK = {
application_prefer_dark_theme = lib.mkForce true;
};
};
};
};
}

75
features/initrd-ssh.nix Normal file
View File

@@ -0,0 +1,75 @@
{
nixos =
{ lib, config, ... }:
let
keyDir = "/etc/secrets/initrd";
mkIpString =
{
address,
gateway,
netmask,
interface,
...
}:
"${address}::${gateway}:${netmask}::${interface}:none";
in
{
options = {
initrd-ssh = {
ip = {
enable = lib.mkEnableOption "static IP for initrd (otherwise DHCP)";
address = lib.mkOption {
type = lib.types.str;
};
gateway = lib.mkOption {
type = lib.types.str;
};
netmask = lib.mkOption {
type = lib.types.str;
default = "255.255.255.0";
};
interface = lib.mkOption {
type = lib.types.str;
};
};
authorizedKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
networkModule = lib.mkOption {
type = lib.types.str;
};
};
};
config = {
boot.initrd.kernelModules = [ config.initrd-ssh.networkModule ];
boot.kernelParams = lib.mkIf config.initrd-ssh.ip.enable [
"ip=${mkIpString config.initrd-ssh.ip}"
];
boot.initrd.network = {
enable = true;
ssh = {
enable = true;
port = 22;
hostKeys = [
"${keyDir}/ssh_host_rsa_key"
"${keyDir}/ssh_host_ed25519_key"
];
inherit (config.initrd-ssh) authorizedKeys;
};
postCommands = ''
echo 'cryptsetup-askpass' >> /root/.profile
'';
};
};
};
}

25
features/localisation.nix Normal file
View File

@@ -0,0 +1,25 @@
{
nixos =
{ lib, config, ... }:
{
options = {
localisation = {
timeZone = lib.mkOption {
type = lib.types.str;
};
defaultLocale = lib.mkOption {
type = lib.types.str;
};
};
};
config = {
time.timeZone = config.localisation.timeZone;
i18n.defaultLocale = config.localisation.defaultLocale;
# NOTE:(@janezicmatej) some apps (e.g. java) need TZ env var explicitly
environment.variables.TZ = config.localisation.timeZone;
};
};
}

View File

@@ -1,36 +1,36 @@
{ {
config, home =
lib, {
pkgs, config,
... options,
}: lib,
{ pkgs,
options = { inputs,
neovim = { ...
enable = lib.mkEnableOption "neovim nightly with lsp support"; }:
package = lib.mkPackageOption pkgs "neovim" { }; {
dotfiles = lib.mkOption { options = {
type = lib.types.nullOr lib.types.path; neovim.dotfiles = lib.mkOption {
default = null; type = lib.types.nullOr lib.types.path;
description = "path to neovim config directory"; default = null;
};
}; };
};
};
config = lib.mkIf config.neovim.enable ( config = {
lib.mkMerge [ # only disable when stylix is present (loaded by desktop feature)
(lib.mkIf (config.neovim.dotfiles != null) { stylix.targets.neovim.enable = lib.mkIf (options ? stylix) false;
xdg.configFile."nvim".source = config.neovim.dotfiles;
}) xdg.configFile."nvim" = lib.mkIf (config.neovim.dotfiles != null) {
{ source = config.neovim.dotfiles;
};
programs.neovim = { programs.neovim = {
enable = true; enable = true;
vimAlias = true; vimAlias = true;
defaultEditor = true; defaultEditor = true;
inherit (config.neovim) package; package = inputs.neovim-nightly-overlay.packages.${pkgs.stdenv.hostPlatform.system}.default;
extraPackages = with pkgs; [ extraPackages = with pkgs; [
# runtime deps
gcc gcc
luajit luajit
nodejs_22 nodejs_22
@@ -38,13 +38,11 @@
gnumake gnumake
osc osc
# search and diff
fd fd
ripgrep ripgrep
bat bat
delta delta
# language servers
pyright pyright
typescript-language-server typescript-language-server
lua-language-server lua-language-server
@@ -52,7 +50,6 @@
nil nil
nixd nixd
# formatters
nixpkgs-fmt nixpkgs-fmt
stylua stylua
]; ];
@@ -64,7 +61,6 @@
"${lib.makeLibraryPath [ pkgs.stdenv.cc.cc.lib ]}" "${lib.makeLibraryPath [ pkgs.stdenv.cc.cc.lib ]}"
]; ];
}; };
} };
] };
);
} }

View File

@@ -0,0 +1,9 @@
{
nixos = _: {
networking.networkmanager.enable = true;
networking.nameservers = [
"1.1.1.1"
"8.8.8.8"
];
};
}

5
features/nix-ld.nix Normal file
View File

@@ -0,0 +1,5 @@
{
nixos = _: {
programs.nix-ld.enable = true;
};
}

25
features/openssh.nix Normal file
View File

@@ -0,0 +1,25 @@
{
nixos =
{ lib, config, ... }:
{
options = {
openssh.port = lib.mkOption {
type = lib.types.port;
default = 22;
};
};
config = {
services.openssh = {
enable = true;
ports = [ config.openssh.port ];
settings = {
PasswordAuthentication = false;
AllowUsers = null;
PermitRootLogin = "no";
StreamLocalBindUnlink = "yes";
};
};
};
};
}

10
features/printing.nix Normal file
View File

@@ -0,0 +1,10 @@
{
nixos = _: {
services.printing.enable = true;
services.avahi = {
enable = true;
nssmdns4 = true;
openFirewall = true;
};
};
}

25
features/shell.nix Normal file
View File

@@ -0,0 +1,25 @@
{
nixos = _: {
programs.zsh.enable = true;
environment.etc."zshenv".text = ''
export ZDOTDIR=$HOME/.config/zsh
'';
};
home =
{ pkgs, ... }:
{
home.packages = with pkgs; [
starship
fzf
htop
jc
jq
openssl
pv
ripgrep
fd
tmux
];
};
}

10
features/steam.nix Normal file
View File

@@ -0,0 +1,10 @@
{
nixos = _: {
programs.steam = {
enable = true;
remotePlay.openFirewall = true;
dedicatedServer.openFirewall = true;
localNetworkGameTransfers.openFirewall = true;
};
};
}

38
features/sway.nix Normal file
View File

@@ -0,0 +1,38 @@
{
nixos =
{ pkgs, ... }:
{
programs.sway = {
enable = true;
package = pkgs.swayfx;
wrapperFeatures.gtk = true;
extraSessionCommands = ''
# fix for java awt apps not rendering
export _JAVA_AWT_WM_NONREPARENTING=1
'';
};
environment.systemPackages = with pkgs; [
waybar
mako
wob
playerctl
brightnessctl
foot
grim
pulseaudio
swayidle
swaylock-effects
jq
slurp
wl-clipboard
pamixer
wlsunset
satty
wayland-pipewire-idle-inhibit
fuzzel
cliphist
zenity
];
};
}

8
features/tailscale.nix Normal file
View File

@@ -0,0 +1,8 @@
{
nixos = _: {
services.tailscale = {
enable = true;
useRoutingFeatures = "both";
};
};
}

33
features/user-matej.nix Normal file
View File

@@ -0,0 +1,33 @@
let
sshKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICQGLdINKzs+sEy62Pefng0bcedgU396+OryFgeH99/c janezicmatej"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDk00+Km03epQXQs+xEwwH3zcurACzkEH+kDOPBw6RQe openpgp:0xB095D449"
];
in
{
keys = {
sshAuthorizedKeys = sshKeys;
};
nixos =
{ pkgs, ... }:
{
users.users.matej = {
uid = 1000;
isNormalUser = true;
home = "/home/matej";
shell = pkgs.zsh;
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keys = sshKeys;
};
users.groups.matej = {
gid = 1000;
members = [ "matej" ];
};
};
home = _: {
home.stateVersion = "24.11";
};
}

View File

@@ -0,0 +1,72 @@
{
nixos =
{
pkgs,
lib,
config,
...
}:
let
inherit (config.vm-9p-automount) user;
inherit (config.users.users.${user}) home group;
in
{
options = {
vm-9p-automount = {
user = lib.mkOption {
type = lib.types.str;
};
prefix = lib.mkOption {
type = lib.types.str;
default = "m_";
};
basePath = lib.mkOption {
type = lib.types.str;
default = "${home}/mnt";
};
};
};
config = {
systemd.services.vm-9p-automount = {
description = "Auto-discover and mount 9p shares";
after = [
"local-fs.target"
"nss-user-lookup.target"
"systemd-modules-load.service"
];
wants = [ "systemd-modules-load.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "vm-9p-automount" ''
BASE="${config.vm-9p-automount.basePath}"
PREFIX="${config.vm-9p-automount.prefix}"
mkdir -p "$BASE"
chown ${user}:${group} "$BASE"
for tagfile in $(find /sys/devices -name mount_tag 2>/dev/null); do
[ -f "$tagfile" ] || continue
tag=$(tr -d '\0' < "$tagfile")
case "$tag" in
"$PREFIX"*) ;;
*) continue ;;
esac
name="''${tag#"$PREFIX"}"
target="$BASE/$name"
mkdir -p "$target"
${pkgs.util-linux}/bin/mount -t 9p "$tag" "$target" \
-o trans=virtio,version=9p2000.L || continue
done
'';
};
};
};
};
}

47
features/vm-guest.nix Normal file
View File

@@ -0,0 +1,47 @@
{
nixos =
{
pkgs,
lib,
config,
...
}:
{
options = {
vm-guest.headless = lib.mkOption {
type = lib.types.bool;
default = false;
};
};
config = {
services.qemuGuest.enable = true;
services.spice-vdagentd.enable = lib.mkIf (!config.vm-guest.headless) true;
boot.kernelParams = lib.mkIf config.vm-guest.headless [ "console=ttyS0,115200" ];
boot.initrd.availableKernelModules = [
"9p"
"9pnet_virtio"
];
boot.kernelModules = [
"9p"
"9pnet_virtio"
];
networking = {
useDHCP = true;
firewall.allowedTCPPorts = [ 22 ];
};
security.sudo.wheelNeedsPassword = false;
environment.systemPackages = with pkgs; [
curl
wget
htop
sshfs
];
};
};
}

12
features/yubikey.nix Normal file
View File

@@ -0,0 +1,12 @@
{
nixos =
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
yubikey-personalization
yubikey-manager
];
services.pcscd.enable = true;
};
}

View File

@@ -131,20 +131,6 @@
}; };
}; };
nixosModules = import ./modules/nixos {
inherit my-lib;
inherit (nixpkgs) lib;
} { };
homeManagerModules = import ./modules/home-manager {
inherit my-lib;
inherit (nixpkgs) lib;
} { };
nixosProfiles = import ./profiles {
inherit my-lib;
inherit (nixpkgs) lib;
} { };
}; };
}; };
} }

View File

@@ -8,23 +8,47 @@ name:
{ {
system, system,
user ? null, user ? null,
features ? [ ],
}: }:
let let
hostConfig = ../hosts/${name}/configuration.nix; inherit (nixpkgs) lib;
hostHWConfig = ../hosts/${name}/hardware-configuration.nix;
hasHWConfig = builtins.pathExists hostHWConfig;
hasUser = user != null; hasUser = user != null;
userKeys = if hasUser then import ../users/${user}/keys.nix else { }; # path helpers
featurePath = f: ../features/${f}.nix;
userFeaturePath = u: ../features/user-${u}.nix;
hostConfig = ../hosts/${name}/configuration.nix;
hostHWConfig = ../hosts/${name}/hardware-configuration.nix;
# auto-import all nixos modules and profiles # load feature with path check
nixosModuleList = builtins.attrValues inputs.self.nixosModules; loadFeature =
nixosProfileList = builtins.attrValues inputs.self.nixosProfiles; f:
assert
builtins.pathExists (featurePath f)
|| throw "feature '${f}' not found at ${toString (featurePath f)}";
import (featurePath f);
# auto-import all home-manager modules loadedFeatures = map loadFeature features;
hmModuleList = builtins.attrValues inputs.self.homeManagerModules;
# load user feature with path check
userFeature =
if hasUser then
assert
builtins.pathExists (userFeaturePath user)
|| throw "user feature 'user-${user}' not found at ${toString (userFeaturePath user)}";
import (userFeaturePath user)
else
null;
allFeatures = loadedFeatures ++ lib.optional (userFeature != null) userFeature;
# extract keys from user feature for specialArgs
userKeys = if userFeature != null then (userFeature.keys or { }) else { };
# collect nixos and home modules from all features
nixosMods = map (f: f.nixos) (builtins.filter (f: f ? nixos) allFeatures);
homeMods = map (f: f.home) (builtins.filter (f: f ? home) allFeatures);
in in
nixpkgs.lib.nixosSystem { nixpkgs.lib.nixosSystem {
inherit system; inherit system;
@@ -33,27 +57,23 @@ nixpkgs.lib.nixosSystem {
{ nixpkgs.overlays = overlays; } { nixpkgs.overlays = overlays; }
{ nixpkgs.config.allowUnfree = true; } { nixpkgs.config.allowUnfree = true; }
{ networking.hostName = name; }
hostConfig hostConfig
] ]
++ nixpkgs.lib.optional hasHWConfig hostHWConfig ++ lib.optional (builtins.pathExists hostHWConfig) hostHWConfig
++ nixosModuleList ++ nixosMods
++ nixosProfileList ++ lib.optionals hasUser [
++ nixpkgs.lib.optional (
hasUser && builtins.pathExists ../users/${user}/nixos.nix
) ../users/${user}/nixos.nix
++ [
inputs.home-manager.nixosModules.home-manager inputs.home-manager.nixosModules.home-manager
{ {
home-manager.useGlobalPkgs = true; home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true; home-manager.useUserPackages = true;
home-manager.backupFileExtension = "backup"; home-manager.backupFileExtension = "backup";
home-manager.users = nixpkgs.lib.mkIf hasUser { home-manager.users.${user}.imports = homeMods;
${user} = import ../users/${user}/home-manager.nix;
};
home-manager.sharedModules = hmModuleList;
home-manager.extraSpecialArgs = { inherit inputs; }; home-manager.extraSpecialArgs = { inherit inputs; };
} }
]; ];
specialArgs = { inherit inputs userKeys; }; specialArgs = {
inherit inputs userKeys user;
};
} }

View File

@@ -1,25 +0,0 @@
{
config,
lib,
pkgs,
inputs,
...
}:
let
packages = inputs.self.outputs.packages.${pkgs.stdenv.hostPlatform.system};
in
{
options = {
claude = {
enable = lib.mkEnableOption "claude code";
package = lib.mkPackageOption pkgs "claude-code" { };
};
};
config = lib.mkIf config.claude.enable {
home.packages = [
config.claude.package
pkgs.mcp-nixos
];
};
}

View File

@@ -1 +0,0 @@
{ lib, my-lib }: args: (my-lib.autoDir ./.)

View File

@@ -1,38 +0,0 @@
{
config,
lib,
pkgs,
inputs,
...
}:
{
options = {
desktop.enable = lib.mkEnableOption "desktop gui applications";
};
config = lib.mkIf config.desktop.enable {
home.packages = with pkgs; [
ghostty
google-chrome
zathura
pavucontrol
bolt-launcher
libnotify
bibata-cursors
vesktop
rocketchat-desktop
telegram-desktop
slack
jellyfin-media-player
cider-2
protonmail-bridge
ledger-live-desktop
mpv
ffmpeg
wf-recorder
wl-mirror
];
home.file.".assets".source = inputs.assets;
};
}

View File

@@ -1,39 +0,0 @@
{
config,
lib,
pkgs,
inputs,
...
}:
let
packages = inputs.self.outputs.packages.${pkgs.stdenv.hostPlatform.system};
in
{
options = {
dev.enable = lib.mkEnableOption "development tools";
};
config = lib.mkIf config.dev.enable {
home.packages = [
pkgs.git
packages.git-linearize
packages.ggman
pkgs.go
pkgs.python3
pkgs.mdbook
pkgs.marksman
pkgs.mdformat
pkgs.google-cloud-sdk
pkgs.google-cloud-sql-proxy
packages.ahab
pkgs.just
pkgs.presenterm
pkgs.osc
];
programs.direnv = {
enable = true;
nix-direnv.enable = true;
};
};
}

View File

@@ -1,26 +0,0 @@
{
config,
lib,
pkgs,
...
}:
{
options = {
shell.enable = lib.mkEnableOption "shell utilities";
};
config = lib.mkIf config.shell.enable {
home.packages = with pkgs; [
starship
fzf
htop
jc
jq
openssl
pv
ripgrep
fd
tmux
];
};
}

View File

@@ -1,32 +0,0 @@
{
lib,
config,
pkgs,
...
}:
{
options = {
calibre = {
enable = lib.mkEnableOption "Calibre e-book management with Kindle support";
};
};
config = lib.mkIf config.calibre.enable {
environment.systemPackages = [ pkgs.calibre ];
# udev rules for kindle and mtp device access
# NOTE:(@janezicmatej) uses services.udev.packages instead of extraRules
# because extraRules writes to 99-local.rules which is too late for uaccess
# see https://github.com/NixOS/nixpkgs/issues/308681
services.udev.packages = [
pkgs.libmtp
(pkgs.writeTextFile {
name = "kindle-udev-rules";
text = ''
ACTION!="remove", SUBSYSTEM=="usb", ATTRS{idVendor}=="1949", TAG+="uaccess"
'';
destination = "/etc/udev/rules.d/70-kindle.rules";
})
];
};
}

View File

@@ -1 +0,0 @@
{ lib, my-lib }: args: (my-lib.autoDir ./.)

View File

@@ -1,44 +0,0 @@
{
lib,
config,
pkgs,
...
}:
{
options = {
desktop = {
enable = lib.mkEnableOption "base desktop environment";
};
};
config = lib.mkIf config.desktop.enable {
services.pipewire = {
enable = true;
pulse.enable = true;
};
hardware.bluetooth.enable = true;
services.blueman.enable = true;
security.polkit.enable = true;
services.dbus.enable = true;
services.playerctld.enable = true;
xdg.portal = {
enable = true;
xdgOpenUsePortal = true;
extraPortals = [
pkgs.xdg-desktop-portal-wlr
pkgs.xdg-desktop-portal-gtk
];
};
fonts.packages = with pkgs; [
font-awesome
nerd-fonts.jetbrains-mono
maple-mono.NF
];
};
}

View File

@@ -1,20 +0,0 @@
{
lib,
config,
...
}:
{
options = {
gnupg = {
enable = lib.mkEnableOption "GnuPG agent with SSH support";
};
};
config = lib.mkIf config.gnupg.enable {
programs.gnupg.agent = {
enable = true;
enableSSHSupport = true;
enableExtraSocket = true;
};
};
}

View File

@@ -1,37 +0,0 @@
{
lib,
config,
pkgs,
inputs,
...
}:
{
options = {
greeter.enable = lib.mkEnableOption "greetd with regreet";
};
config = lib.mkIf config.greeter.enable {
programs.regreet = {
enable = true;
# single output to avoid stretching across monitors
cageArgs = [
"-s"
"-m"
"last"
];
font = {
name = lib.mkForce "JetBrainsMono Nerd Font";
size = lib.mkForce 14;
};
settings = {
background = {
path = lib.mkForce "${inputs.assets}/wallpaper.png";
fit = lib.mkForce "Cover";
};
GTK = {
application_prefer_dark_theme = lib.mkForce true;
};
};
};
};
}

View File

@@ -1,83 +0,0 @@
{
lib,
config,
...
}:
let
# generate host keys for new machines: ./scripts/initrd-ssh-keygen.sh
keyDir = "/etc/secrets/initrd";
mkIpString =
{
address,
gateway,
netmask,
interface,
...
}:
"${address}::${gateway}:${netmask}::${interface}:none";
in
{
options = {
initrd-ssh = {
enable = lib.mkEnableOption "SSH in initrd for remote LUKS unlock";
ip = {
enable = lib.mkEnableOption "static IP for initrd (otherwise DHCP)";
address = lib.mkOption {
type = lib.types.str;
example = "10.222.0.247";
};
gateway = lib.mkOption {
type = lib.types.str;
example = "10.222.0.1";
};
netmask = lib.mkOption {
type = lib.types.str;
default = "255.255.255.0";
};
interface = lib.mkOption {
type = lib.types.str;
example = "enp5s0";
};
};
authorizedKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
networkModule = lib.mkOption {
type = lib.types.str;
example = "r8169";
};
};
};
config = lib.mkIf config.initrd-ssh.enable {
boot.initrd.kernelModules = [ config.initrd-ssh.networkModule ];
boot.kernelParams = lib.mkIf config.initrd-ssh.ip.enable [
"ip=${mkIpString config.initrd-ssh.ip}"
];
boot.initrd.network = {
enable = true;
ssh = {
enable = true;
port = 22;
hostKeys = [
"${keyDir}/ssh_host_rsa_key"
"${keyDir}/ssh_host_ed25519_key"
];
inherit (config.initrd-ssh) authorizedKeys;
};
postCommands = ''
echo 'cryptsetup-askpass' >> /root/.profile
'';
};
};
}

View File

@@ -1,28 +0,0 @@
{
lib,
config,
...
}:
{
options = {
localisation = {
enable = lib.mkEnableOption "localisation defaults";
timeZone = lib.mkOption {
type = lib.types.str;
};
defaultLocale = lib.mkOption {
type = lib.types.str;
};
};
};
config = lib.mkIf config.localisation.enable {
time.timeZone = config.localisation.timeZone;
i18n.defaultLocale = config.localisation.defaultLocale;
# NOTE:(@janezicmatej) some apps (e.g. java) need TZ env var explicitly
environment.variables.TZ = config.localisation.timeZone;
};
}

View File

@@ -1,23 +0,0 @@
{
lib,
config,
...
}:
{
options = {
nvidia.enable = lib.mkEnableOption "NVIDIA GPU support";
};
config = lib.mkIf config.nvidia.enable {
hardware.graphics.enable = true;
services.xserver.videoDrivers = [ "nvidia" ];
hardware.nvidia = {
modesetting.enable = true;
open = true;
nvidiaSettings = true;
package = config.boot.kernelPackages.nvidiaPackages.stable;
};
};
}

View File

@@ -1,29 +0,0 @@
{
lib,
config,
...
}:
{
options = {
openssh = {
enable = lib.mkEnableOption "hardened SSH server";
port = lib.mkOption {
type = lib.types.port;
default = 22;
};
};
};
config = lib.mkIf config.openssh.enable {
services.openssh = {
enable = true;
ports = [ config.openssh.port ];
settings = {
PasswordAuthentication = false;
AllowUsers = null;
PermitRootLogin = "no";
StreamLocalBindUnlink = "yes";
};
};
};
}

View File

@@ -1,21 +0,0 @@
{
lib,
config,
...
}:
{
options = {
printing = {
enable = lib.mkEnableOption "CUPS printing with Avahi discovery";
};
};
config = lib.mkIf config.printing.enable {
services.printing.enable = true;
services.avahi = {
enable = true;
nssmdns4 = true;
openFirewall = true;
};
};
}

View File

@@ -1,54 +0,0 @@
{
pkgs,
lib,
config,
...
}:
{
options = {
sway = {
enable = lib.mkEnableOption "enable sway module";
cmdFlags = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
};
config = lib.mkIf config.sway.enable {
programs.sway = {
enable = true;
package = pkgs.swayfx;
wrapperFeatures.gtk = true;
extraOptions = config.sway.cmdFlags;
extraSessionCommands = ''
# fix for java awt apps not rendering
export _JAVA_AWT_WM_NONREPARENTING=1
'';
};
environment.systemPackages = with pkgs; [
waybar
mako
wob
playerctl
brightnessctl
foot
grim
pulseaudio
swayidle
swaylock-effects
jq
slurp
wl-clipboard
pamixer
wlsunset
satty
wayland-pipewire-idle-inhibit
fuzzel
cliphist
zenity
];
};
}

View File

@@ -1,74 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
inherit (config.vm-9p-automount) user;
inherit (config.users.users.${user}) home group;
in
{
options = {
vm-9p-automount = {
enable = lib.mkEnableOption "auto-discover and mount 9p shares";
user = lib.mkOption {
type = lib.types.str;
description = "user to own the mount points";
};
prefix = lib.mkOption {
type = lib.types.str;
default = "m_";
description = "9p mount tag prefix to match";
};
basePath = lib.mkOption {
type = lib.types.str;
default = "${home}/mnt";
description = "directory to mount shares under";
};
};
};
config = lib.mkIf config.vm-9p-automount.enable {
systemd.services.vm-9p-automount = {
description = "Auto-discover and mount 9p shares";
after = [
"local-fs.target"
"nss-user-lookup.target"
"systemd-modules-load.service"
];
wants = [ "systemd-modules-load.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "vm-9p-automount" ''
BASE="${config.vm-9p-automount.basePath}"
PREFIX="${config.vm-9p-automount.prefix}"
mkdir -p "$BASE"
chown ${user}:${group} "$BASE"
for tagfile in $(find /sys/devices -name mount_tag 2>/dev/null); do
[ -f "$tagfile" ] || continue
tag=$(tr -d '\0' < "$tagfile")
case "$tag" in
"$PREFIX"*) ;;
*) continue ;;
esac
name="''${tag#"$PREFIX"}"
target="$BASE/$name"
mkdir -p "$target"
${pkgs.util-linux}/bin/mount -t 9p "$tag" "$target" \
-o trans=virtio,version=9p2000.L || continue
done
'';
};
};
};
}

View File

@@ -1,49 +0,0 @@
{
pkgs,
lib,
config,
...
}:
{
options = {
vm-guest = {
enable = lib.mkEnableOption "VM guest configuration";
headless = lib.mkOption {
type = lib.types.bool;
default = false;
description = "run without display, serial console only";
};
};
};
config = lib.mkIf config.vm-guest.enable {
services.qemuGuest.enable = true;
services.spice-vdagentd.enable = lib.mkIf (!config.vm-guest.headless) true;
boot.kernelParams = lib.mkIf config.vm-guest.headless [ "console=ttyS0,115200" ];
# 9p for host file mounting
boot.initrd.availableKernelModules = [
"9p"
"9pnet_virtio"
];
boot.kernelModules = [
"9p"
"9pnet_virtio"
];
networking = {
useDHCP = true;
firewall.allowedTCPPorts = [ 22 ];
};
security.sudo.wheelNeedsPassword = false;
environment.systemPackages = with pkgs; [
curl
wget
htop
sshfs
];
};
}

View File

@@ -1,31 +0,0 @@
{
lib,
config,
pkgs,
...
}:
{
options = {
workstation = {
enable = lib.mkEnableOption "workstation utilities";
};
};
config = lib.mkIf config.workstation.enable {
programs.nix-ld.enable = true;
virtualisation.docker = {
enable = true;
logDriver = "json-file";
};
services.tailscale = {
enable = true;
useRoutingFeatures = "both";
};
environment.systemPackages = with pkgs; [
smartmontools
];
};
}

View File

@@ -1,23 +0,0 @@
{
pkgs,
lib,
config,
...
}:
{
options = {
yubikey = {
enable = lib.mkEnableOption "enable yubikey module";
};
};
config = lib.mkIf config.yubikey.enable {
environment.systemPackages = with pkgs; [
yubikey-personalization
yubikey-manager
];
services.pcscd.enable = true;
};
}

View File

@@ -1,19 +0,0 @@
{
lib,
config,
...
}:
{
options = {
zsh = {
enable = lib.mkEnableOption "zsh with ZDOTDIR in ~/.config/zsh";
};
};
config = lib.mkIf config.zsh.enable {
programs.zsh.enable = true;
environment.etc."zshenv".text = ''
export ZDOTDIR=$HOME/.config/zsh
'';
};
}

View File

@@ -1,17 +0,0 @@
{
lib,
config,
...
}:
{
options = {
profiles.base.enable = lib.mkEnableOption "base profile for all machines";
};
config = lib.mkIf config.profiles.base.enable {
openssh.enable = lib.mkDefault true;
zsh.enable = lib.mkDefault true;
localisation.enable = lib.mkDefault true;
gnupg.enable = lib.mkDefault true;
};
}

View File

@@ -1 +0,0 @@
{ lib, my-lib }: args: (my-lib.autoDir ./.)

View File

@@ -1,21 +0,0 @@
{
lib,
config,
...
}:
{
options = {
profiles.desktop.enable = lib.mkEnableOption "desktop profile (sway, audio, printing)";
};
config = lib.mkIf config.profiles.desktop.enable {
profiles.base.enable = lib.mkDefault true;
desktop.enable = lib.mkDefault true;
sway.enable = lib.mkDefault true;
greeter.enable = lib.mkDefault true;
printing.enable = lib.mkDefault true;
workstation.enable = lib.mkDefault true;
yubikey.enable = lib.mkDefault true;
calibre.enable = lib.mkDefault true;
};
}

View File

@@ -1,15 +0,0 @@
{
lib,
config,
...
}:
{
options = {
profiles.server.enable = lib.mkEnableOption "headless server profile";
};
config = lib.mkIf config.profiles.server.enable {
profiles.base.enable = lib.mkDefault true;
workstation.enable = lib.mkDefault true;
};
}

View File

@@ -1,22 +0,0 @@
{
pkgs,
inputs,
osConfig,
...
}:
{
home.stateVersion = "24.11";
# always-on
shell.enable = true;
dev.enable = true;
neovim = {
enable = true;
package = inputs.neovim-nightly-overlay.packages.${pkgs.stdenv.hostPlatform.system}.default;
};
claude.enable = true;
# desktop-conditional
desktop.enable = osConfig.desktop.enable;
}

View File

@@ -1,6 +0,0 @@
{
sshAuthorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICQGLdINKzs+sEy62Pefng0bcedgU396+OryFgeH99/c janezicmatej"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDk00+Km03epQXQs+xEwwH3zcurACzkEH+kDOPBw6RQe openpgp:0xB095D449"
];
}

View File

@@ -1,27 +0,0 @@
{
lib,
config,
pkgs,
...
}:
let
keys = import ./keys.nix;
in
{
users.users.matej = {
uid = 1000;
isNormalUser = true;
home = "/home/matej";
shell = pkgs.zsh;
extraGroups = [
"wheel"
"docker"
];
openssh.authorizedKeys.keys = keys.sshAuthorizedKeys;
};
users.groups.matej = {
gid = 1000;
members = [ "matej" ];
};
}