huge refactor

This commit is contained in:
fwastring 2026-04-02 10:58:37 +02:00
parent 03e5a47910
commit 1d4c8455ee
30 changed files with 972 additions and 697 deletions

View file

@ -0,0 +1,69 @@
{
lib,
config,
pkgs,
...
}:
with lib;
{
options.features.network = {
enable = mkEnableOption "enable network tooling and VPN services";
netbird = {
enable = mkOption {
type = types.bool;
default = true;
description = "Enable NetBird service.";
};
uiEnable = mkOption {
type = types.bool;
default = true;
description = "Enable NetBird UI component.";
};
};
tailscale = {
waitForNetbird = mkOption {
type = types.bool;
default = true;
description = "Add netbird ordering to tailscaled unit.";
};
};
tooling = {
enable = mkOption {
type = types.bool;
default = true;
description = "Install network troubleshooting CLI tools.";
};
packages = mkOption {
type = types.listOf types.package;
default = with pkgs; [
dnsutils
nmap
ipcalc
];
description = "Packages installed when network tooling is enabled.";
};
};
};
config = mkIf config.features.network.enable {
services.netbird = mkIf config.features.network.netbird.enable {
enable = true;
ui.enable = config.features.network.netbird.uiEnable;
};
systemd.services.tailscaled =
mkIf (config.features.network.tailscale.waitForNetbird && config.features.network.netbird.enable)
{
after = [
"netbird.service"
"network-online.target"
];
wants = [
"netbird.service"
"network-online.target"
];
};
environment.systemPackages = mkIf config.features.network.tooling.enable config.features.network.tooling.packages;
};
}

View file

@ -0,0 +1,71 @@
{ lib, config, ... }:
with lib;
{
options.features.sound = {
enable = mkEnableOption "enable sound and bluetooth stack";
pipewire = {
enable = mkOption {
type = types.bool;
default = true;
description = "Enable PipeWire audio stack.";
};
alsa32Bit = mkOption {
type = types.bool;
default = true;
description = "Enable 32-bit ALSA support.";
};
pulseCompat = mkOption {
type = types.bool;
default = true;
description = "Enable PulseAudio compatibility through PipeWire.";
};
};
bluetooth = {
enable = mkOption {
type = types.bool;
default = true;
description = "Enable Bluetooth subsystem.";
};
powerOnBoot = mkOption {
type = types.bool;
default = true;
description = "Power on Bluetooth at boot.";
};
disableHeadsetProfile = mkOption {
type = types.bool;
default = true;
description = "Disable headset profile for Bluetooth devices.";
};
bluemanEnable = mkOption {
type = types.bool;
default = true;
description = "Enable Blueman service.";
};
};
};
config = mkIf config.features.sound.enable {
services.pulseaudio.enable = false;
security.rtkit.enable = true;
services = {
pipewire = {
enable = config.features.sound.pipewire.enable;
alsa.enable = config.features.sound.pipewire.enable;
alsa.support32Bit = config.features.sound.pipewire.alsa32Bit;
pulse.enable = config.features.sound.pipewire.pulseCompat;
};
blueman.enable = config.features.sound.bluetooth.bluemanEnable;
};
hardware = {
bluetooth = mkIf config.features.sound.bluetooth.enable {
enable = config.features.sound.bluetooth.enable;
powerOnBoot = config.features.sound.bluetooth.powerOnBoot;
settings = {
General = mkIf config.features.sound.bluetooth.disableHeadsetProfile {
Disable = "Headset";
};
};
};
};
};
}

View file

@ -0,0 +1,78 @@
{
pkgs,
lib,
config,
...
}:
with lib;
{
options.features.system = {
enable = mkEnableOption "enable shared system utilities";
preset = mkOption {
type = types.enum [
"minimal"
"desktop"
"ops"
];
default = "desktop";
description = "Package profile for shared system tooling.";
};
corePackages = mkOption {
type = types.listOf types.package;
default = with pkgs; [
sops
unzip
zip
wget
htop
procps
fastfetch
bc
fzf
eza
rsync
ripgrep
fd
];
description = "Always-installed core system packages.";
};
desktopPackages = mkOption {
type = types.listOf types.package;
default = with pkgs; [
bluez
bluez-tools
poppler-utils
alsa-utils
libnotify
hyprpicker
];
description = "Additional packages for desktop-oriented hosts.";
};
opsPackages = mkOption {
type = types.listOf types.package;
default = with pkgs; [
grc
lazygit
];
description = "Additional packages for operations-heavy hosts.";
};
extras = mkOption {
type = types.listOf types.package;
default = with pkgs; [
lolcat
fortune
cowsay
typst
];
description = "Additional optional packages for all presets except minimal.";
};
};
config = mkIf config.features.system.enable {
environment.systemPackages =
config.features.system.corePackages
++ optionals (config.features.system.preset != "minimal") config.features.system.extras
++ optionals (config.features.system.preset == "desktop") config.features.system.desktopPackages
++ optionals (config.features.system.preset == "ops") config.features.system.opsPackages;
};
}

