refactor: split overloaded modules

This commit is contained in:
Nickolaj Jepsen 2026-01-21 00:10:58 +01:00
parent 234ab50a2c
commit 15f5c2552d
15 changed files with 638 additions and 586 deletions

View file

@ -17,7 +17,7 @@
./audio.nix ./audio.nix
./fonts.nix ./fonts.nix
./greetd.nix ./greetd.nix
./niri.nix ./niri
./qt.nix ./qt.nix
./gtk.nix ./gtk.nix
./dms/default.nix ./dms/default.nix

View file

@ -1,243 +0,0 @@
{
config,
lib,
pkgs,
inputs,
...
}: let
c = config.fireproof.theme.colors;
hasMonitors = config.monitors != [];
primaryMonitorName =
if hasMonitors
then (builtins.head config.monitors).name or ""
else "";
in {
config = lib.mkIf config.fireproof.desktop.windowManager.enable {
programs.xwayland.enable = true;
xdg.portal = {
enable = true;
extraPortals = [pkgs.xdg-desktop-portal-gtk];
config.common.default = "gnome";
xdgOpenUsePortal = true;
};
programs.niri = {
enable = true;
package = inputs.niri.packages."${pkgs.stdenv.hostPlatform.system}".niri-unstable;
};
fireproof.home-manager.programs.niri.settings = {
prefer-no-csd = true;
clipboard.disable-primary = true;
spawn-at-startup = [
{command = ["systemctl" "--user" "start" "hyprpaper"];}
];
xwayland-satellite = {
enable = true;
path = lib.getExe inputs.niri.packages."${pkgs.stdenv.hostPlatform.system}".xwayland-satellite-unstable;
};
environment = {
NIXOS_OZONE_WL = "1";
GDK_BACKEND = "wayland"; # Attempt to fix screen recording issue
};
layout = {
gaps = 10;
focus-ring.enable = false;
insert-hint.display.color = "#${c.accent}";
border = {
enable = true;
width = 2;
active.color = "#${c.accent}";
inactive.color = "#${c.ui}";
};
tab-indicator = {
hide-when-single-tab = true;
place-within-column = true;
gap = 2;
position = "top";
corner-radius = 8;
};
};
input = {
focus-follows-mouse = {
enable = true;
max-scroll-amount = "0%";
};
mouse.accel-profile = "flat";
keyboard.xkb.layout = "eu";
};
window-rules = [
{
clip-to-geometry = true;
geometry-corner-radius = {
top-left = 8.0;
top-right = 8.0;
bottom-left = 8.0;
bottom-right = 8.0;
};
}
];
workspaces = lib.mkIf (primaryMonitorName != "") {
"01" = {
open-on-output = primaryMonitorName;
};
"02" = {
open-on-output = primaryMonitorName;
};
"03" = {
open-on-output = primaryMonitorName;
};
"04" = {
open-on-output = primaryMonitorName;
};
"05" = {
open-on-output = primaryMonitorName;
};
};
binds = {
"XF86AudioRaiseVolume" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "audio" "increment" "3"];
};
"XF86AudioLowerVolume" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "audio" "decrement" "3"];
};
"XF86AudioMute" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "audio" "mute"];
};
"XF86AudioMicMute" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "audio" "micmute"];
};
"XF86MonBrightnessUp" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "brightness" "increment" "5" ""];
};
"XF86MonBrightnessDown" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "brightness" "decrement" "5" ""];
};
"Mod+N" = {
action.spawn = ["dms" "ipc" "notifications" "toggle"];
hotkey-overlay.title = "Toggle Notification Center";
};
"Mod+V" = {
action.spawn = ["dms" "ipc" "clipboard" "toggle"];
hotkey-overlay.title = "Toggle Clipboard Manager";
};
"Mod+Space" = {
action.spawn = ["dms" "ipc" "spotlight" "toggle"];
hotkey-overlay.title = "Toggle Application Launcher";
};
"Mod+Semicolon" = {
action.spawn = ["dms" "ipc" "spotlight" "toggleQuery" ":"];
hotkey-overlay.title = "Toggle Emoji Picker";
};
"Mod+P" = {
action.spawn = ["dms" "ipc" "powermenu" "toggle"];
hotkey-overlay.title = "Toggle Power Menu";
};
"Mod+Left".action.focus-column-or-monitor-left = {};
"Mod+Down".action.focus-window-or-monitor-down = {};
"Mod+Up".action.focus-window-or-monitor-up = {};
"Mod+Right".action.focus-column-or-monitor-right = {};
"Mod+H".action.focus-column-or-monitor-left = {};
"Mod+J".action.focus-window-or-monitor-down = {};
"Mod+K".action.focus-window-or-monitor-up = {};
"Mod+L".action.focus-column-or-monitor-right = {};
"Mod+Shift+Left".action.move-column-left-or-to-monitor-left = {};
"Mod+Shift+Down".action.move-window-down = {};
"Mod+Shift+Up".action.move-window-up = {};
"Mod+Shift+Right".action.move-column-right-or-to-monitor-right = {};
"Mod+Shift+H".action.move-column-left-or-to-monitor-left = {};
"Mod+Shift+J".action.move-window-down = {};
"Mod+Shift+K".action.move-window-up = {};
"Mod+Shift+L".action.move-column-right-or-to-monitor-right = {};
"Mod+Home".action.focus-column-first = {};
"Mod+End".action.focus-column-last = {};
"Mod+Shift+Home".action.move-column-to-first = {};
"Mod+Shift+End".action.move-column-to-last = {};
"Mod+Ctrl+Left".action.focus-monitor-left = {};
"Mod+Ctrl+Down".action.focus-monitor-down = {};
"Mod+Ctrl+Up".action.focus-monitor-up = {};
"Mod+Ctrl+Right".action.focus-monitor-right = {};
"Mod+Ctrl+H".action.focus-monitor-left = {};
"Mod+Ctrl+J".action.focus-monitor-down = {};
"Mod+Ctrl+K".action.focus-monitor-up = {};
"Mod+Ctrl+L".action.focus-monitor-right = {};
"Mod+F".action.maximize-column = {};
"Mod+Shift+F".action.fullscreen-window = {};
"Mod+M".action.toggle-column-tabbed-display = {};
"Mod+A".action.toggle-overview = {};
"Mod+S".action.toggle-window-floating = {};
"Mod+C".action.switch-preset-column-width = {};
"Mod+Z".action.set-column-width = "-5%";
"Mod+X".action.set-column-width = "+5%";
"Mod+Ctrl+X".action.expand-column-to-available-width = {};
"Mod+Shift+Z".action.set-window-height = "-5%";
"Mod+Shift+X".action.set-window-height = "+5%";
"Mod+Shift+WheelScrollDown".action.focus-workspace-down = {};
"Mod+Shift+WheelScrollUp".action.focus-workspace-up = {};
"Mod+WheelScrollDown".action.focus-column-right = {};
"Mod+WheelScrollUp".action.focus-column-left = {};
"Mod+WheelScrollRight".action.focus-column-right = {};
"Mod+WheelScrollLeft".action.focus-column-left = {};
"Mod+Shift+WheelScrollRight".action.move-column-right = {};
"Mod+Shift+WheelScrollLeft".action.move-column-left = {};
"Mod+q".action.focus-workspace = "01";
"Mod+w".action.focus-workspace = "02";
"Mod+e".action.focus-workspace = "03";
"Mod+r".action.focus-workspace = "04";
"Mod+t".action.focus-workspace = "05";
"Mod+Shift+q".action.move-column-to-workspace = "01";
"Mod+Shift+w".action.move-column-to-workspace = "02";
"Mod+Shift+e".action.move-column-to-workspace = "03";
"Mod+Shift+r".action.move-column-to-workspace = "04";
"Mod+Shift+t".action.move-column-to-workspace = "05";
"Mod+Comma".action.consume-or-expel-window-left = {};
"Mod+Period".action.consume-or-expel-window-right = {};
"Print".action.screenshot = {};
"Ctrl+Print".action.screenshot-screen = {};
"Alt+Print".action.screenshot-window = {};
"Mod+Slash".action.show-hotkey-overlay = {};
"Mod+Return".action.spawn = ["ghostty"];
"Mod+Backspace".action.close-window = {};
};
outputs = lib.mkIf (config.monitors != []) (
lib.listToAttrs (map (monitor: {
inherit (monitor) name;
value = {
inherit (monitor) position;
mode = lib.mkIf (monitor.resolution.width != null && monitor.resolution.height != null) {
inherit (monitor.resolution) width height;
refresh = monitor.refreshRateNiri;
};
focus-at-startup = monitor.name == primaryMonitorName;
transform.rotation =
if (monitor.transform != null)
then monitor.transform * 90
else 0;
};
})
config.monitors)
);
};
};
}

View file

@ -0,0 +1,132 @@
{
config,
lib,
...
}: {
config = lib.mkIf config.fireproof.desktop.windowManager.enable {
fireproof.home-manager.programs.niri.settings.binds = {
"XF86AudioRaiseVolume" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "audio" "increment" "3"];
};
"XF86AudioLowerVolume" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "audio" "decrement" "3"];
};
"XF86AudioMute" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "audio" "mute"];
};
"XF86AudioMicMute" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "audio" "micmute"];
};
"XF86MonBrightnessUp" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "brightness" "increment" "5" ""];
};
"XF86MonBrightnessDown" = {
allow-when-locked = true;
action.spawn = ["dms" "ipc" "brightness" "decrement" "5" ""];
};
"Mod+N" = {
action.spawn = ["dms" "ipc" "notifications" "toggle"];
hotkey-overlay.title = "Toggle Notification Center";
};
"Mod+V" = {
action.spawn = ["dms" "ipc" "clipboard" "toggle"];
hotkey-overlay.title = "Toggle Clipboard Manager";
};
"Mod+Space" = {
action.spawn = ["dms" "ipc" "spotlight" "toggle"];
hotkey-overlay.title = "Toggle Application Launcher";
};
"Mod+Semicolon" = {
action.spawn = ["dms" "ipc" "spotlight" "toggleQuery" ":"];
hotkey-overlay.title = "Toggle Emoji Picker";
};
"Mod+P" = {
action.spawn = ["dms" "ipc" "powermenu" "toggle"];
hotkey-overlay.title = "Toggle Power Menu";
};
"Mod+Left".action.focus-column-or-monitor-left = {};
"Mod+Down".action.focus-window-or-monitor-down = {};
"Mod+Up".action.focus-window-or-monitor-up = {};
"Mod+Right".action.focus-column-or-monitor-right = {};
"Mod+H".action.focus-column-or-monitor-left = {};
"Mod+J".action.focus-window-or-monitor-down = {};
"Mod+K".action.focus-window-or-monitor-up = {};
"Mod+L".action.focus-column-or-monitor-right = {};
"Mod+Shift+Left".action.move-column-left-or-to-monitor-left = {};
"Mod+Shift+Down".action.move-window-down = {};
"Mod+Shift+Up".action.move-window-up = {};
"Mod+Shift+Right".action.move-column-right-or-to-monitor-right = {};
"Mod+Shift+H".action.move-column-left-or-to-monitor-left = {};
"Mod+Shift+J".action.move-window-down = {};
"Mod+Shift+K".action.move-window-up = {};
"Mod+Shift+L".action.move-column-right-or-to-monitor-right = {};
"Mod+Home".action.focus-column-first = {};
"Mod+End".action.focus-column-last = {};
"Mod+Shift+Home".action.move-column-to-first = {};
"Mod+Shift+End".action.move-column-to-last = {};
"Mod+Ctrl+Left".action.focus-monitor-left = {};
"Mod+Ctrl+Down".action.focus-monitor-down = {};
"Mod+Ctrl+Up".action.focus-monitor-up = {};
"Mod+Ctrl+Right".action.focus-monitor-right = {};
"Mod+Ctrl+H".action.focus-monitor-left = {};
"Mod+Ctrl+J".action.focus-monitor-down = {};
"Mod+Ctrl+K".action.focus-monitor-up = {};
"Mod+Ctrl+L".action.focus-monitor-right = {};
"Mod+F".action.maximize-column = {};
"Mod+Shift+F".action.fullscreen-window = {};
"Mod+M".action.toggle-column-tabbed-display = {};
"Mod+A".action.toggle-overview = {};
"Mod+S".action.toggle-window-floating = {};
"Mod+C".action.switch-preset-column-width = {};
"Mod+Z".action.set-column-width = "-5%";
"Mod+X".action.set-column-width = "+5%";
"Mod+Ctrl+X".action.expand-column-to-available-width = {};
"Mod+Shift+Z".action.set-window-height = "-5%";
"Mod+Shift+X".action.set-window-height = "+5%";
"Mod+Shift+WheelScrollDown".action.focus-workspace-down = {};
"Mod+Shift+WheelScrollUp".action.focus-workspace-up = {};
"Mod+WheelScrollDown".action.focus-column-right = {};
"Mod+WheelScrollUp".action.focus-column-left = {};
"Mod+WheelScrollRight".action.focus-column-right = {};
"Mod+WheelScrollLeft".action.focus-column-left = {};
"Mod+Shift+WheelScrollRight".action.move-column-right = {};
"Mod+Shift+WheelScrollLeft".action.move-column-left = {};
"Mod+q".action.focus-workspace = "01";
"Mod+w".action.focus-workspace = "02";
"Mod+e".action.focus-workspace = "03";
"Mod+r".action.focus-workspace = "04";
"Mod+t".action.focus-workspace = "05";
"Mod+Shift+q".action.move-column-to-workspace = "01";
"Mod+Shift+w".action.move-column-to-workspace = "02";
"Mod+Shift+e".action.move-column-to-workspace = "03";
"Mod+Shift+r".action.move-column-to-workspace = "04";
"Mod+Shift+t".action.move-column-to-workspace = "05";
"Mod+Comma".action.consume-or-expel-window-left = {};
"Mod+Period".action.consume-or-expel-window-right = {};
"Print".action.screenshot = {};
"Ctrl+Print".action.screenshot-screen = {};
"Alt+Print".action.screenshot-window = {};
"Mod+Slash".action.show-hotkey-overlay = {};
"Mod+Return".action.spawn = ["ghostty"];
"Mod+Backspace".action.close-window = {};
};
};
}