View file

@ -0,0 +1,62 @@
{
config,
pkgs,
lib,
...
}:
with lib;
{
options.features.users = {
enable = mkEnableOption "enable default users";
name = mkOption {
type = types.str;
default = "fw";
description = "Primary user account name.";
};
fullName = mkOption {
type = types.str;
default = "Fredrik Wastring";
description = "Primary user full name.";
};
initialPassword = mkOption {
type = types.str;
default = "password";
description = "Initial password for the primary user.";
};
extraGroups = mkOption {
type = types.listOf types.str;
default = [
"networkmanager"
"wheel"
"audio"
"docker"
"input"
];
description = "Additional groups for the primary user.";
};
sshAuthorizedKeys = mkOption {
type = types.listOf types.str;
default = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFpJBGPIfPB1BwSG7aoKqwfccyZSaU7J3xpJ8behMp9N fw@core"
];
description = "SSH authorized keys for the primary user.";
};
};
config = mkIf config.features.users.enable {
users = {
defaultUserShell = pkgs.bash;
users = {
${config.features.users.name} = {
initialPassword = config.features.users.initialPassword;
isNormalUser = true;
description = config.features.users.fullName;
extraGroups = config.features.users.extraGroups;
openssh.authorizedKeys = {
keys = config.features.users.sshAuthorizedKeys;
};
};
};
};
};
}

View file

@ -100,6 +100,21 @@ in
default = 24;
description = "VAAPI quality parameter (lower means better quality, higher CPU/bitrate).";
};
waitForRtsp = mkOption {
type = types.bool;
default = true;
description = "Wait for the RTSP destination to accept TCP connections before ffmpeg starts.";
};
waitForRtspTimeoutSec = mkOption {
type = types.int;
default = 30;
description = "Maximum seconds to wait for RTSP destination availability.";
};
waitForRtspIntervalSec = mkOption {
type = types.int;
default = 1;
description = "Seconds between RTSP availability checks.";
};
vaapiDriver = mkOption {
type = types.nullOr types.str;
default = null;
@ -209,6 +224,13 @@ in
"-rc_mode VBR -b:v ${toString streamCfg.bitrateKbps}k -maxrate ${toString maxrateKbps}k -bufsize ${toString bufsizeKbps}k"
else
"-rc_mode CQP -qp ${toString streamCfg.vaapiQp}";
rtspUrlMatch = builtins.match "rtsp://([^/:]+)(:([0-9]+))?/.*" streamCfg.rtspUrl;
rtspHost = if rtspUrlMatch != null then builtins.elemAt rtspUrlMatch 0 else "127.0.0.1";
rtspPort =
if rtspUrlMatch != null && builtins.elemAt rtspUrlMatch 2 != null then
builtins.elemAt rtspUrlMatch 2
else
"8554";
videoCodecArgs =
if streamCfg.useVaapi then
"-vaapi_device ${streamCfg.vaapiDevice} -vf format=nv12,hwupload -c:v h264_vaapi -profile:v main -level:v 4.1 ${vaapiRateControlArgs} -g ${
@ -230,6 +252,22 @@ in
Restart = "always";
RestartSec = "2";
Environment = optional (streamCfg.vaapiDriver != null) "LIBVA_DRIVER_NAME=${streamCfg.vaapiDriver}";
ExecStartPre = optional cfg.waitForRtsp (
pkgs.writeShellScript "wait-for-rtsp-${sanitizeName streamName}" ''
set -eu
retries=${toString cfg.waitForRtspTimeoutSec}
interval=${toString cfg.waitForRtspIntervalSec}
i=0
until ${pkgs.netcat-openbsd}/bin/nc -z -w1 ${rtspHost} ${rtspPort}; do
i=$((i + 1))
if [ "$i" -ge "$retries" ]; then
echo "Timed out waiting for RTSP server at ${rtspHost}:${rtspPort}" >&2
exit 1
fi
sleep "$interval"
done
''
);
ExecStart = "${pkgs.ffmpeg}/bin/ffmpeg -hide_banner -loglevel warning -f v4l2 -framerate ${toString streamCfg.framerate} -video_size ${streamCfg.videoSize} -i ${streamCfg.device} ${videoCodecArgs} -f rtsp -rtsp_transport tcp ${streamCfg.rtspUrl}";
};
}