View file

@ -0,0 +1,29 @@
{
config,
lib,
pkgs,
inputs,
...
}: {
imports = [
./settings.nix
./binds.nix
./outputs.nix
];
config = lib.mkIf config.fireproof.desktop.windowManager.enable {
programs.xwayland.enable = true;
xdg.portal = {
enable = true;
extraPortals = [pkgs.xdg-desktop-portal-gtk];
config.common.default = "gnome";
xdgOpenUsePortal = true;
};
programs.niri = {
enable = true;
package = inputs.niri.packages."${pkgs.stdenv.hostPlatform.system}".niri-unstable;
};
};
}

View file

@ -0,0 +1,42 @@
{
config,
lib,
...
}: let
hasMonitors = config.monitors != [];
primaryMonitorName =
if hasMonitors
then (builtins.head config.monitors).name or ""
else "";
in {
config = lib.mkIf config.fireproof.desktop.windowManager.enable {
fireproof.home-manager.programs.niri.settings = {
workspaces = lib.mkIf (primaryMonitorName != "") {
"01".open-on-output = primaryMonitorName;
"02".open-on-output = primaryMonitorName;
"03".open-on-output = primaryMonitorName;
"04".open-on-output = primaryMonitorName;
"05".open-on-output = primaryMonitorName;
};
outputs = lib.mkIf (config.monitors != []) (
lib.listToAttrs (map (monitor: {
inherit (monitor) name;
value = {
inherit (monitor) position;
mode = lib.mkIf (monitor.resolution.width != null && monitor.resolution.height != null) {
inherit (monitor.resolution) width height;
refresh = monitor.refreshRateNiri;
};
focus-at-startup = monitor.name == primaryMonitorName;
transform.rotation =
if (monitor.transform != null)
then monitor.transform * 90
else 0;
};
})
config.monitors)
);
};
};
}

View file

@ -0,0 +1,64 @@
{
config,
lib,
pkgs,
inputs,
...
}: let
c = config.fireproof.theme.colors;
in {
config = lib.mkIf config.fireproof.desktop.windowManager.enable {
fireproof.home-manager.programs.niri.settings = {
prefer-no-csd = true;
clipboard.disable-primary = true;
spawn-at-startup = [
{command = ["systemctl" "--user" "start" "hyprpaper"];}
];
xwayland-satellite = {
enable = true;
path = lib.getExe inputs.niri.packages."${pkgs.stdenv.hostPlatform.system}".xwayland-satellite-unstable;
};
environment = {
NIXOS_OZONE_WL = "1";
GDK_BACKEND = "wayland";
};
layout = {
gaps = 10;
focus-ring.enable = false;
insert-hint.display.color = "#${c.accent}";
border = {
enable = true;
width = 2;
active.color = "#${c.accent}";
inactive.color = "#${c.ui}";
};
tab-indicator = {
hide-when-single-tab = true;
place-within-column = true;
gap = 2;
position = "top";
corner-radius = 8;
};
};
input = {
focus-follows-mouse = {
enable = true;
max-scroll-amount = "0%";
};
mouse.accel-profile = "flat";
keyboard.xkb.layout = "eu";
};
window-rules = [
{
clip-to-geometry = true;
geometry-corner-radius = {
top-left = 8.0;
top-right = 8.0;
bottom-left = 8.0;
bottom-right = 8.0;
};
}
];
};
};
}

View file

@ -8,7 +8,7 @@
./audiobookshelf.nix ./audiobookshelf.nix
./freshrss.nix ./freshrss.nix
./glance.nix ./glance.nix
./home-assistant.nix ./home-assistant
./jellyfin.nix ./jellyfin.nix
./nextcloud.nix ./nextcloud.nix
./nginx.nix ./nginx.nix
@ -19,7 +19,7 @@
./restic.nix ./restic.nix
./scrutiny.nix ./scrutiny.nix
./security.nix ./security.nix
./sso.nix ./sso
./vaultwarden.nix ./vaultwarden.nix
]; ];
} }

View file

@ -1,224 +0,0 @@
{
pkgs,
config,
lib,
...
}:
lib.mkIf config.fireproof.homelab.enable (let
mosquittoPort = 1883;
zigbee2mqttPort = 8180;
homeAssistantPort = 8123;
in {
age.secrets = {
"zigbee2mqtt-secret.yaml" = {
rekeyFile = ../../secrets/hosts/homelab/zigbee2mqtt-secret.yaml.age;
owner = "zigbee2mqtt";
group = "zigbee2mqtt";
};
mosquitto-zigbee2mqtt.rekeyFile = ../../secrets/hosts/homelab/mosquitto-zigbee2mqtt.age;
mosquitto-sas.rekeyFile = ../../secrets/hosts/homelab/mosquitto-sas.age;
mosquitto-ha.rekeyFile = ../../secrets/hosts/homelab/mosquitto-ha.age;
hassSecrets = {
rekeyFile = ../../secrets/hosts/homelab/hass.yaml.age;
path = "${config.services.home-assistant.configDir}/secrets.yaml";
mode = "400";
owner = "hass";
group = "hass";
};
};
networking.firewall.allowedTCPPorts = [
mosquittoPort
];
services = {
restic.backups.homelab = {
paths = [
config.services.zigbee2mqtt.dataDir
config.services.home-assistant.configDir
];
exclude = [
"/var/lib/zigbee2mqtt/log/"
];
};
oauth2-proxy.nginx.virtualHosts."zigbee.nickolaj.com".allowed_groups = ["iot-admin"];
nginx.virtualHosts = {
"zigbee.nickolaj.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString zigbee2mqttPort}";
proxyWebsockets = true;
};
};
"ha.nickolaj.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString homeAssistantPort}";
proxyWebsockets = true;
};
};
};
home-assistant = {
enable = true;
package = pkgs.home-assistant;
customComponents = with pkgs.home-assistant-custom-components; [
adaptive_lighting
sleep_as_android_mqtt
(pkgs.buildHomeAssistantComponent rec {
owner = "Sian-Lee-SA";
domain = "switch_manager";
version = "v2.0.4b";
src = pkgs.fetchFromGitHub {
inherit owner;
repo = "Home-Assistant-Switch-Manager";
rev = version;
hash = "sha256-W9xO3JjnRKHk/dlXMA6y5nEJl/KsGzPvJoumGw+nohw=";
};
})
(pkgs.buildHomeAssistantComponent rec {
owner = "snicker";
domain = "zwift";
version = "v3.3.5";
src = pkgs.fetchFromGitHub {
inherit owner;
repo = "zwift_hass";
rev = version;
hash = "sha256-+lJ6Otp8lT+xVtjiQLSQrqT5cVinRTRPTzS+HB1AxB0=";
};
propagatedBuildInputs = [
(pkgs.python313.pkgs.buildPythonPackage rec {
pname = "zwift-client";
version = "0.2.0";
pyproject = true;
src = pkgs.fetchFromGitHub {
owner = "nickolaj-jepsen";
repo = "zwift-client";
rev = "882fb881f1271dc104fd0250cab4ceb6e3710a59";
hash = "sha256-4gOlWG+QVwODlIhiNH7rhiD0rzNv2WxY2ty9o/51eHU=";
};
doCheck = false;
propagatedBuildInputs = with pkgs.python313.pkgs; [
hatchling
requests
protobuf
];
})
];
})
];
extraComponents = [
"default_config"
"met"
"mqtt"
"esphome"
"google"
"spotify"
"unifi"
"upnp"
"homeassistant_hardware"
];
config = {
homeassistant = {
name = "Home";
latitude = "!secret latitude";
longitude = "!secret longitude";
elevation = "!secret elevation";
unit_system = "metric";
time_zone = "Europe/Copenhagen";
};
frontend = {
themes = "!include_dir_merge_named themes";
};
http = {
server_port = homeAssistantPort;
use_x_forwarded_for = true;
trusted_proxies = [
"127.0.0.1"
"::1"
];
};
sensor = [
{
platform = "zwift";
username = "!secret zwift_username";
password = "!secret zwift_password";
}
];
automation = "!include automations.yaml";
script = "!include scripts.yaml";
scene = "!include scenes.yaml";
};
};
mosquitto = {
enable = true;
listeners = [
{
port = mosquittoPort;
users."zigbee2mqtt" = {
acl = ["readwrite #"];
passwordFile = "${config.age.secrets.mosquitto-zigbee2mqtt.path}";
};
users."homeassistant" = {
acl = ["readwrite #"];
passwordFile = "${config.age.secrets.mosquitto-ha.path}";
};
users."sleep_as_android" = {
acl = ["readwrite SleepAsAndroid"];
passwordFile = "${config.age.secrets.mosquitto-sas.path}";
};
}
];
};
zigbee2mqtt = {
enable = true;
settings = {
homeassistant = {
enabled = true;
};
mqtt = {
base_topic = "zigbee2mqtt";
server = "mqtt://localhost:${toString mosquittoPort}";
user = "zigbee2mqtt";
password = "!${config.age.secrets."zigbee2mqtt-secret.yaml".path} password";
};
frontend = {
enabled = true;
port = zigbee2mqttPort;
};
serial = {
port = "/dev/serial/by-id/usb-Silicon_Labs_Sonoff_Zigbee_3.0_USB_Dongle_Plus_0001-if00-port0";
adapter = "zstack";
};
advanced = {
network_key = [
233
138
136
76
51
117
128
127
74
84
33
179
116
61
79
101
];
channel = 25;
log_level = "debug";
};
};
};
};
})

View file

@ -0,0 +1,6 @@
{...}: {
imports = [
./hass.nix
./mqtt.nix
];
}

View file

@ -0,0 +1,124 @@
{
pkgs,
config,
lib,
...
}: let
homeAssistantPort = 8123;
in {
config = lib.mkIf config.fireproof.homelab.enable {
age.secrets.hassSecrets = {
rekeyFile = ../../../secrets/hosts/homelab/hass.yaml.age;
path = "${config.services.home-assistant.configDir}/secrets.yaml";
mode = "400";
owner = "hass";
group = "hass";
};
services.restic.backups.homelab = {
paths = [config.services.home-assistant.configDir];
};
services.nginx.virtualHosts."ha.nickolaj.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString homeAssistantPort}";
proxyWebsockets = true;
};
};
services.home-assistant = {
enable = true;
package = pkgs.home-assistant;
customComponents = with pkgs.home-assistant-custom-components; [
adaptive_lighting
sleep_as_android_mqtt
(pkgs.buildHomeAssistantComponent rec {
owner = "Sian-Lee-SA";
domain = "switch_manager";
version = "v2.0.4b";
src = pkgs.fetchFromGitHub {
inherit owner;
repo = "Home-Assistant-Switch-Manager";
rev = version;
hash = "sha256-W9xO3JjnRKHk/dlXMA6y5nEJl/KsGzPvJoumGw+nohw=";
};
})
(pkgs.buildHomeAssistantComponent rec {
owner = "snicker";
domain = "zwift";
version = "v3.3.5";
src = pkgs.fetchFromGitHub {
inherit owner;
repo = "zwift_hass";
rev = version;
hash = "sha256-+lJ6Otp8lT+xVtjiQLSQrqT5cVinRTRPTzS+HB1AxB0=";
};
propagatedBuildInputs = [
(pkgs.python313.pkgs.buildPythonPackage {
pname = "zwift-client";
version = "0.2.0";
pyproject = true;
src = pkgs.fetchFromGitHub {
owner = "nickolaj-jepsen";
repo = "zwift-client";
rev = "882fb881f1271dc104fd0250cab4ceb6e3710a59";
hash = "sha256-4gOlWG+QVwODlIhiNH7rhiD0rzNv2WxY2ty9o/51eHU=";
};
doCheck = false;
propagatedBuildInputs = with pkgs.python313.pkgs; [
hatchling
requests
protobuf
];
})
];
})
];
extraComponents = [
"default_config"
"met"
"mqtt"
"esphome"
"google"
"spotify"
"unifi"
"upnp"
"homeassistant_hardware"
];
config = {
homeassistant = {
name = "Home";
latitude = "!secret latitude";
longitude = "!secret longitude";
elevation = "!secret elevation";
unit_system = "metric";
time_zone = "Europe/Copenhagen";
};
frontend = {
themes = "!include_dir_merge_named themes";
};
http = {
server_port = homeAssistantPort;
use_x_forwarded_for = true;
trusted_proxies = [
"127.0.0.1"
"::1"
];
};
sensor = [
{
platform = "zwift";
username = "!secret zwift_username";
password = "!secret zwift_password";
}
];
automation = "!include automations.yaml";
script = "!include scripts.yaml";
scene = "!include scenes.yaml";
};
};
};
}

View file

@ -0,0 +1,104 @@
{
config,
lib,
...
}: let
mosquittoPort = 1883;
zigbee2mqttPort = 8180;
in {
config = lib.mkIf config.fireproof.homelab.enable {
age.secrets = {
"zigbee2mqtt-secret.yaml" = {
rekeyFile = ../../../secrets/hosts/homelab/zigbee2mqtt-secret.yaml.age;
owner = "zigbee2mqtt";
group = "zigbee2mqtt";
};
mosquitto-zigbee2mqtt.rekeyFile = ../../../secrets/hosts/homelab/mosquitto-zigbee2mqtt.age;
mosquitto-sas.rekeyFile = ../../../secrets/hosts/homelab/mosquitto-sas.age;
mosquitto-ha.rekeyFile = ../../../secrets/hosts/homelab/mosquitto-ha.age;
};
networking.firewall.allowedTCPPorts = [mosquittoPort];
services.restic.backups.homelab = {
paths = [config.services.zigbee2mqtt.dataDir];
exclude = ["/var/lib/zigbee2mqtt/log/"];
};
services.oauth2-proxy.nginx.virtualHosts."zigbee.nickolaj.com".allowed_groups = ["iot-admin"];
services.nginx.virtualHosts."zigbee.nickolaj.com" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString zigbee2mqttPort}";
proxyWebsockets = true;
};
};
services.mosquitto = {
enable = true;
listeners = [
{
port = mosquittoPort;
users."zigbee2mqtt" = {
acl = ["readwrite #"];
passwordFile = "${config.age.secrets.mosquitto-zigbee2mqtt.path}";
};
users."homeassistant" = {
acl = ["readwrite #"];
passwordFile = "${config.age.secrets.mosquitto-ha.path}";
};
users."sleep_as_android" = {
acl = ["readwrite SleepAsAndroid"];
passwordFile = "${config.age.secrets.mosquitto-sas.path}";
};
}
];
};
services.zigbee2mqtt = {
enable = true;
settings = {
homeassistant = {
enabled = true;
};
mqtt = {
base_topic = "zigbee2mqtt";
server = "mqtt://localhost:${toString mosquittoPort}";
user = "zigbee2mqtt";
password = "!${config.age.secrets."zigbee2mqtt-secret.yaml".path} password";
};
frontend = {
enabled = true;
port = zigbee2mqttPort;
};
serial = {
port = "/dev/serial/by-id/usb-Silicon_Labs_Sonoff_Zigbee_3.0_USB_Dongle_Plus_0001-if00-port0";
adapter = "zstack";
};
advanced = {
network_key = [
233
138
136
76
51
117
128
127
74
84
33
179
116
61
79
101
];
channel = 25;
log_level = "debug";
};
};
};
};
}

View file

@ -1,116 +0,0 @@
{
config,
pkgsUnstable,
lib,
...
}:
lib.mkIf config.fireproof.homelab.enable (let
port = 9190;
rootDomain = "nickolaj.com";
zitadelDomain = "sso.${rootDomain}";
oathproxyDomain = "oauth2-proxy.${rootDomain}";
in {
age.secrets.zitadel-master = {
rekeyFile = ../../secrets/hosts/homelab/zitadel-master.age;
owner = config.services.zitadel.user;
};
age.secrets.oauth2-proxy = {
rekeyFile = ../../secrets/hosts/homelab/oauth2-proxy-keyfile.age;
owner = "oauth2-proxy";
};
services.nginx.virtualHosts."${zitadelDomain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:${toString port}";
extraConfig = ''
grpc_pass grpc://127.0.0.1:${toString port};
grpc_set_header Host $host:$server_port;
'';
};
};
services.nginx.virtualHosts."${oathproxyDomain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyWebsockets = true;
proxyPass = "http://127.0.0.1:4180";
};
};
services.postgresql = {
ensureDatabases = ["zitadel"];
ensureUsers = [
{
name = "zitadel";
ensureDBOwnership = true;
ensureClauses.login = true;
}
];
};
services.zitadel = {
enable = true;
package = pkgsUnstable.zitadel;
masterKeyFile = config.age.secrets.zitadel-master.path;
settings = {
Port = port;
Database.postgres = {
Host = "/var/run/postgresql/";
Port = 5432;
Database = "zitadel";
User = {
Username = "zitadel";
SSL.Mode = "disable";
};
Admin = {
Username = "zitadel";
SSL.Mode = "disable";
ExistingDatabase = "zitadel";
};
};
ExternalDomain = zitadelDomain;
ExternalPort = 443;
ExternalSecure = true;
};
steps.FirstInstance = {
InstanceName = "Fireproof Auth";
Org = {
Name = "Fireproof Auth";
Human = {
UserName = "nickolaj1177@gmail.com";
FirstName = "Nickolaj";
LastName = "Jepsen";
Email.Verified = true;
Password = "Password1!";
PasswordChangeRequired = true;
};
};
LoginPolicy.AllowRegister = false;
};
};
services.oauth2-proxy = {
enable = true;
provider = "oidc";
reverseProxy = true;
redirectURL = "https://${oathproxyDomain}/oauth2/callback";
validateURL = "https://${zitadelDomain}/oauth2/";
oidcIssuerUrl = "https://${zitadelDomain}:443";
keyFile = config.age.secrets.oauth2-proxy.path;
passBasicAuth = true;
setXauthrequest = true;
nginx.domain = oathproxyDomain;
email.domains = ["*"];
extraConfig = {
whitelist-domain = ".${rootDomain}";
cookie-domain = ".${rootDomain}";
};
};
systemd.services.oauth2-proxy.serviceConfig = {
Restart = "always";
RestartSec = "5s";
};
})

View file

@ -0,0 +1,6 @@
{...}: {
imports = [
./zitadel.nix
./proxy.nix
];
}

View file

@ -0,0 +1,48 @@
{
config,
lib,
...
}: let
rootDomain = "nickolaj.com";
zitadelDomain = "sso.${rootDomain}";
oathproxyDomain = "oauth2-proxy.${rootDomain}";
in {
config = lib.mkIf config.fireproof.homelab.enable {
age.secrets.oauth2-proxy = {
rekeyFile = ../../../secrets/hosts/homelab/oauth2-proxy-keyfile.age;
owner = "oauth2-proxy";
};
services.nginx.virtualHosts."${oathproxyDomain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyWebsockets = true;
proxyPass = "http://127.0.0.1:4180";
};
};
services.oauth2-proxy = {
enable = true;
provider = "oidc";
reverseProxy = true;
redirectURL = "https://${oathproxyDomain}/oauth2/callback";
validateURL = "https://${zitadelDomain}/oauth2/";
oidcIssuerUrl = "https://${zitadelDomain}:443";
keyFile = config.age.secrets.oauth2-proxy.path;
passBasicAuth = true;
setXauthrequest = true;
nginx.domain = oathproxyDomain;
email.domains = ["*"];
extraConfig = {
whitelist-domain = ".${rootDomain}";
cookie-domain = ".${rootDomain}";
};
};
systemd.services.oauth2-proxy.serviceConfig = {
Restart = "always";
RestartSec = "5s";
};
};
}

View file

@ -0,0 +1,80 @@
{
config,
pkgsUnstable,
lib,
...
}: let
port = 9190;
zitadelDomain = "sso.nickolaj.com";
in {
config = lib.mkIf config.fireproof.homelab.enable {
age.secrets.zitadel-master = {
rekeyFile = ../../../secrets/hosts/homelab/zitadel-master.age;
owner = config.services.zitadel.user;
};
services.nginx.virtualHosts."${zitadelDomain}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:${toString port}";
extraConfig = ''
grpc_pass grpc://127.0.0.1:${toString port};
grpc_set_header Host $host:$server_port;
'';
};
};
services.postgresql = {
ensureDatabases = ["zitadel"];
ensureUsers = [
{
name = "zitadel";
ensureDBOwnership = true;
ensureClauses.login = true;
}
];
};
services.zitadel = {
enable = true;
package = pkgsUnstable.zitadel;
masterKeyFile = config.age.secrets.zitadel-master.path;
settings = {
Port = port;
Database.postgres = {
Host = "/var/run/postgresql/";
Port = 5432;
Database = "zitadel";
User = {
Username = "zitadel";
SSL.Mode = "disable";
};
Admin = {
Username = "zitadel";
SSL.Mode = "disable";
ExistingDatabase = "zitadel";
};
};
ExternalDomain = zitadelDomain;
ExternalPort = 443;
ExternalSecure = true;
};
steps.FirstInstance = {
InstanceName = "Fireproof Auth";
Org = {
Name = "Fireproof Auth";
Human = {
UserName = "nickolaj1177@gmail.com";
FirstName = "Nickolaj";
LastName = "Jepsen";
Email.Verified = true;
Password = "Password1!";
PasswordChangeRequired = true;
};
};
LoginPolicy.AllowRegister = false;
};
};
};
}