summaryrefslogtreecommitdiff
path: root/hosts
diff options
context:
space:
mode:
Diffstat (limited to 'hosts')
-rw-r--r--hosts/T495/configuration.nix88
-rw-r--r--hosts/T495/hardware-configuration.nix49
-rw-r--r--hosts/T495/resources/secrets/wg.yaml17
-rw-r--r--hosts/T495/wg.nix9
-rw-r--r--hosts/X230/configuration.nix65
-rw-r--r--hosts/X230/hardware-configuration.nix48
-rw-r--r--hosts/X230/resources/secrets/wg0.yaml16
-rw-r--r--hosts/X230/wg.nix7
-rw-r--r--hosts/flex-wg-router/configuration.nix83
-rw-r--r--hosts/flex-wg-router/hardware-configuration.nix45
-rw-r--r--hosts/flex-wg-router/resources/secrets/wg.yaml17
-rw-r--r--hosts/flex-wg-router/wg.nix9
-rw-r--r--hosts/gnuslashprinter/configuration.nix29
-rw-r--r--hosts/gnuslashprinter/flask-relay-ctl.nix37
-rw-r--r--hosts/gnuslashprinter/hardware-configuration.nix44
-rw-r--r--hosts/gnuslashprinter/klipper.nix100
-rw-r--r--hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml10
-rw-r--r--hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py54
-rw-r--r--hosts/gnuslashprinter/resources/klipper/mcu/config109
-rw-r--r--hosts/gnuslashprinter/resources/klipper/printer.cfg180
-rw-r--r--hosts/hp-envy-office/configuration.nix69
-rw-r--r--hosts/hp-envy-office/hardware-configuration.nix48
-rw-r--r--hosts/hp-envy-office/resources/secrets/wg.yaml16
-rw-r--r--hosts/hp-envy-office/wg.nix7
-rw-r--r--hosts/libreX60/bios-flashing.nix8
-rw-r--r--hosts/libreX60/configuration.nix47
-rw-r--r--hosts/libreX60/hardware-configuration.nix46
-rw-r--r--hosts/libreX60/home-timmy/configuration.nix8
-rw-r--r--hosts/libreX60/home.nix22
-rw-r--r--hosts/libreX60/powertop-auto-tune.nix17
-rw-r--r--hosts/optiplex/configuration.nix109
-rw-r--r--hosts/optiplex/g610.nix9
-rw-r--r--hosts/optiplex/hardware-configuration.nix44
-rw-r--r--hosts/piframe/configuration.nix77
-rw-r--r--hosts/piframe/hardware-configuration.nix24
-rw-r--r--hosts/poweredge/bitcoind.nix41
-rw-r--r--hosts/poweredge/configuration.nix45
-rw-r--r--hosts/poweredge/ddns-updater.nix22
-rw-r--r--hosts/poweredge/filebrowser.nix48
-rw-r--r--hosts/poweredge/fileshares.nix47
-rw-r--r--hosts/poweredge/gitea.nix41
-rw-r--r--hosts/poweredge/hardware-configuration.nix50
-rw-r--r--hosts/poweredge/immich.nix78
-rw-r--r--hosts/poweredge/jellyfin.nix91
-rw-r--r--hosts/poweredge/lgtm.nix201
-rw-r--r--hosts/poweredge/networking.nix157
-rw-r--r--hosts/poweredge/notification-mailer.nix27
-rw-r--r--hosts/poweredge/nvidia.nix22
-rw-r--r--hosts/poweredge/resources/secrets/ddns-updater-config.yaml16
-rw-r--r--hosts/poweredge/resources/secrets/grafana.yaml16
-rw-r--r--hosts/poweredge/resources/secrets/mailer-pass.yaml16
-rw-r--r--hosts/poweredge/resources/secrets/router.yaml17
-rw-r--r--hosts/poweredge/resources/secrets/transmission.yaml17
-rw-r--r--hosts/poweredge/router-hosts.nix84
-rw-r--r--hosts/poweredge/transmission.nix118
-rw-r--r--hosts/sweetiepc/configuration.nix51
-rw-r--r--hosts/sweetiepc/hardware-configuration.nix38
-rw-r--r--hosts/sweetiepc/resources/secrets/wg.yaml16
-rw-r--r--hosts/sweetiepc/wg.nix7
59 files changed, 2833 insertions, 0 deletions
diff --git a/hosts/T495/configuration.nix b/hosts/T495/configuration.nix
new file mode 100644
index 0000000..b80f27e
--- /dev/null
+++ b/hosts/T495/configuration.nix
@@ -0,0 +1,88 @@
+{ config, lib, pkgs, ... }: {
+ imports = [ ./wg.nix ];
+
+ # Setup bootloader
+ boot._loader.enable = true;
+
+ # Enable common options
+ _archetypes = {
+ profiles = {
+ desktop = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ btrfs.enable = true;
+ };
+ collections = {
+ desktop = {
+ extraUtilities.enable = true;
+ cad.enable = true;
+ chromium.enable = true;
+ crypto.enable = true;
+ graphics.enable = true;
+ office.enable = true;
+ };
+ development = {
+ android.enable = true;
+ c.enable = true;
+ docker.enable = true;
+ lua.enable = true;
+ web = {
+ hugo = {
+ enable = true;
+ openFirewall = true;
+ };
+ node.enable = true;
+ };
+ };
+ bluetooth.enable = true;
+ };
+ };
+
+ # Enable user timmy
+ _users.timmy = {
+ enable = true;
+ autologin.enable = true;
+ nas = {
+ enable = true;
+ office.enable = true;
+ };
+ wifi.enable = true;
+ };
+
+ # Install spotify
+ nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
+ "spotify"
+ ];
+ environment.systemPackages = with pkgs; [
+ spotify
+ ];
+
+ # Use amdgpu driver for x11
+ services.xserver.videoDrivers = [ "amdgpu" ];
+
+ # Configure home
+ home-manager.users.timmy = {
+ gtk._mintTheme = {
+ dark = true;
+ color = "Purple";
+ icons.color = "Purple";
+ };
+ programs._st = {
+ enable = true;
+ font = {
+ name = "TamzenForPowerline";
+ attrs = {
+ pixelsize = 20;
+ };
+ };
+ };
+ programs._seasonalwallpaper.wallpapers.download = true;
+ fonts.fontconfig = {
+ subpixelRendering = "rgb";
+ hinting = "slight";
+ };
+ };
+
+ system.stateVersion = "24.05";
+}
diff --git a/hosts/T495/hardware-configuration.nix b/hosts/T495/hardware-configuration.nix
new file mode 100644
index 0000000..6258a83
--- /dev/null
+++ b/hosts/T495/hardware-configuration.nix
@@ -0,0 +1,49 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "nvme" "ehci_pci" "xhci_pci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ "kvm-amd" ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/4256823f-107b-41a9-851d-6bd9939f1f4b";
+ fsType = "btrfs";
+ options = [ "subvol=@" ];
+ };
+
+ boot.initrd.luks.devices."cryptroot".device = "/dev/disk/by-uuid/f20115a9-0a0e-43d2-9cee-c705b2de43b9";
+
+ fileSystems."/home" =
+ { device = "/dev/disk/by-uuid/4256823f-107b-41a9-851d-6bd9939f1f4b";
+ fsType = "btrfs";
+ options = [ "subvol=@home" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/9441-53B1";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ swapDevices = [ ];
+
+ # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+ # (the default) this is the recommended approach. When using systemd-networkd it's
+ # still possible to use this option, but it's recommended to use it in conjunction
+ # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+ networking.useDHCP = lib.mkDefault true;
+ # networking.interfaces.enp3s0f0.useDHCP = lib.mkDefault true;
+ # networking.interfaces.enp4s0.useDHCP = lib.mkDefault true;
+ # networking.interfaces.wlp1s0.useDHCP = lib.mkDefault true;
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/T495/resources/secrets/wg.yaml b/hosts/T495/resources/secrets/wg.yaml
new file mode 100644
index 0000000..7581228
--- /dev/null
+++ b/hosts/T495/resources/secrets/wg.yaml
@@ -0,0 +1,17 @@
+wg0: ENC[AES256_GCM,data:ZWvG10UxVMb7e8wxTgQqybp2NsziJdTRDqF/ZJ1N3bf220P3l59jQcHyHi7l9UFbUhErM/zxXc0/B7AE1TPzcFGZRxqQC3TenCCW8RuKkm9bXcctseXtLqRr/EYRH07952dTzvdN/eBxLXLPqgi/K1qnuWBLS2zf/WKAJyCFsKPlzhH6lA7+VAM16Q9m+0nktruQ34DMok1LBlN7DN/gZkJWx6Ipzst4f9lrDiSm1QNmxskGUqDdPWednIJ8d8iW2j5xFBgzE0iUyZu0VwuOqP2ioUTYLiLGbx2/XrRXyz66u5gx1agBtpeAirWmNcmJlDl8olik64tXQoemHUHXQokpsuR1ENnm2DFqB1lVbXA3Q00cyzYX08cii7olGHKbVaty2n4RMAFwTBh2fRYs4LsH+cyn4umhLkOhUEbFInujrA5vVltQv9h/r4Y22osBgVNuSQ4I5hl4d348yc45yCENnW6iSs8Diu141l+IbxvQPIpbR462ctYtM+TRJW4AFbgPUdHrRD7DrURnyP9ZePSIU/1pPFZNeNJjYwDETXaz,iv:WLgAIxHhKLgw8TGfuXTlh7rkcjIawYjb3J6mo3sRSmE=,tag:b7tZzypGa8EmUinCJcDkoQ==,type:str]
+wg1: ENC[AES256_GCM,data:MCojgM00HJuTgY+K6KBUnDqH4Y8Jerc37vnYO4SC/srlNXxp5QKAuyTDwb7bjmGboyOHq//GLbRMDN5vZEeUQNGyjsycElmSl76bcLS2XPdGrbxGZD7HQY9PY+Z0V1avwVovRrITfG2irffnVxqfis8R5GBOovy/MRhyjG8B6vhastYoGp1ncAmAV+QsUGn/MTeKr+MpZ1Q+SGyeu8gAvPknOEzrR8WqK0ySbWKCQjeEe38kLNwhSNI1cp4Md02dskNUWnVKRyvfXOuYoPsGAmhVikZ+C4yFlndEJoQ5IJPbTO0mv2394bKtflCyB2hnerrTd+NaZcRgPP72Lvw3gDmx7olweL7f2uzr5Vs4NO1AIWcw/MkJxW2UU4GzHbJZM7H3b/IUY1+y8UDEk4rtBT9t+M5LapuVbpagwulIP7+O7Bjsgb4ZJxaGDjtUe3JcUgMDRAexDa7lioDZMpWT5UPRvUczfncqXs+b40vo02eZqiAxtWVK/8587uo=,iv:lsCA/USQc6ADWdRVYIQHXQtdtukEf7cXNTXIKzlynDU=,tag:xlW4MCIwMC8A8f2maHhPQg==,type:str]
+sops:
+ age:
+ - enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4NEN4NGxDR1oreGVoSGhE
+ TzMxSEY0QVBhS2Z6MW15ci9aVlJ0a3IyVlZBCldPRVNvcUhJSHhWSEk3akd4RjN0
+ ajhUV2d1ZWRsRFU4cTE2dGl6RmM4MGsKLS0tIFhnUjl5aDJqWVB1NE15SlNzR2Iv
+ YTNydURsOUMrSXZGdk9UOUdUQlA0SFUKxEDJRR6tpYva9qpWo9NxwCxk/xpRVoTl
+ YJkmDZzMcXikXXiro96AprP9dXJXvMPKYPGl2Zsal8PlGFPBoHW2GA==
+ -----END AGE ENCRYPTED FILE-----
+ recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv
+ lastmodified: "2026-06-19T19:30:00Z"
+ mac: ENC[AES256_GCM,data:tqZqi1l/15EAq/aqiXHmmi1aPUrdKJiK7pQZa5bUOdThfB3CVIQ4s0lfvybhMJfU6gbuiGih/Umr0bJ9joaFozFn/mPPUj7sjuqzHk1bxMG3qp1kHmmegAVBtjv0Fht62N1MFtH8woG3k+iRmd0ndTdFCMa0OVDvsCzw3t11wbw=,iv:/mQWM769XROsm6MEHpEsX8TiBEQd6PWODkb9CkH0eZY=,tag:oMObl25mPwPcqluWwUefcQ==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.13.1
diff --git a/hosts/T495/wg.nix b/hosts/T495/wg.nix
new file mode 100644
index 0000000..7b98ad8
--- /dev/null
+++ b/hosts/T495/wg.nix
@@ -0,0 +1,9 @@
+{ config, pkgs, inputs, ... }: {
+ sops.secrets.wg0 = { sopsFile = ./resources/secrets/wg.yaml; key = "wg0"; }; # Office
+ sops.secrets.wg1 = { sopsFile = ./resources/secrets/wg.yaml; key = "wg1"; }; # Home
+
+ networking.wg-quick.interfaces = {
+ wg0.configFile = config.sops.secrets.wg0.path;
+ wg1.configFile = config.sops.secrets.wg1.path;
+ };
+}
diff --git a/hosts/X230/configuration.nix b/hosts/X230/configuration.nix
new file mode 100644
index 0000000..22856f2
--- /dev/null
+++ b/hosts/X230/configuration.nix
@@ -0,0 +1,65 @@
+{ config, lib, pkgs, ... }: {
+ imports = [ ./wg.nix ];
+
+ # Setup bootloader
+ boot._loader.enable = true;
+
+ # Enable common options
+ _archetypes = {
+ profiles = {
+ desktop = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ btrfs.enable = true;
+ };
+ collections = {
+ desktop = {
+ crypto.enable = true;
+ graphics.enable = true;
+ office.enable = true;
+ };
+ };
+ };
+
+ # Enable user timmy
+ _users.timmy = {
+ enable = true;
+ autologin.enable = true;
+ wifi.enable = true;
+ };
+
+ # Use intel driver for better performance and vsync
+ services.xserver.videoDrivers = [ "intel" ];
+
+ # Configure home
+ home-manager.users.timmy = {
+ gtk._mintTheme = {
+ dark = true;
+ color = "Teal";
+ icons.color = "Teal";
+ };
+ fonts.fontconfig.defaultFonts.monospace = [ "TamzenForPowerline" ];
+ gtk = {
+ font.name = "monospace";
+ font.size = 8;
+ cursorTheme.size = 24;
+ };
+ programs._st = {
+ enable = true;
+ font = {
+ name = "TamzenForPowerline";
+ attrs = {
+ pixelsize = 14;
+ };
+ };
+ };
+ programs._seasonalwallpaper.wallpapers.download = true;
+ fonts.fontconfig = {
+ subpixelRendering = "rgb";
+ hinting = "full";
+ };
+ };
+
+ system.stateVersion = "24.05";
+}
diff --git a/hosts/X230/hardware-configuration.nix b/hosts/X230/hardware-configuration.nix
new file mode 100644
index 0000000..0791585
--- /dev/null
+++ b/hosts/X230/hardware-configuration.nix
@@ -0,0 +1,48 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" "sd_mod" "sdhci_pci" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ "kvm-intel" ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/41036740-73bc-4004-a302-01233b4d83b8";
+ fsType = "btrfs";
+ options = [ "subvol=@" ];
+ };
+
+ boot.initrd.luks.devices."enc".device = "/dev/disk/by-uuid/6019772f-4a1c-4abd-9c70-b1d71cc2de65";
+
+ fileSystems."/home" =
+ { device = "/dev/disk/by-uuid/41036740-73bc-4004-a302-01233b4d83b8";
+ fsType = "btrfs";
+ options = [ "subvol=@home" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/72D4-F66A";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ swapDevices = [ ];
+
+ # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+ # (the default) this is the recommended approach. When using systemd-networkd it's
+ # still possible to use this option, but it's recommended to use it in conjunction
+ # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+ networking.useDHCP = lib.mkDefault true;
+ # networking.interfaces.enp0s25.useDHCP = lib.mkDefault true;
+ # networking.interfaces.wlp3s0.useDHCP = lib.mkDefault true;
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/X230/resources/secrets/wg0.yaml b/hosts/X230/resources/secrets/wg0.yaml
new file mode 100644
index 0000000..5941b70
--- /dev/null
+++ b/hosts/X230/resources/secrets/wg0.yaml
@@ -0,0 +1,16 @@
+wg0: ENC[AES256_GCM,data:wcgowNptTdrJNjzH6n/ulbec5+GPkuRAUNidWFy4dhClioTg8vdrXhhwP+sykwEZYOjsLurkU0Rw1w9ds+AGe3J+FnW1qKdskcY+8t/CyNY51pUbzMCKxexnNj52+0+VlH6FAUyplo6ESg/vlWCFyuyACWjQfdqDW/1PxJzrYqZ7MIwbCdntjE/84F52BqxePt3LolzvzTGUOx5Lr6Jbv9i3tv1R9NmZxt5t2gwaGbIIPWMVZh972w5HJYa8bfx67vuyj6HE46tMiu8WdQbHfjRvVIA+0OtHihpDGHfi5Q6iXpO/rk4YJZjsiEgTMTqD08HD5Gm+wBFwHSJjCOBpBsq3GsspDLNI+EXel7Gmtk+BhL1tFQdpYPwz7bHd03Znawr4Br7R0gHJg4FXnhKlG+SyDqXKirnCyCTfUotIBmU0dX4tzmwiOMyvImgXZA==,iv:GAk27qkZDopzdWnBeL7yTmyn9dM2wSzKd41NRhsyNNY=,tag:Ba7jZhqEa8dUedIvVFBbHA==,type:str]
+sops:
+ age:
+ - recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4NEN4NGxDR1oreGVoSGhE
+ TzMxSEY0QVBhS2Z6MW15ci9aVlJ0a3IyVlZBCldPRVNvcUhJSHhWSEk3akd4RjN0
+ ajhUV2d1ZWRsRFU4cTE2dGl6RmM4MGsKLS0tIFhnUjl5aDJqWVB1NE15SlNzR2Iv
+ YTNydURsOUMrSXZGdk9UOUdUQlA0SFUKxEDJRR6tpYva9qpWo9NxwCxk/xpRVoTl
+ YJkmDZzMcXikXXiro96AprP9dXJXvMPKYPGl2Zsal8PlGFPBoHW2GA==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-03-09T12:36:15Z"
+ mac: ENC[AES256_GCM,data:T8hQbFrPOGYQl8tbqUMLdQN3qjhcAXmKfwqEsLQkDjr2nxUXDz8d58TUsaRvkHC4jPo9lXyPL5SSpua2kzJIcDDLPkiPB/3qV8ksJQ0dgcfLkUnrI1mtoo9AOPnHrTjstSAR2cmiFbCTqRpkLnGwI+NoCLjNjd/GNSAlMl00QMg=,iv:boSHzNAuKGkAhtkApOOJEtW4gt13mMmCin24gf3dXIs=,tag:1+4+BE8ZPgvgf4RlH6Wmhw==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.12.1
diff --git a/hosts/X230/wg.nix b/hosts/X230/wg.nix
new file mode 100644
index 0000000..3881b63
--- /dev/null
+++ b/hosts/X230/wg.nix
@@ -0,0 +1,7 @@
+{ config, pkgs, inputs, ... }: {
+ sops.secrets.wg0.sopsFile = ./resources/secrets/wg0.yaml;
+
+ networking.wg-quick.interfaces = {
+ wg0.configFile = config.sops.secrets.wg0.path;
+ };
+}
diff --git a/hosts/flex-wg-router/configuration.nix b/hosts/flex-wg-router/configuration.nix
new file mode 100644
index 0000000..a0777f0
--- /dev/null
+++ b/hosts/flex-wg-router/configuration.nix
@@ -0,0 +1,83 @@
+{ config, lib, pkgs, ... }: let
+ ipAddress = "10.1.1.1";
+in {
+ imports = [ ./wg.nix ];
+
+ # Setup bootloader
+ boot._loader.enable = true;
+
+ # Enable common options
+ _archetypes = {
+ profiles = {
+ headless = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ btrfs.enable = true;
+ };
+ };
+
+ # Network config
+ networking = {
+ # Label lan and wan interfaces
+ _interfaceLabels = {
+ enable = true;
+ interfaces = {
+ lan0 = "98:b7:85:22:9b:43"; # External
+ wan0 = "54:ee:75:8c:4b:2d"; # Internal
+ };
+ };
+ # Set ip addresses
+ interfaces = {
+ lan0.ipv4.addresses = [{
+ address = ipAddress;
+ prefixLength = 24;
+ }];
+ wan0.ipv4.addresses = [{
+ address = "46.110.173.163"; # Reserved static ip for wg-router
+ prefixLength = 31;
+ }];
+ };
+ defaultGateway = {
+ address = "46.110.173.161";
+ interface = "wan0";
+ };
+ # Firewall rules
+ firewall = {
+ interfaces.wan0 = {
+ allowedUDPPorts = [ 51820 ];
+ };
+ };
+ };
+
+ # Router config
+ services._router = {
+ routing = {
+ enable = true;
+ interfaces = {
+ lan = [ "lan0" ];
+ wan = "wan0";
+ };
+ };
+ dnsDhcpConfig = {
+ localDomain = "wg-router.pls.lan";
+ dhcp = {
+ defaultGateway = ipAddress;
+ localhostIp = ipAddress;
+ rangeStart = "10.1.1.100";
+ rangeEnd = "10.1.1.250";
+ staticLeases = {
+ idrac-8HT2W52 = {
+ macAddress = "18:fb:7b:9d:16:b3";
+ staticIp = "10.1.1.10";
+ };
+ };
+ };
+ };
+ };
+
+ # Enable user timmy
+ _users.timmy.enable = true;
+
+ system.stateVersion = "25.05";
+}
diff --git a/hosts/flex-wg-router/hardware-configuration.nix b/hosts/flex-wg-router/hardware-configuration.nix
new file mode 100644
index 0000000..01cff6d
--- /dev/null
+++ b/hosts/flex-wg-router/hardware-configuration.nix
@@ -0,0 +1,45 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ "kvm-intel" ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/01eae5fd-a46e-4a36-8a9d-247a0b16bcef";
+ fsType = "btrfs";
+ options = [ "subvol=@" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/345A-436A";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ fileSystems."/home" =
+ { device = "/dev/disk/by-uuid/01eae5fd-a46e-4a36-8a9d-247a0b16bcef";
+ fsType = "btrfs";
+ options = [ "subvol=@home" ];
+ };
+
+ swapDevices = [ ];
+
+ # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+ # (the default) this is the recommended approach. When using systemd-networkd it's
+ # still possible to use this option, but it's recommended to use it in conjunction
+ # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+ networking.useDHCP = lib.mkDefault true;
+ # networking.interfaces.enp3s0.useDHCP = lib.mkDefault true;
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/flex-wg-router/resources/secrets/wg.yaml b/hosts/flex-wg-router/resources/secrets/wg.yaml
new file mode 100644
index 0000000..1f6867b
--- /dev/null
+++ b/hosts/flex-wg-router/resources/secrets/wg.yaml
@@ -0,0 +1,17 @@
+wg1: ENC[AES256_GCM,data:r7jNBzEcItmlEtjhKCbyOBaNYfutKxC2UdUYSLHfYyLnwwdIwM1kfvd5K1/UZNAKoG7sHpBha59M1tvZAOIGAFnzG14YsVrMD8w6Qy4pc0FmdyNHDEM4EwaqHFRjbb5oBAFv6lI2VZ3AgXf6StXoVUYtbEA1QBVqVq4Syk6/CalnhkE2LuZpuVA5GZUZ8aTmFRp9zOnhcNoVJMrokTUswV4Mgn3zt2Tb+3bfoZJ9jbb6H8P/F0NGU+phy0EENZMIqOGBP5aPPIZfVQYphQcG6BYiddti3Copq57vqh/qOB70LPle6b/IsaT/K8Xqjp8PjNI/e5gkZdVwIGx/w3Gk0+CkD0tDEUMBdsFfvm7Dbz3xQxN66/0ZMGQgic0xtytr/DfKCIMIwsr33GKavP7OXEJ6lUF615Y4PQhNNx4ePlgcttt2b7TG5bM8nxKsaQ==,iv:mLYNgKXCp8w2JO90Rsn7gtifEn4Yc6JKnjws7uo1w10=,tag:c51B1fZe1HnJhFDc86HnOg==,type:str]
+wg0: ENC[AES256_GCM,data:SJQ21aLwoQ0nEHfoHRd+ksL8pX7HoCRVjGIS/BZxq9JQhHJg9ZHHbwwUkz/3vrq1S+PD7e1bL0FHpgHPuZVHawpaFIeWd6TEPH+6oUxlRbDaEbcWR5POlNyMVV3z9TnOElgmqT0VUqfY80NEqFPbCLdjcWHjnwO4nzrEhPMA9WG2PFCAnZNUtVXh2mnblA61/xmxkSVysahBP+bTHA8a+v/AXy7WrHbnHizTeevdCMqWyDhzHvO8hfH4tU/xJ7GQrG/bxk4JZ6XT8a2CAqmNEKyWicB/zSc5NdILNQL7Kx2mzg/fDp4nltf7iBRZfLuN+r7whrKJ2lJQPATeyjMlIgHUcnohjihiOsGYiBcB3/Y4hIHVt7rRBMoFBB2OgNKC3gx6saZreRxLHZcRZFcVm39G9vaw6EI=,iv:qO8vMlstL/kOxFSlUd/dCtAK9ZzZt+LH/9vfulqHiMc=,tag:yuiwA8Hp8qDrF3UPlCMSUg==,type:str]
+sops:
+ age:
+ - recipient: age1f0tmpy2nam58skmznjyqd3zf54rxtfrk6fda0vlpq9y3yg6wac7sjf0vja
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoNVZ5dmNSd1NRQUhURkl2
+ dnk3MkNjMFUyNnU5L1FFNTV0a1NUbUZ6ZUEwCmNNUldIdnoycVpwUHJrcXZvZXp5
+ NGVHcUlHUm1uK0QxV1JmdDVyQVoxZDgKLS0tIDJhSHhkYjNML045SHNobytucnVZ
+ L25wUWRJbzZMZDFseXdvOFJXQVRxN28KJjC3ola24tTEV8tFYpnsId4d0S+jHkS9
+ ME6i4jorWRlQKdYn/gTUoqgMAvJEc73hjTfgX6bFshhuhflfGxXQQw==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-03-31T19:40:45Z"
+ mac: ENC[AES256_GCM,data:XON+JNOWr6WRYYI0+vCC4qiDST8iY/XQZlkB16l5vMsirS3j5iAIi60hn5viFqMn+IIV7GretbVnBVP32R4C59II8bIySzrsdJj5AuvTvdBvglhkelhiDnchqE98KCG9zr41bJsSaQ/8ubRy7b5jiu9aqzQFg9UQZousecIu/MU=,iv:IJNCc05iu0sZxa3RFh5l1TMcwl3YKRtVXn4wfdOy6M8=,tag:OO5uC8nAjqsWoxC1N801GA==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.12.1
diff --git a/hosts/flex-wg-router/wg.nix b/hosts/flex-wg-router/wg.nix
new file mode 100644
index 0000000..b454b81
--- /dev/null
+++ b/hosts/flex-wg-router/wg.nix
@@ -0,0 +1,9 @@
+{ config, pkgs, inputs, ... }: {
+ sops.secrets.wg0.sopsFile = ./resources/secrets/wg.yaml;
+ sops.secrets.wg1.sopsFile = ./resources/secrets/wg.yaml;
+
+ networking.wg-quick.interfaces = {
+ wg0.configFile = config.sops.secrets.wg0.path;
+ wg1.configFile = config.sops.secrets.wg1.path;
+ };
+}
diff --git a/hosts/gnuslashprinter/configuration.nix b/hosts/gnuslashprinter/configuration.nix
new file mode 100644
index 0000000..9e1f734
--- /dev/null
+++ b/hosts/gnuslashprinter/configuration.nix
@@ -0,0 +1,29 @@
+{
+ imports = [
+ ./flask-relay-ctl.nix
+ ./klipper.nix
+ ];
+
+ boot._loader.enable = true;
+
+ _archetypes = {
+ profiles = {
+ headless = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ btrfs.enable = true;
+ };
+ };
+
+ # Enable user timmy
+ _users.timmy.enable = true;
+
+ # Name devices
+ services.udev.extraRules = ''
+ SUBSYSTEM=="tty", KERNELS=="3-9", SYMLINK+="gsp-power"
+ SUBSYSTEM=="tty", KERNELS=="3-10", SYMLINK+="gsp-control"
+ '';
+
+ system.stateVersion = "25.11";
+}
diff --git a/hosts/gnuslashprinter/flask-relay-ctl.nix b/hosts/gnuslashprinter/flask-relay-ctl.nix
new file mode 100644
index 0000000..27507b6
--- /dev/null
+++ b/hosts/gnuslashprinter/flask-relay-ctl.nix
@@ -0,0 +1,37 @@
+{ pkgs, ... }: let
+ flaskrelayctl = pkgs.python3Packages.buildPythonApplication {
+ pname = "flaskrelayctl";
+ version = "1.0";
+ src = ./resources/flaskrelayctl;
+ pyproject = true;
+ build-system = with pkgs.python3Packages; [ setuptools ];
+ dependencies = with pkgs.python3Packages; [
+ flask
+ pyserial
+ ];
+ };
+in {
+ users.users.relay-api = {
+ isSystemUser = true;
+ group = "relay-api";
+ extraGroups = [ "dialout" ];
+ };
+ users.groups.relay-api = {};
+
+ systemd.services.relay-api = let
+ RELAYCTL_DEV = "/dev/gsp-power";
+ in {
+ description = "USB Relay Flask API";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ serviceConfig = {
+ User = "relay-api";
+ Group = "relay-api";
+ ExecStart = "${flaskrelayctl}/bin/flaskrelayctl";
+ Restart = "always";
+ DeviceAllow = [ "${RELAYCTL_DEV} rw" ];
+ };
+ };
+
+ environment.systemPackages = [ flaskrelayctl ];
+}
diff --git a/hosts/gnuslashprinter/hardware-configuration.nix b/hosts/gnuslashprinter/hardware-configuration.nix
new file mode 100644
index 0000000..278d6e6
--- /dev/null
+++ b/hosts/gnuslashprinter/hardware-configuration.nix
@@ -0,0 +1,44 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/416a481f-d656-4bd1-acdb-bd0a6f2d2c3a";
+ fsType = "btrfs";
+ options = [ "subvol=@" ];
+ };
+
+ fileSystems."/home" =
+ { device = "/dev/disk/by-uuid/416a481f-d656-4bd1-acdb-bd0a6f2d2c3a";
+ fsType = "btrfs";
+ options = [ "subvol=@home" ];
+ };
+
+ fileSystems."/nix" =
+ { device = "/dev/disk/by-uuid/416a481f-d656-4bd1-acdb-bd0a6f2d2c3a";
+ fsType = "btrfs";
+ options = [ "subvol=@nix" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/18C4-6D53";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ swapDevices = [ ];
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/gnuslashprinter/klipper.nix b/hosts/gnuslashprinter/klipper.nix
new file mode 100644
index 0000000..331a0c7
--- /dev/null
+++ b/hosts/gnuslashprinter/klipper.nix
@@ -0,0 +1,100 @@
+{
+ # Klipper firmware
+ services.klipper = {
+ enable = true;
+ firmwares = {
+ mcu = {
+ enable = true;
+ # Serial port connected to the microcontroller
+ serial = "/dev/gsp-control";
+ # Klipper flash must be enabled in order to build mcu firmware
+ # The resulting `klipper-flash-mcu` command will show the location of the firmware bin in the nix store
+ enableKlipperFlash = true;
+ # Run klipper-genconf to generate this
+ configFile = ./resources/klipper/mcu/config;
+ };
+ };
+ configFile = ./resources/klipper/printer.cfg;
+ logFile = "/var/lib/klipper/klipper.log";
+ };
+ # Mutable config
+ services.klipper.mutableConfig = true;
+ #configDir = "/var/lib/moonraker/config"; # Accessible by moonraker # TODO
+
+ # Moonraker web-api
+ security.polkit.enable = true; # required for services.moonraker.allowSystemControl
+ services.moonraker = {
+ user = "root";
+ enable = true;
+ address = "0.0.0.0";
+ allowSystemControl = true;
+ settings = {
+ authorization = {
+ force_logins = true;
+ cors_domains = [
+ "*.local"
+ "*.lan"
+ "*://app.fluidd.xyz"
+ "*://my.mainsail.xyz"
+ ];
+ trusted_clients = [
+ "10.0.0.0/8"
+ "127.0.0.0/8"
+ "169.254.0.0/16"
+ "172.16.0.0/12"
+ "192.168.0.0/16"
+ "FE80::/10"
+ "::1/128"
+ ];
+ };
+ #file_manager.check_klipper_config_path = false; # Disable warning when klipper config is not accessible by moonraker
+ # mainsail.cfg
+ #"update_manager mainsail-config" = {
+ # type = "git_repo";
+ # primary_branch = "master";
+ # path = "~/mainsail-config";
+ # origin = "https://github.com/mainsail-crew/mainsail-config.git";
+ # managed_services = "klipper";
+ #};
+
+ "power printer" = let
+ relayApiHost = "http://localhost:5050";
+ in {
+ type = "http";
+ on_url = "${relayApiHost}/on";
+ off_url = "${relayApiHost}/off";
+ status_url = "${relayApiHost}/status";
+ response_template = ''
+
+ # The module will perform the "GET" request using the appropriate url.
+ # We use the `last_response` method to fetch the result and decode the
+ # json response.
+ {% set resp = http_request.last_response().json() %}
+ # The expression below will render "on" or "off".
+ {resp["state"].lower()}
+ '';
+ off_when_shutdown = true;
+ on_when_job_queued = true;
+ locked_while_printing = true;
+ restart_klipper_when_powered = true;
+ restart_delay = "1";
+ bound_services = "klipper";
+ initial_state = "off";
+ };
+ };
+ };
+
+ # Mainsail web-interface
+ services.mainsail = {
+ enable = true;
+ hostName = "0.0.0.0";
+ nginx.listenAddresses = [ "0.0.0.0" ];
+ };
+ services.nginx.clientMaxBodySize = "1000m"; # Allow large gcodes, etc.
+ networking.firewall.allowedTCPPorts = [ 80 ]; # Port for mainsail via nginx
+
+ # Webcam support in mainsail
+ # TODO hook to restart ustreamer when webcam is connected
+ services.ustreamer.enable = true;
+ services.mainsail.nginx.locations."/webcam/".proxyPass = "http://localhost:8080/stream"; # Default location for ustreamer stream
+}
diff --git a/hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml b/hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml
new file mode 100644
index 0000000..b151b74
--- /dev/null
+++ b/hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml
@@ -0,0 +1,10 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "flaskrelayctl"
+version = "1.0"
+
+[project.scripts]
+flaskrelayctl = "server:main"
diff --git a/hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py b/hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py
new file mode 100644
index 0000000..f2c0bc3
--- /dev/null
+++ b/hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py
@@ -0,0 +1,54 @@
+from flask import Flask, jsonify
+import serial
+import os
+
+app = Flask(__name__)
+
+RELAY_DEV = os.environ.get("RELAYCTL_DEV", "/dev/ttyUSB0")
+RELAY_HOST = os.environ.get("RELAYCTL_HOST", "127.0.0.1")
+RELAY_PORT = os.environ.get("RELAYCTL_PORT", "5050")
+RELAY_BAUD = 9600
+RELAY_ID = 1 # NOTE: multi relay boards are not supported (only relay id 1)
+
+def get_cmd(state: bool):
+ """
+ LCUS relay 4-byte control command syntax is as follows:
+ start: always 0xA0
+ relay: id of relay (counts from 1)
+ state: coil energized 0 or 1
+ checksum: sum of previous 3 bytes
+ """
+ start = 0xA0
+ relay = RELAY_ID
+ state = int(state)
+ checksum = start + relay + state
+ return bytes((start, relay, state, checksum))
+
+def get_serial():
+ return serial.Serial(RELAY_DEV, RELAY_BAUD, timeout=1)
+
+@app.route("/on", methods=["GET", "POST"])
+def on():
+ with get_serial() as ser:
+ ser.write(get_cmd(True))
+ return jsonify({"state": "ON"})
+
+@app.route("/off", methods=["GET", "POST"])
+def off():
+ with get_serial() as ser:
+ ser.write(get_cmd(False))
+ return jsonify({"state": "OFF"})
+
+@app.route("/status", methods=["GET"])
+def status():
+ with get_serial() as ser:
+ ser.write(bytes((0xFF,))) # write 0xFF to get status
+ r = ser.readline()
+ state = r.decode("utf-8").removeprefix("CH1: ").strip()
+ return jsonify({"state": state})
+
+def main():
+ app.run(host=RELAY_HOST, port=int(RELAY_PORT))
+
+if __name__ == "__main__":
+ main()
diff --git a/hosts/gnuslashprinter/resources/klipper/mcu/config b/hosts/gnuslashprinter/resources/klipper/mcu/config
new file mode 100644
index 0000000..3e192a3
--- /dev/null
+++ b/hosts/gnuslashprinter/resources/klipper/mcu/config
@@ -0,0 +1,109 @@
+# CONFIG_LOW_LEVEL_OPTIONS is not set
+# CONFIG_MACH_AVR is not set
+# CONFIG_MACH_ATSAM is not set
+# CONFIG_MACH_ATSAMD is not set
+# CONFIG_MACH_LPC176X is not set
+CONFIG_MACH_STM32=y
+# CONFIG_MACH_HC32F460 is not set
+# CONFIG_MACH_RPXXXX is not set
+# CONFIG_MACH_PRU is not set
+# CONFIG_MACH_AR100 is not set
+# CONFIG_MACH_LINUX is not set
+# CONFIG_MACH_SIMU is not set
+CONFIG_BOARD_DIRECTORY="stm32"
+CONFIG_MCU="stm32f103xe"
+CONFIG_CLOCK_FREQ=72000000
+CONFIG_SERIAL=y
+CONFIG_FLASH_SIZE=0x10000
+CONFIG_FLASH_BOOT_ADDRESS=0x8000000
+CONFIG_RAM_START=0x20000000
+CONFIG_RAM_SIZE=0x5000
+CONFIG_STACK_SIZE=512
+CONFIG_FLASH_APPLICATION_ADDRESS=0x8007000
+CONFIG_STM32_SELECT=y
+CONFIG_MACH_STM32F103=y
+# CONFIG_MACH_STM32F207 is not set
+# CONFIG_MACH_STM32F401 is not set
+# CONFIG_MACH_STM32F405 is not set
+# CONFIG_MACH_STM32F407 is not set
+# CONFIG_MACH_STM32F429 is not set
+# CONFIG_MACH_STM32F446 is not set
+# CONFIG_MACH_STM32F765 is not set
+# CONFIG_MACH_STM32F031 is not set
+# CONFIG_MACH_STM32F042 is not set
+# CONFIG_MACH_STM32F070 is not set
+# CONFIG_MACH_STM32F072 is not set
+# CONFIG_MACH_STM32G070 is not set
+# CONFIG_MACH_STM32G071 is not set
+# CONFIG_MACH_STM32G0B0 is not set
+# CONFIG_MACH_STM32G0B1 is not set
+# CONFIG_MACH_STM32G431 is not set
+# CONFIG_MACH_STM32G474 is not set
+# CONFIG_MACH_STM32H723 is not set
+# CONFIG_MACH_STM32H743 is not set
+# CONFIG_MACH_STM32H750 is not set
+# CONFIG_MACH_STM32L412 is not set
+# CONFIG_MACH_N32G452 is not set
+# CONFIG_MACH_N32G455 is not set
+CONFIG_MACH_STM32F1=y
+CONFIG_HAVE_STM32_USBFS=y
+CONFIG_STM32_USB_DOUBLE_BUFFER_TX=y
+CONFIG_HAVE_STM32_CANBUS=y
+CONFIG_STM32_DFU_ROM_ADDRESS=0
+# CONFIG_STM32_FLASH_START_2000 is not set
+# CONFIG_STM32_FLASH_START_5000 is not set
+CONFIG_STM32_FLASH_START_7000=y
+# CONFIG_STM32_FLASH_START_8000 is not set
+# CONFIG_STM32_FLASH_START_8800 is not set
+# CONFIG_STM32_FLASH_START_9000 is not set
+# CONFIG_STM32_FLASH_START_10000 is not set
+# CONFIG_STM32_FLASH_START_800 is not set
+# CONFIG_STM32_FLASH_START_1000 is not set
+# CONFIG_STM32_FLASH_START_4000 is not set
+# CONFIG_STM32_FLASH_START_0000 is not set
+CONFIG_CLOCK_REF_FREQ=8000000
+CONFIG_STM32F0_TRIM=16
+# CONFIG_STM32_USB_PA11_PA12 is not set
+CONFIG_STM32_SERIAL_USART1=y
+# CONFIG_STM32_CANBUS_PA11_PA12 is not set
+# CONFIG_STM32_CANBUS_PA11_PB9 is not set
+CONFIG_SERIAL_BAUD=250000
+CONFIG_USB_VENDOR_ID=0x1d50
+CONFIG_USB_DEVICE_ID=0x614e
+CONFIG_USB_SERIAL_NUMBER="12345"
+CONFIG_WANT_ADC=y
+CONFIG_WANT_SPI=y
+CONFIG_WANT_SOFTWARE_SPI=y
+CONFIG_WANT_I2C=y
+CONFIG_WANT_SOFTWARE_I2C=y
+CONFIG_WANT_HARD_PWM=y
+CONFIG_WANT_BUTTONS=y
+CONFIG_WANT_TMCUART=y
+CONFIG_WANT_NEOPIXEL=y
+CONFIG_WANT_PULSE_COUNTER=y
+CONFIG_WANT_ST7920=y
+CONFIG_WANT_HD44780=y
+CONFIG_WANT_ADXL345=y
+CONFIG_WANT_LIS2DW=y
+CONFIG_WANT_MPU9250=y
+CONFIG_WANT_ICM20948=y
+CONFIG_WANT_THERMOCOUPLE=y
+CONFIG_WANT_HX71X=y
+CONFIG_WANT_ADS1220=y
+CONFIG_WANT_LDC1612=y
+CONFIG_WANT_SENSOR_ANGLE=y
+CONFIG_NEED_SENSOR_BULK=y
+CONFIG_WANT_LOAD_CELL_PROBE=y
+CONFIG_NEED_SOS_FILTER=y
+CONFIG_CANBUS_FREQUENCY=1000000
+CONFIG_INLINE_STEPPER_HACK=y
+CONFIG_HAVE_STEPPER_OPTIMIZED_BOTH_EDGE=y
+CONFIG_WANT_STEPPER_OPTIMIZED_BOTH_EDGE=y
+CONFIG_HAVE_GPIO=y
+CONFIG_HAVE_GPIO_ADC=y
+CONFIG_HAVE_GPIO_SPI=y
+CONFIG_HAVE_GPIO_I2C=y
+CONFIG_HAVE_GPIO_HARD_PWM=y
+CONFIG_HAVE_STRICT_TIMING=y
+CONFIG_HAVE_CHIPID=y
+CONFIG_HAVE_BOOTLOADER_REQUEST=y
diff --git a/hosts/gnuslashprinter/resources/klipper/printer.cfg b/hosts/gnuslashprinter/resources/klipper/printer.cfg
new file mode 100644
index 0000000..4baba56
--- /dev/null
+++ b/hosts/gnuslashprinter/resources/klipper/printer.cfg
@@ -0,0 +1,180 @@
+# This file contains pin mappings for the stock 2020 Creality Ender 3
+# V2. To use this config, during "make menuconfig" select the
+# STM32F103 with a "28KiB bootloader" and serial (on USART1 PA10/PA9)
+# communication.
+
+# If you prefer a direct serial connection, in "make menuconfig"
+# select "Enable extra low-level configuration options" and select
+# serial (on USART3 PB11/PB10), which is broken out on the 10 pin IDC
+# cable used for the LCD module as follows:
+# 3: Tx, 4: Rx, 9: GND, 10: VCC
+
+# Flash this firmware by copying "out/klipper.bin" to a SD card and
+# turning on the printer with the card inserted. The firmware
+# filename must end in ".bin" and must not match the last filename
+# that was flashed.
+
+# See docs/Config_Reference.md for a description of parameters.
+
+[include mainsail.cfg]
+
+# Default V2 config
+[stepper_x]
+step_pin: PC2
+dir_pin: PB9
+enable_pin: !PC3
+microsteps: 16
+rotation_distance: 40
+endstop_pin: ^PA5
+position_endstop: -20
+position_min: -20
+position_max: 235
+homing_speed: 50
+
+[stepper_y]
+step_pin: PB8
+dir_pin: PB7
+enable_pin: !PC3
+microsteps: 16
+rotation_distance: 40
+endstop_pin: ^PA6
+position_endstop: -8
+position_min: -8
+position_max: 235
+homing_speed: 50
+
+[stepper_z]
+step_pin: PB6
+dir_pin: !PB5
+enable_pin: !PC3
+microsteps: 16
+rotation_distance: 8
+endstop_pin: probe:z_virtual_endstop
+#position_endstop: 0.0
+position_min: -5
+position_max: 250
+
+[extruder]
+max_extrude_only_distance: 100.0
+step_pin: PB4
+dir_pin: PB3
+enable_pin: !PC3
+microsteps: 16
+rotation_distance: 34.406
+nozzle_diameter: 0.400
+filament_diameter: 1.750
+heater_pin: PA1
+sensor_type: EPCOS 100K B57560G104F
+sensor_pin: PC5
+control: pid
+# tuned for stock hardware with 200 degree Celsius target
+pid_Kp: 21.527
+pid_Ki: 1.063
+pid_Kd: 108.982
+min_temp: 0
+max_temp: 250
+
+[heater_bed]
+heater_pin: PA2
+sensor_type: EPCOS 100K B57560G104F
+sensor_pin: PC4
+control: pid
+# tuned for stock hardware with 50 degree Celsius target
+pid_Kp: 54.027
+pid_Ki: 0.770
+pid_Kd: 948.182
+min_temp: 0
+max_temp: 130
+
+[fan]
+pin: PA0
+
+[mcu]
+serial: /dev/gsp-control
+restart_method: command
+
+[printer]
+kinematics: cartesian
+max_velocity: 300
+max_accel: 3000
+max_z_velocity: 5
+max_z_accel: 100
+
+######################################################################
+# 128x64 Full Graphic Creality CR10 / ENDER 3 stockdisplay
+######################################################################
+
+# This section is used for a Creality "12864" display with a single
+# ribbon cable between the display's EXP3 plug and the
+# micro-controller board's EXP1 connector.
+
+[display]
+lcd_type: st7920
+cs_pin: EXP1_7
+sclk_pin: EXP1_6
+sid_pin: EXP1_8
+encoder_pins: ^EXP1_5, ^EXP1_3
+click_pin: ^!EXP1_2
+
+[output_pin beeper]
+pin: EXP1_1
+
+[board_pins]
+aliases:
+ EXP1_1=PC6,EXP1_3=PB10,EXP1_5=PB14,EXP1_7=PB12,EXP1_9=<GND>,
+ EXP1_2=PB2,EXP1_4=PB11,EXP1_6=PB13,EXP1_8=PB15,EXP1_10=<5V>,
+ PROBE_IN=PB1,PROBE_OUT=PB0,FIL_RUNOUT=PA6
+
+[display_status]
+
+######
+# Bed leveling
+
+[bltouch]
+sensor_pin: ^PROBE_IN
+control_pin: PROBE_OUT
+x_offset: -40
+y_offset: -12
+#z_offset: 1.1
+probe_with_touch_mode: true
+stow_on_each_sample: false
+
+[bed_mesh]
+speed: 120
+mesh_min: 10, 10
+mesh_max: 195, 220
+probe_count: 5,5
+algorithm: bicubic
+
+[safe_z_home]
+home_xy_position: 157.5, 129.5
+speed: 75
+z_hop: 10
+z_hop_speed: 5
+move_to_previous: true
+
+
+#*# <---------------------- SAVE_CONFIG ---------------------->
+#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
+#*#
+#*# [bed_mesh default]
+#*# version = 1
+#*# points =
+#*# -0.017500, 0.025000, 0.050000, 0.122500, 0.155000
+#*# -0.045000, -0.040000, 0.037500, 0.102500, 0.145000
+#*# -0.007500, -0.032500, 0.005000, 0.092500, 0.132500
+#*# -0.060000, -0.045000, -0.007500, 0.087500, 0.127500
+#*# 0.075000, 0.042500, 0.015000, 0.105000, 0.175000
+#*# x_count = 5
+#*# y_count = 5
+#*# mesh_x_pps = 2
+#*# mesh_y_pps = 2
+#*# algo = bicubic
+#*# tension = 0.2
+#*# min_x = 10.0
+#*# max_x = 195.0
+#*# min_y = 10.0
+#*# max_y = 220.0
+#*#
+#*# [bltouch]
+#*# z_offset = 1.342
diff --git a/hosts/hp-envy-office/configuration.nix b/hosts/hp-envy-office/configuration.nix
new file mode 100644
index 0000000..5e3a463
--- /dev/null
+++ b/hosts/hp-envy-office/configuration.nix
@@ -0,0 +1,69 @@
+{ config, lib, pkgs, ... }: {
+ imports = [ ./wg.nix ];
+
+ # Setup bootloader
+ boot._loader.enable = true;
+ boot.loader.timeout = 15; # Show for longer since it's usually skipped
+
+ # Enable common options
+ _archetypes = {
+ profiles = {
+ desktop = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ btrfs.enable = true;
+ };
+ collections = {
+ desktop = {
+ extraUtilities.enable = true;
+ chromium.enable = true;
+ graphics.enable = true;
+ office.enable = true;
+ };
+ development = {
+ docker.enable = true;
+ web = {
+ node.enable = true;
+ };
+ };
+ virtualization.enable = true;
+ };
+ };
+
+ # Enable user timmy
+ _users.timmy = {
+ enable = true;
+ nas = {
+ enable = true;
+ office.enable = true;
+ office.automount = true;
+ };
+ };
+
+ # Disable suspend
+ systemd._suspend.disable = true;
+
+ # Use amdgpu driver for x11
+ services.xserver.videoDrivers = [ "amdgpu" ];
+
+ # Configure home
+ home-manager.users.timmy = {
+ gtk._mintTheme = {
+ dark = true;
+ color = "Blue";
+ icons.color = "Blue";
+ };
+ programs._seasonalwallpaper.wallpapers.download = true;
+ fonts.fontconfig = {
+ subpixelRendering = "rgb";
+ hinting = "none";
+ };
+ gtk.gtk3.bookmarks = [
+ "file:///home/timmy/docs/src/sites/admin Admin"
+ "file:///media/chexx/chexx cHEXx"
+ ];
+ };
+
+ system.stateVersion = "24.11";
+}
diff --git a/hosts/hp-envy-office/hardware-configuration.nix b/hosts/hp-envy-office/hardware-configuration.nix
new file mode 100644
index 0000000..22a8c24
--- /dev/null
+++ b/hosts/hp-envy-office/hardware-configuration.nix
@@ -0,0 +1,48 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "ums_realtek" "usb_storage" "usbhid" "sd_mod" "sr_mod" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/5749d84b-690b-43a2-b834-f94675003189";
+ fsType = "btrfs";
+ options = [ "subvol=@" ];
+ };
+
+ boot.initrd.luks.devices."cryptroot".device = "/dev/disk/by-uuid/4a617e54-4800-4474-b1fd-3bca5f66e55a";
+
+ fileSystems."/home" =
+ { device = "/dev/disk/by-uuid/5749d84b-690b-43a2-b834-f94675003189";
+ fsType = "btrfs";
+ options = [ "subvol=@home" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/5E93-7CE3";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ swapDevices = [ ];
+
+ # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+ # (the default) this is the recommended approach. When using systemd-networkd it's
+ # still possible to use this option, but it's recommended to use it in conjunction
+ # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+ networking.useDHCP = lib.mkDefault true;
+ # networking.interfaces.enp3s0.useDHCP = lib.mkDefault true;
+ # networking.interfaces.wlp2s0.useDHCP = lib.mkDefault true;
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/hp-envy-office/resources/secrets/wg.yaml b/hosts/hp-envy-office/resources/secrets/wg.yaml
new file mode 100644
index 0000000..f3b53aa
--- /dev/null
+++ b/hosts/hp-envy-office/resources/secrets/wg.yaml
@@ -0,0 +1,16 @@
+wg1: ENC[AES256_GCM,data:XWdnE2QvfvFlMKUW6BoUSsEXDmYj4aNfbxvA6pFeIZM7NEtIwC4/NsplPwFIZwF372/bwDGXGocuh5gd1p/eAlsyz2DrAS+8g1+4T40EPPmXPgh++vUTvcpPlt74Qxp2yAeEU4CU7UPLvlxSvNjh5PGS68Cw7KxSB7kiWFxRWtm5oVfb+U6cBaQE6Biie7wPmXNWOobGHTfFYDeNmH6w33nH4lCV2MC0eYty9ytwHeVS7gUNrk4oxIfd+1FmNzwNHtVZvRg4wRzcc2M9fD0LuyuY6QVS/qaJG4hNNEHZ6qa0VMTnOzQ4jFHtd5jnz2vb7ckE7UWcFPjXYObcykk0End7sHVN/bD+fUv56JKZOHvVYFgs6OwCzUPAufnv10+h,iv:LMEpZW3mwGuIpJoacBYL8M0ROVNeVMzeb7ncZtfxIDA=,tag:aNCziN9CVgm0IB8VvVorEA==,type:str]
+sops:
+ age:
+ - recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhdUJRS21FeFlseWJXU1dt
+ MnhQRnFvZWM0M1o4bUhBWW9KRDdnZ1pGZkYwCnhPYmFHZmdnRS9lb2xsTXZBcmIx
+ dHF5dmlrbjJyUk84QVBLTEFwMWdESGMKLS0tIHFyZGpSeTFoNEQyZThFc2RyQkhY
+ Q1ZvODVWSXE1STlkZ09tVXdVeU1WaVkKhKMfJclNgHXN7pww2w3AaKwcWiBo676g
+ RWSkV6C+5purA0CzTu1uC3CKz8UK8mVgPfamSZdZQU8+6bGMmseWoQ==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-03-31T19:32:51Z"
+ mac: ENC[AES256_GCM,data:zpEYjHqta6HuRHIKijbLmAND5mCaR14ZUdEeXq/zJ8g4DgWrAkaukhYdXhLH+SEUZt8d3tmj5Eq+6oz9qEjdWhBuPykxVBmBiqIhQBgACCMhSL5v3wY1rxL2ZiQ7szEuwh0GjXpkzPno0Z2+xZ6FzVsJdGnZwykru+JWQcUIfvk=,iv:yUiP/clvI/NnDrji9eMYiTqtO1xsTc7u86V/nlQSMIA=,tag:UyMz/BdYoGxXCJIb8tITcQ==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.12.1
diff --git a/hosts/hp-envy-office/wg.nix b/hosts/hp-envy-office/wg.nix
new file mode 100644
index 0000000..763496e
--- /dev/null
+++ b/hosts/hp-envy-office/wg.nix
@@ -0,0 +1,7 @@
+{ config, pkgs, inputs, ... }: {
+ sops.secrets.wg1.sopsFile = ./resources/secrets/wg.yaml;
+
+ networking.wg-quick.interfaces = {
+ wg1.configFile = config.sops.secrets.wg1.path;
+ };
+}
diff --git a/hosts/libreX60/bios-flashing.nix b/hosts/libreX60/bios-flashing.nix
new file mode 100644
index 0000000..6117813
--- /dev/null
+++ b/hosts/libreX60/bios-flashing.nix
@@ -0,0 +1,8 @@
+# https://libreboot.org/docs/install/#thinkpad-t60x60x60tabletx60s
+
+{ pkgs, ... }: {
+ environment.systemPackages = [ pkgs.flashprog ];
+
+ boot.kernelParams = [ "iomem=relaxed" ];
+}
+
diff --git a/hosts/libreX60/configuration.nix b/hosts/libreX60/configuration.nix
new file mode 100644
index 0000000..bf4f388
--- /dev/null
+++ b/hosts/libreX60/configuration.nix
@@ -0,0 +1,47 @@
+{ config, lib, pkgs, ... }: {
+ imports = [
+ ./powertop-auto-tune.nix
+ # Uncomment this module and reboot to enable bios flashing
+ #./bios-flashing.nix
+ ];
+
+ # Use grub
+ boot._loader = {
+ enable = true;
+ loader = "grub";
+ mode = "bios";
+ grub.biosDevice = "/dev/sda";
+ };
+
+ # NOTE libre kernel removed, check back later
+ # Use libre kernel
+ #boot.kernelPackages = pkgs.linuxPackages-libre;
+
+ # Enable common options
+ _archetypes = {
+ profiles = {
+ desktop = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ btrfs.enable = true;
+ };
+ };
+
+ # Enable user timmy
+ _users.timmy = {
+ enable = true;
+ autologin.enable = true;
+ wifi.enable = true;
+ };
+
+ # i915 Gpu requires intel driver
+ services.xserver.videoDrivers = [ "intel" ];
+
+ # Configure home
+ home-manager.users.timmy = {
+ imports = [ ./home.nix ];
+ };
+
+ system.stateVersion = "24.11";
+}
diff --git a/hosts/libreX60/hardware-configuration.nix b/hosts/libreX60/hardware-configuration.nix
new file mode 100644
index 0000000..b0a7868
--- /dev/null
+++ b/hosts/libreX60/hardware-configuration.nix
@@ -0,0 +1,46 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ata_piix" "ahci" "firewire_ohci" "usb_storage" "sd_mod" "sdhci_pci" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ "kvm-intel" ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/91572803-436d-4f43-b41f-dfba2103752e";
+ fsType = "btrfs";
+ options = [ "subvol=@" ];
+ };
+
+ fileSystems."/home" =
+ { device = "/dev/disk/by-uuid/91572803-436d-4f43-b41f-dfba2103752e";
+ fsType = "btrfs";
+ options = [ "subvol=@home" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/F618-D6C2";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ swapDevices = [ ];
+
+ # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+ # (the default) this is the recommended approach. When using systemd-networkd it's
+ # still possible to use this option, but it's recommended to use it in conjunction
+ # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+ networking.useDHCP = lib.mkDefault true;
+ networking.interfaces.enp1s0.useDHCP = lib.mkDefault true;
+ networking.interfaces.wlp2s0.useDHCP = lib.mkDefault true;
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/libreX60/home-timmy/configuration.nix b/hosts/libreX60/home-timmy/configuration.nix
new file mode 100644
index 0000000..a5f064c
--- /dev/null
+++ b/hosts/libreX60/home-timmy/configuration.nix
@@ -0,0 +1,8 @@
+{
+ imports = [ ../home.nix ];
+
+ targets.genericLinux.enable = true;
+
+ _archetypes.profiles.desktop.enable = true;
+ _users.timmy.home.enable = true;
+}
diff --git a/hosts/libreX60/home.nix b/hosts/libreX60/home.nix
new file mode 100644
index 0000000..b537fc2
--- /dev/null
+++ b/hosts/libreX60/home.nix
@@ -0,0 +1,22 @@
+{
+ gtk._mintTheme = {
+ dark = true;
+ color = "Red";
+ icons.color = "Red";
+ };
+ fonts.fontconfig.defaultFonts.monospace = [ "TamzenForPowerline" ];
+ gtk = {
+ font.name = "monospace";
+ font.size = 8;
+ cursorTheme.size = 24;
+ };
+ programs._st = {
+ enable = true;
+ font = {
+ name = "TamzenForPowerline";
+ attrs = {
+ pixelsize = 14;
+ };
+ };
+ };
+}
diff --git a/hosts/libreX60/powertop-auto-tune.nix b/hosts/libreX60/powertop-auto-tune.nix
new file mode 100644
index 0000000..0eb9578
--- /dev/null
+++ b/hosts/libreX60/powertop-auto-tune.nix
@@ -0,0 +1,17 @@
+# https://en.wikibooks.org/wiki/Libreboot/ThinkPad_X60#Remove_High_Pitched_Whining_Noise
+# TLDR; running `powertop --auto-tune` is supposed to kill the high pitched noises produced by the X60
+
+{ pkgs, ... }: {
+ environment.systemPackages = [ pkgs.powertop ];
+
+ # Create systemd service
+ systemd.services.powertop-autotune = {
+ description = "Powertop Auto-Tune";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ serviceConfig = {
+ Type = "oneshot";
+ ExecStart = "${pkgs.powertop}/bin/powertop --auto-tune";
+ };
+ };
+}
diff --git a/hosts/optiplex/configuration.nix b/hosts/optiplex/configuration.nix
new file mode 100644
index 0000000..5108e72
--- /dev/null
+++ b/hosts/optiplex/configuration.nix
@@ -0,0 +1,109 @@
+{ config, lib, pkgs, home-manager, ... }: {
+ # Setup bootloader
+ boot._loader.enable = true;
+
+ # Enable common options
+ _archetypes = {
+ profiles = {
+ desktop = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ btrfs.enable = true;
+ };
+ collections = {
+ desktop = {
+ extraUtilities.enable = true;
+ cad.enable = true;
+ chromium.enable = true;
+ crypto.enable = true;
+ graphics.enable = true;
+ office.enable = true;
+ video.enable = true;
+ };
+ development = {
+ android.enable = true;
+ c.enable = true;
+ docker.enable = true;
+ lua.enable = true;
+ web = {
+ hugo = {
+ enable = true;
+ openFirewall = true;
+ };
+ node.enable = true;
+ };
+ };
+ virtualization.enable = true;
+ };
+ };
+
+ # Enable user timmy
+ _users.timmy = {
+ enable = true;
+ autologin.enable = true;
+ nas = {
+ enable = true;
+ home.enable = true;
+ home.automount = true;
+ };
+ };
+
+ # Disable suspend
+ systemd._suspend.disable = true;
+
+ # Allow unfree for nvidia + others
+ nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
+ "blackmagic-desktop-video"
+ "decklink"
+ "nvidia-kernel-modules"
+ "nvidia-settings"
+ "nvidia-x11"
+ "spotify"
+ "steam"
+ "steam-original"
+ "steam-run"
+ "steam-unwrapped"
+ ];
+
+ # Install more software
+ environment.systemPackages = with pkgs; [
+ prismlauncher
+ spotify
+ #vintagestory
+ ];
+
+ programs.steam.enable = true;
+
+ # Use nvidia driver
+ services.xserver.videoDrivers = [ "nvidia" ];
+ hardware.nvidia = {
+ modesetting.enable = true; # Required
+ powerManagement.enable = false; # Can cause bugs
+ powerManagement.finegrained = false; # Only works on turing and newer
+ nvidiaSettings = true;
+ open = false; # Not compatible w/ GTX-1050
+ package = config.boot.kernelPackages.nvidiaPackages.legacy_580;
+ forceFullCompositionPipeline = true; # Enables vsync
+ };
+
+ # Enable decklink support for intensity pro
+ services._decklink.enable = true;
+ services._decklink.decklinkSupport.enable = true;
+
+ # Configure home
+ home-manager.users.timmy = {
+ gtk._mintTheme = {
+ dark = true;
+ color = "Green";
+ icons.color = "Green";
+ };
+ programs._seasonalwallpaper.wallpapers.download = true;
+ fonts.fontconfig = {
+ subpixelRendering = "rgb";
+ hinting = "none";
+ };
+ };
+
+ system.stateVersion = "25.11";
+}
diff --git a/hosts/optiplex/g610.nix b/hosts/optiplex/g610.nix
new file mode 100644
index 0000000..8cceafc
--- /dev/null
+++ b/hosts/optiplex/g610.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }: {
+ # Turn off backlight on my G610 keyboard
+ systemd.services.G610DisableBacklight = {
+ description = "G610 disable backlight";
+ wantedBy = [ "multi-user.target" ];
+
+ serviceConfig.ExecStart = "${pkgs.g810-led}/bin/g610-led -a 00";
+ };
+}
diff --git a/hosts/optiplex/hardware-configuration.nix b/hosts/optiplex/hardware-configuration.nix
new file mode 100644
index 0000000..9485a66
--- /dev/null
+++ b/hosts/optiplex/hardware-configuration.nix
@@ -0,0 +1,44 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "firewire_ohci" "usbhid" "usb_storage" "sd_mod" "sr_mod" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ "kvm-intel" ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/e4094dd8-d1fd-4aa1-8f95-82a9144a32be";
+ fsType = "btrfs";
+ options = [ "subvol=root" ];
+ };
+
+ fileSystems."/home" =
+ { device = "/dev/disk/by-uuid/e4094dd8-d1fd-4aa1-8f95-82a9144a32be";
+ fsType = "btrfs";
+ options = [ "subvol=home" ];
+ };
+
+ fileSystems."/nix" =
+ { device = "/dev/disk/by-uuid/e4094dd8-d1fd-4aa1-8f95-82a9144a32be";
+ fsType = "btrfs";
+ options = [ "subvol=nix" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/9350-1411";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ swapDevices = [ ];
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/piframe/configuration.nix b/hosts/piframe/configuration.nix
new file mode 100644
index 0000000..b45a293
--- /dev/null
+++ b/hosts/piframe/configuration.nix
@@ -0,0 +1,77 @@
+{ config, lib, pkgs, ... }: {
+ _archetypes = {
+ # Use headless and pi profiles
+ profiles.headless = {
+ enable = true;
+ home.users.timmy.enable = true;
+ home.users.piframe.enable = true;
+ };
+ profiles.pi = {
+ enable = true;
+ home.users.timmy.enable = true;
+ home.users.piframe.enable = true;
+ };
+ collections = {
+ desktop.xserver.enable = true;
+ };
+ };
+
+ # Install twm as basic window manager to boot into and immediately go fullscreen on immich-frame
+ services.xserver.windowManager.twm.enable = true;
+
+ # Install immich-frame
+ environment.systemPackages = with pkgs; [
+ immich-frame
+ ];
+
+ # Open 8080 for immich-frame
+ networking.firewall.allowedTCPPorts = [ 8080 ];
+
+ # Enable user timmy for ssh maintenance and wifi secrets
+ _users.timmy = {
+ enable = true;
+ wifi.enable = true;
+ };
+
+ # Enable piframe user
+ users.users.piframe = {
+ isNormalUser = true;
+ };
+
+ # Configure automatic login with getty
+ services.getty.autologinUser = "piframe";
+
+ # Configure piframe home
+ home-manager.users.piframe = {
+ services = {
+ _gammastep.enable = true;
+ # Manual location to avoid having to mess with geoclue
+ gammastep = {
+ provider = "manual";
+ latitude = 41.881832;
+ longitude = -87.623177;
+ };
+ };
+ programs.bash = {
+ enable = true;
+ profileExtra = ''
+ # startx on tty1 immediately
+ [ -z $DISPLAY ] && [ `tty` = /dev/tty1 ] && startx
+ '';
+ };
+ home.file.".xinitrc".text = ''
+ #!/bin/sh
+ gammastep &
+ unclutter --start-hidden &
+ immich-frame --fullscreen &
+ exec twm
+ '';
+ home.file.".twmrc".text = ''
+ *NoTitle # Hide title bar on all windows
+ *NoBorder # Hide borders on all windows
+ '';
+ home.stateVersion = "25.11";
+ };
+
+ system.stateVersion = "25.11";
+}
diff --git a/hosts/piframe/hardware-configuration.nix b/hosts/piframe/hardware-configuration.nix
new file mode 100644
index 0000000..ceb755a
--- /dev/null
+++ b/hosts/piframe/hardware-configuration.nix
@@ -0,0 +1,24 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "xhci_pci" "usbhid" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888";
+ fsType = "ext4";
+ };
+
+ swapDevices = [ ];
+
+ nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
+}
diff --git a/hosts/poweredge/bitcoind.nix b/hosts/poweredge/bitcoind.nix
new file mode 100644
index 0000000..e282234
--- /dev/null
+++ b/hosts/poweredge/bitcoind.nix
@@ -0,0 +1,41 @@
+{
+ containers.bitcoind = {
+ autoStart = true;
+ ephemeral = true;
+ privateNetwork = true;
+ hostBridge = "br-lan0";
+ localMacAddress = "02:00:00:00:00:04";
+
+ # Host path
+ bindMounts = {
+ "/var/lib/bitcoind-main" = {
+ hostPath = "/media/ingens/bitcoin";
+ isReadOnly = false;
+ };
+ };
+
+ config = { lib, pkgs, config, ... }: let
+ p2pPort = 8333;
+ rpcPort = 8332;
+ in {
+ # Network
+ networking.enableIPv6 = false;
+ networking.interfaces.eth0.useDHCP = true;
+ networking.firewall.allowedTCPPorts = [ p2pPort rpcPort ];
+
+ # Bitcoin
+ services.bitcoind."main" = {
+ enable = true;
+ port = p2pPort;
+ rpc.port = rpcPort;
+ rpc.users.timmy.passwordHMAC = "7557196eec6e3e062a57b0720abff7cf$617211e94b964f548cfbb5c23590f690c2940e424fffe72485276493f1cb0b29";
+ extraConfig = ''
+ rpcallowip=192.168.1.0/24
+ rpcbind=0.0.0.0
+ '';
+ };
+
+ system.stateVersion = "25.11";
+ };
+ };
+}
diff --git a/hosts/poweredge/configuration.nix b/hosts/poweredge/configuration.nix
new file mode 100644
index 0000000..c15f222
--- /dev/null
+++ b/hosts/poweredge/configuration.nix
@@ -0,0 +1,45 @@
+{ config, lib, pkgs, ... }: {
+ imports = [
+ ./bitcoind.nix
+ ./ddns-updater.nix
+ ./filebrowser.nix
+ ./fileshares.nix
+ ./gitea.nix
+ ./immich.nix
+ ./jellyfin.nix
+ ./lgtm.nix
+ ./networking.nix
+ ./nvidia.nix
+ ./transmission.nix
+ #./notification-mailer.nix # TODO move some of this stuff to archetype
+ ];
+
+ # Allow unfree packages for nvidia/cuda
+ nixpkgs.config.allowUnfree = true;
+
+ # Setup bootloader
+ boot._loader.enable = true;
+
+ # Enable common options
+ _archetypes = {
+ profiles = {
+ headless = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ zfs.enable = true;
+ cuda.enable = true;
+ };
+ };
+
+ # Import zfs pools
+ boot.zfs.extraPools = [ "ingens" "memoria" ];
+
+ # Enable user timmy
+ _users.timmy.enable = true;
+
+ # Without this, "ZFS requires networking.hostId to be set" will be raised
+ networking.hostId = "4d9e002f";
+
+ system.stateVersion = "25.11";
+}
diff --git a/hosts/poweredge/ddns-updater.nix b/hosts/poweredge/ddns-updater.nix
new file mode 100644
index 0000000..319e3c4
--- /dev/null
+++ b/hosts/poweredge/ddns-updater.nix
@@ -0,0 +1,22 @@
+{ config, lib, ... }: let
+ credential = "config";
+in {
+ # Config for ddns-updater, owned by the ddns-updater systemd service user
+ sops.secrets.ddns-updater-config.sopsFile = ./resources/secrets/ddns-updater-config.yaml;
+
+ # Load secret as a credential in systemd service
+ systemd.services.ddns-updater.serviceConfig = {
+ LoadCredential = [
+ "${credential}:${config.sops.secrets.ddns-updater-config.path}"
+ ];
+ };
+
+ # Enable ddns updater
+ services.ddns-updater = {
+ enable = true;
+ environment = {
+ SERVER_ENABLED="no";
+ CONFIG_FILEPATH = "%d/${credential}";
+ };
+ };
+}
diff --git a/hosts/poweredge/filebrowser.nix b/hosts/poweredge/filebrowser.nix
new file mode 100644
index 0000000..818b3df
--- /dev/null
+++ b/hosts/poweredge/filebrowser.nix
@@ -0,0 +1,48 @@
+{
+ containers.filebrowser = {
+ autoStart = true;
+ privateNetwork = true;
+ hostBridge = "br-lan0";
+ localMacAddress = "02:00:00:00:00:05";
+
+ # Host path
+ bindMounts = {
+ "/var/lib/filebrowser/data" = {
+ hostPath = "/media";
+ isReadOnly = true;
+ };
+ };
+
+ config = { lib, pkg, config, ... }: let
+ publicPort = 9000;
+ in {
+ # Network
+ networking.interfaces.eth0.useDHCP = true;
+ networking.firewall.allowedTCPPorts = [ 80 publicPort ]; # Caddy (private + public)
+
+ # Filebrowser
+ services.filebrowser.enable = true;
+
+ # Reverse proxy
+ services.caddy = {
+ enable = true;
+ # Private
+ virtualHosts.":80".extraConfig = ''
+ reverse_proxy localhost:8080
+ '';
+ # Public (not sure why toString is needed but ok)
+ virtualHosts.":${toString publicPort}".extraConfig = ''
+ @shared {
+ path /share/*
+ path /static/*
+ path /api/public/*
+ }
+
+ reverse_proxy @shared localhost:8080
+ '';
+ };
+
+ system.stateVersion = "26.05";
+ };
+ };
+}
diff --git a/hosts/poweredge/fileshares.nix b/hosts/poweredge/fileshares.nix
new file mode 100644
index 0000000..c5fca71
--- /dev/null
+++ b/hosts/poweredge/fileshares.nix
@@ -0,0 +1,47 @@
+{
+ # Configure services
+ services._fileShares = {
+ enable = true;
+ smb.openFirewall = true;
+ nfs.openFirewall = true;
+ };
+ # Shares
+ services._fileShares.shares = {
+ PS2 = {
+ path = "/media/ingens/games/ps2";
+ smb = {
+ enable = true;
+ allowUser = "ps2";
+ extraOptions = {
+ "min protocol" = "NT1";
+ "max protocol" = "NT1";
+ };
+ };
+ };
+ WinBackups = {
+ path = "/media/ingens/backups/windows";
+ smb.enable = true;
+ };
+ pictures = {
+ path = "/media/ingens/pictures";
+ nfs.enable = true;
+ };
+ tapes = {
+ path = "/media/ingens/tapes";
+ nfs.enable = true;
+ smb.enable = true;
+ };
+ backups = {
+ path = "/media/ingens/backups";
+ nfs.enable = true;
+ };
+ };
+
+ # ps2 user
+ users.users.ps2 = {
+ isSystemUser = true;
+ password = "ps2";
+ group = "ps2";
+ };
+ users.groups.ps2 = {};
+}
diff --git a/hosts/poweredge/gitea.nix b/hosts/poweredge/gitea.nix
new file mode 100644
index 0000000..4c800df
--- /dev/null
+++ b/hosts/poweredge/gitea.nix
@@ -0,0 +1,41 @@
+{
+ containers.gitea = {
+ autoStart = true;
+ privateNetwork = true;
+ hostBridge = "br-lan0";
+ localMacAddress = "02:00:00:00:00:03";
+
+ # TODO make a mountpoint for git repositories
+
+ config = { lib, pkgs, config, ... }: {
+ # Network
+ networking.interfaces.eth0.useDHCP = true;
+ networking.firewall.allowedTCPPorts = [ 80 22 ]; # Caddy + ssh
+
+ # Gitea
+ services.gitea = {
+ enable = true;
+ #user = "git"; # So ssh cloning uses git@gitea
+ settings = {
+ repository = {
+ ENABLE_PUSH_CREATE_USER = true;
+ ENABLE_PUSH_CREATE_ORG = true;
+ };
+ };
+ };
+
+ # SSH for repositories
+ services.openssh.enable = true;
+
+ # Reverse proxy
+ services.caddy = {
+ enable = true;
+ virtualHosts.":80".extraConfig = ''
+ reverse_proxy localhost:3000
+ '';
+ };
+
+ system.stateVersion = "25.11";
+ };
+ };
+}
diff --git a/hosts/poweredge/hardware-configuration.nix b/hosts/poweredge/hardware-configuration.nix
new file mode 100644
index 0000000..0fcc098
--- /dev/null
+++ b/hosts/poweredge/hardware-configuration.nix
@@ -0,0 +1,50 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "megaraid_sas" "usbhid" "usb_storage" "sd_mod" "sr_mod" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ "kvm-intel" ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "zpool/root";
+ fsType = "zfs";
+ options = [ "zfsutil" ];
+ };
+
+ fileSystems."/nix" =
+ { device = "zpool/nix";
+ fsType = "zfs";
+ options = [ "zfsutil" ];
+ };
+
+ fileSystems."/var" =
+ { device = "zpool/var";
+ fsType = "zfs";
+ options = [ "zfsutil" ];
+ };
+
+ fileSystems."/home" =
+ { device = "zpool/home";
+ fsType = "zfs";
+ options = [ "zfsutil" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/D083-98C0";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ swapDevices = [ ];
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/poweredge/immich.nix b/hosts/poweredge/immich.nix
new file mode 100644
index 0000000..444a0f2
--- /dev/null
+++ b/hosts/poweredge/immich.nix
@@ -0,0 +1,78 @@
+{ pkgs, ... }: let
+ onnxruntimeCuda = pkgs.onnxruntime.override { cudaSupport = true; };
+in {
+ containers.immich = {
+ autoStart = true;
+ privateNetwork = true;
+ hostBridge = "br-lan0";
+ localMacAddress = "02:00:00:00:00:01";
+
+ # Host path
+ bindMounts = {
+ "/var/lib/immich" = {
+ hostPath = "/media/ingens/immich";
+ isReadOnly = false;
+ };
+ };
+
+ # GPU
+ allowedDevices = [
+ { node = "/dev/nvidia0"; modifier = "rw"; }
+ { node = "/dev/nvidiactl"; modifier = "rw"; }
+ { node = "/dev/nvidia-uvm"; modifier = "rw"; }
+ { node = "/dev/nvidia-uvm-tools"; modifier = "rw"; }
+ { node = "/dev/nvidia-modeset"; modifier = "rw"; }
+ ];
+
+ bindMounts = {
+ # NVENC/NVDEC - video transcoding
+ "/dev/nvidia0" = { hostPath = "/dev/nvidia0"; isReadOnly = false; };
+ "/dev/nvidiactl" = { hostPath = "/dev/nvidiactl"; isReadOnly = false; };
+ # CUDA - required for ML inference
+ "/dev/nvidia-uvm" = { hostPath = "/dev/nvidia-uvm"; isReadOnly = false; };
+ "/dev/nvidia-uvm-tools" = { hostPath = "/dev/nvidia-uvm-tools"; isReadOnly = false; };
+ # Modeset - needed by some driver paths
+ "/dev/nvidia-modeset" = { hostPath = "/dev/nvidia-modeset"; isReadOnly = false; };
+ };
+
+ config = { lib, pkgs, config, ... }: {
+ imports = [ ./nvidia.nix ];
+
+ # Network
+ networking.interfaces.eth0.useDHCP = true;
+ networking.firewall.allowedTCPPorts = [ 80 ]; # Caddy
+
+ # Immich
+ services.immich = {
+ enable = true;
+ accelerationDevices = [
+ "/dev/nvidia0"
+ "/dev/nvidiactl"
+ "/dev/nvidia-uvm"
+ "/dev/nvidia-uvm-tools"
+ "/dev/nvidia-modeset"
+ ];
+
+ # Tell immich-server to use NVENC for transcoding
+ environment = {
+ NVIDIA_VISIBLE_DEVICES = "all";
+ NVIDIA_DRIVER_CAPABILITIES = "compute,video,utility";
+ };
+
+ # Enable the ML microservice with CUDA
+ machine-learning.enable = true;
+ };
+ environment.systemPackages = with pkgs; [ immich immich-cli ];
+
+ # Reverse proxy
+ services.caddy = {
+ enable = true;
+ virtualHosts.":80".extraConfig = ''
+ reverse_proxy localhost:2283
+ '';
+ };
+
+ system.stateVersion = "25.11";
+ };
+ };
+}
diff --git a/hosts/poweredge/jellyfin.nix b/hosts/poweredge/jellyfin.nix
new file mode 100644
index 0000000..761c3ae
--- /dev/null
+++ b/hosts/poweredge/jellyfin.nix
@@ -0,0 +1,91 @@
+{
+ containers.jellyfin = {
+ autoStart = true;
+ privateNetwork = true;
+ hostBridge = "br-lan0";
+ localMacAddress = "02:00:00:00:00:02";
+ bindMounts = {
+ "/media" = {
+ hostPath = "/media/ingens/media";
+ isReadOnly = true;
+ };
+ };
+
+ # GPU
+ allowedDevices = [
+ { node = "/dev/nvidia0"; modifier = "rw"; }
+ { node = "/dev/nvidiactl"; modifier = "rw"; }
+ { node = "/dev/nvidia-uvm"; modifier = "rw"; }
+ { node = "/dev/nvidia-uvm-tools"; modifier = "rw"; }
+ { node = "/dev/nvidia-modeset"; modifier = "rw"; }
+ ];
+
+ bindMounts = {
+ # NVENC/NVDEC - video transcoding
+ "/dev/nvidia0" = { hostPath = "/dev/nvidia0"; isReadOnly = false; };
+ "/dev/nvidiactl" = { hostPath = "/dev/nvidiactl"; isReadOnly = false; };
+ # CUDA - required for ML inference
+ "/dev/nvidia-uvm" = { hostPath = "/dev/nvidia-uvm"; isReadOnly = false; };
+ "/dev/nvidia-uvm-tools" = { hostPath = "/dev/nvidia-uvm-tools"; isReadOnly = false; };
+ # Modeset - needed by some driver paths
+ "/dev/nvidia-modeset" = { hostPath = "/dev/nvidia-modeset"; isReadOnly = false; };
+ };
+
+ config = { lib, config, pkgs, ... }: {
+ imports = [ ./nvidia.nix ];
+
+ # Network
+ networking.interfaces.eth0.useDHCP = true;
+ networking.firewall.allowedTCPPorts = [ 80 ]; # Caddy
+
+ # Jellyfin
+ services.jellyfin = {
+ enable = true;
+ openFirewall = true;
+
+ hardwareAcceleration = {
+ enable = true;
+ type = "nvenc";
+ device = "/dev/nvidia0"; # Required by the module's assertion, unused in encoding.xml for nvenc
+ };
+
+ forceEncodingConfig = true; # Override encoding.xml
+
+ transcoding = {
+ enableHardwareEncoding = true;
+
+ hardwareEncodingCodecs = {
+ hevc = true; # Pascal 4th-gen NVENC supports HEVC 8/10-bit encode
+ av1 = false; # NOT supported on Pascal — do not enable
+ };
+
+ hardwareDecodingCodecs = {
+ h264 = true;
+ hevc = true;
+ hevc10bit = true;
+ vp8 = true;
+ vp9 = true;
+ mpeg2 = true;
+ vc1 = true;
+ av1 = false; # NOT supported on Pascal NVDEC
+ hevcRExt10bit = false; # Range Extensions decode is a newer-gen feature, skip on Pascal
+ hevcRExt12bit = false;
+ };
+
+ encodingPreset = "medium";
+ enableToneMapping = true; # works via CUDA-based tonemap_cuda filter on Pascal+
+ };
+ };
+
+ # Reverse proxy
+ services.caddy = {
+ enable = true;
+ virtualHosts.":80".extraConfig = ''
+ reverse_proxy localhost:8096
+ '';
+ };
+
+ system.stateVersion = "26.05";
+ };
+ };
+}
diff --git a/hosts/poweredge/lgtm.nix b/hosts/poweredge/lgtm.nix
new file mode 100644
index 0000000..6f43c02
--- /dev/null
+++ b/hosts/poweredge/lgtm.nix
@@ -0,0 +1,201 @@
+{ pkgs, config, ... }: {
+ # Log shipping via Alloy
+ services.alloy.enable = true;
+
+ environment.etc."alloy/config.alloy".text = ''
+ loki.source.journal "journal" {
+ forward_to = [loki.write.default.receiver]
+ relabel_rules = loki.relabel.journal.rules
+ max_age = "12h"
+ }
+
+ loki.relabel "journal" {
+ forward_to = []
+ rule {
+ source_labels = ["__journal__systemd_unit"]
+ target_label = "unit"
+ }
+ }
+
+ loki.write "default" {
+ endpoint {
+ url = "http://lgtm:3100/loki/api/v1/push"
+ }
+ }
+ '';
+
+ # Export prometheus data from host for container to access
+ services.prometheus.exporters.smartctl = {
+ enable = true;
+ port = 9633;
+ listenAddress = "0.0.0.0";
+ };
+
+ services.prometheus.exporters.node = {
+ enable = true;
+ port = 9100;
+ listenAddress = "0.0.0.0";
+ enabledCollectors = [ "systemd" "diskstats" "filesystem" "meminfo" "cpu" ];
+ # zfs + hwmon already on by default, no need to list them
+ };
+
+ # # Host config — zpool_influxdb needs real pool access
+ # services.telegraf = {
+ # enable = true;
+ # extraConfig = {
+ # agent.interval = "30s";
+ #
+ # inputs.execd = [{
+ # command = [ "${pkgs.zpool_influxdb}/bin/zpool_influxdb" "--execd" ];
+ # signal = "none";
+ # data_format = "influx";
+ # }];
+ #
+ # outputs.prometheus_client = [{
+ # listen = ":9273";
+ # metric_version = 2;
+ # }];
+ # };
+ # };
+
+ # Allow ports for prometheus
+ networking.firewall.allowedTCPPorts = [ 9100 9273 9633 ]; # at least reachable from container subnet
+
+ # Secret key for grafana
+ sops.secrets.grafana-secret-key = { sopsFile = ./resources/secrets/grafana.yaml; key = "secret_key"; }; # Office
+
+ # Container
+ containers.grafana = {
+ autoStart = true;
+ privateNetwork = true;
+ hostBridge = "br-lan0";
+ localMacAddress = "02:00:00:00:00:06";
+
+ # Bind wg0-router secret to container
+ bindMounts."/run/secrets/grafana-secret_key" = {
+ hostPath = config.sops.secrets.grafana-secret-key.path;
+ isReadOnly = true;
+ };
+
+ config = { lib, pkgs, config, ... }: {
+ # Network
+ networking.enableIPv6 = false;
+ networking.interfaces.eth0.useDHCP = true;
+ networking.firewall.allowedTCPPorts = [ 80 3100 9090 ]; # Caddy (grafana) + loki + prometheus
+
+ # Loki
+ services.loki = {
+ enable = true;
+ configuration = {
+ server = {
+ http_listen_port = 3100;
+ http_listen_address = "0.0.0.0"; # explicit, since the host's Alloy needs to reach it to push logs
+ };
+ auth_enabled = false;
+
+ common = {
+ path_prefix = "/var/lib/loki";
+ storage.filesystem = {
+ chunks_directory = "/var/lib/loki/chunks";
+ rules_directory = "/var/lib/loki/rules";
+ };
+ ring = {
+ instance_addr = "127.0.0.1";
+ kvstore.store = "inmemory";
+ };
+ replication_factor = 1;
+ };
+
+ schema_config.configs = [{
+ from = "2024-01-01";
+ store = "tsdb";
+ object_store = "filesystem";
+ schema = "v13";
+ index = { prefix = "index_"; period = "24h"; };
+ }];
+ };
+ };
+
+ # Prometheus
+ services.prometheus = {
+ enable = true;
+ scrapeConfigs = [
+ {
+ job_name = "smartctl";
+ static_configs = [{ targets = [ "poweredge:9633" ]; }];
+ }
+ {
+ job_name = "node";
+ static_configs = [{ targets = [ "poweredge:9100" ]; }];
+ }
+ {
+ job_name = "zpool";
+ static_configs = [{ targets = [ "poweredge:9273" ]; }];
+ }
+ ];
+ };
+
+ # Grafana
+ # https://wiki.nixos.org/wiki/Grafana
+ services.grafana = {
+ enable = true;
+ settings.server = { http_addr = "0.0.0.0"; http_port = 3000; };
+ provision = {
+ enable = true;
+
+ # Creates a *mutable* dashboard provider, pulling from /etc/grafana-dashboards.
+ # With this, you can manually provision dashboards from JSON with `environment.etc` like below.
+ dashboards.settings.providers = [{
+ name = "my dashboards";
+ disableDeletion = true;
+ options = {
+ path = "/etc/grafana-dashboards";
+ foldersFromFilesStructure = true;
+ };
+ }];
+
+ datasources.settings.datasources = [
+ { name = "Prometheus"; type = "prometheus"; url = "http://127.0.0.1:9090"; isDefault = true; }
+ { name = "Loki"; type = "loki"; url = "http://127.0.0.1:3100"; }
+ ];
+ };
+ };
+
+ # Grafana secret key
+ systemd.services.grafana.serviceConfig = {
+ LoadCredential = [
+ "secret_key:/run/secrets/grafana-secret_key"
+ ];
+ };
+ services.grafana.settings.security.secret_key = "$__file{/run/credentials/grafana.service/secret_key}";
+
+ # Grafana dashboards
+ environment.etc = let
+ mkDashDl = id: rev: "https://grafana.com/api/dashboards/${toString id}/revisions/${toString rev}/download";
+ in {
+ "grafana-dashboards/node-exporter-full.json".source = pkgs.fetchurl {
+ url = mkDashDl 1860 45;
+ sha256 = "sha256-GExrdAnzBtp1Ul13cvcZRbEM6iOtFrXXjEaY6g6lGYY=";
+ };
+ "grafana-dashboards/smartctl-exporter.json".source = pkgs.fetchurl {
+ url = mkDashDl 22604 2;
+ sha256 = "sha256-ci8WE23fZ+ltEKFoUdNNVXsUIV0jqtas79ia2lYIo88=";
+ };
+ "grafana-dashboards/zfs-pool-metrics.json".source = pkgs.fetchurl {
+ url = mkDashDl 15362 1;
+ sha256 = "sha256-latX1gwRjdpIalT5kpm9MfDaaXFLZCJ0ZP8CZJo8guI=";
+ };
+ };
+
+ # Reverse proxy
+ services.caddy = {
+ enable = true;
+ virtualHosts.":80".extraConfig = ''
+ reverse_proxy localhost:3000
+ '';
+ };
+
+ system.stateVersion = "26.05";
+ };
+ };
+}
diff --git a/hosts/poweredge/networking.nix b/hosts/poweredge/networking.nix
new file mode 100644
index 0000000..bf0aa1c
--- /dev/null
+++ b/hosts/poweredge/networking.nix
@@ -0,0 +1,157 @@
+let
+ hostIp = "192.168.1.10";
+in { config, ... }: {
+ networking = {
+ # Label lan and wan interfaces
+ _interfaceLabels = {
+ enable = true;
+ interfaces.lan0 = "50:9a:4c:5d:c3:7a";
+ interfaces.wan0 = "50:9a:4c:5d:c3:7b";
+ };
+ # Create bridged lan interface for all containers
+ bridges.br-lan0.interfaces = [ "lan0" ];
+ # Disable dhcp on router interfaces
+ interfaces = {
+ veth-router-lan.useDHCP = false;
+ vb-router-lan0.useDHCP = false;
+ };
+ # Configure network
+ defaultGateway = "10.255.255.1"; # Read explaination for veth-router-lan below
+ nameservers = [ "192.168.1.1" ]; # DNS will only be available from this ip address THROUGH the default gateway
+ # br-lan0 will be the interface used for networking on poweredge host
+ interfaces.br-lan0.ipv4.addresses = [{
+ address = hostIp;
+ prefixLength = 24;
+ }];
+ };
+
+ # Secrets
+ sops.secrets.router-wg0 = { sopsFile = ./resources/secrets/router.yaml; key = "wg0"; }; # Office
+ sops.secrets.router-wg1 = { sopsFile = ./resources/secrets/router.yaml; key = "wg1"; }; # Remote access
+
+ # Router container
+ containers.router = {
+ autoStart = true;
+ ephemeral = true;
+ privateNetwork = true;
+ # Pass wan0 directly into container since it isn't needed elsewhere
+ interfaces = [ "wan0" ];
+ # Setup router lan0
+ # NOTE: Host/container communication is not possible through a hostBridge interface
+ extraVeths.vb-router-lan0.hostBridge = "br-lan0";
+ # Setup virtual host-router bridge interface.
+ # This is the default gateway for host/container communication since
+ # communication isn't possible through hostBridge interfaces.
+ # This is essentially equivalent to connecting the host to the
+ # container with a virtual ethernet cable on a separate interface.
+ extraVeths.veth-router-lan = {
+ hostAddress = "10.255.255.2";
+ localAddress = "10.255.255.1";
+ };
+ # Bind secrets to container
+ bindMounts."/run/secrets/wg0" = {
+ hostPath = config.sops.secrets.router-wg0.path;
+ isReadOnly = true;
+ };
+ bindMounts."/run/secrets/wg1" = {
+ hostPath = config.sops.secrets.router-wg1.path;
+ isReadOnly = true;
+ };
+
+ config = { lib, config, ... }: {
+ imports = [
+ ../../nixos/services/router
+ ./router-hosts.nix # Contains dhcp config + static leases + overrides
+ ];
+
+ networking = {
+ # Set ip addresses
+ enableIPv6 = false;
+ interfaces = {
+ vb-router-lan0.ipv4.addresses = [{
+ address = "192.168.1.1";
+ prefixLength = 24;
+ }];
+ wan0.useDHCP = true;
+ };
+ # Setup wireguard
+ wg-quick.interfaces = {
+ wg0.configFile = "/run/secrets/wg0";
+ wg1.configFile = "/run/secrets/wg1";
+ };
+ # NAT (port-forwarding) rules
+ nat.forwardPorts =[
+ { # Bitcoin p2p
+ sourcePort = 8333;
+ proto = "tcp";
+ destination = "192.168.1.44:8333";
+ }
+ { # Filebrowser
+ sourcePort = 19045;
+ proto = "tcp";
+ destination = "192.168.1.45:9000";
+ }
+ ];
+ firewall.allowedUDPPorts = [ 51820 ]; # Allow wg1 running on router host through w/o NAT
+ # Additional advanced rules
+ # TODO add multi NAT feature to router service (this is just a normal nat rule)
+ nftables = {
+ enable = true;
+ tables = {
+ # NAT/masquerade wg0 allowing vb-router-lan0 clients to access wg0
+ wg-nat = {
+ family = "ip";
+ content = ''
+ chain post {
+ type nat hook postrouting priority srcnat; policy accept;
+ iifname "vb-router-lan0" oifname "wg0" masquerade comment "vb-router-lan0 => wg0"
+ iifname "veth-router-lan" oifname "wg0" masquerade comment "veth-router-lan => wg0"
+ }
+ '';
+ };
+ };
+ };
+ };
+
+ # Setup router
+ services._router = {
+ dnsDhcpConfig.enable = true;
+ routing = {
+ enable = true;
+ interfaces = {
+ lan = [ "vb-router-lan0" "veth-router-lan" ];
+ wan = "wan0";
+ };
+ };
+ };
+
+ services.unbound._blocklists = {
+ enable = true;
+ hageziBlocklists = [ "pro" "nsfw" ];
+ };
+
+ system.stateVersion = "25.11";
+ };
+ };
+
+ # FIXME the following snippet will cause the router container to fail to start:
+ # networking.defaultGateway = {
+ # address = "10.255.255.1";
+ # interface = "veth-router-lan";
+ # };
+ # Journalctl will report:
+ # poweredge container router: Bring veth-router-lan up
+ # poweredge container router: RTNETLINK answers: File exists
+ # poweredge systemd: container@router.service: Control process exited, code=exited, status=2/INVALIDARGUMENT
+ # So the issue nixos is creating an interface with that same name.
+ # As a temporary workaround, the following service is deployed (after the container starts):
+ systemd.services.router-default-route = {
+ after = [ "container@router.service" ];
+ wants = [ "container@router.service" ];
+ serviceConfig.Type = "oneshot";
+ script = ''
+ /run/current-system/sw/bin/ip route replace default via 10.255.255.1 dev veth-router-lan
+ '';
+ wantedBy = [ "multi-user.target" ];
+ };
+}
diff --git a/hosts/poweredge/notification-mailer.nix b/hosts/poweredge/notification-mailer.nix
new file mode 100644
index 0000000..d8fddc7
--- /dev/null
+++ b/hosts/poweredge/notification-mailer.nix
@@ -0,0 +1,27 @@
+{ config, ... }: let
+ serverEmail = "server-notifications@tjkeller.xyz";
+in {
+ # Mailer password secret for mail application password
+ sops.secrets.mailerPassword.sopsFile = ./resources/secrets/mailer-pass.yaml;
+
+ # Enable mta for system event notifications
+ services.mail._mailer = {
+ sender = {
+ host = "mail.tjkeller.xyz";
+ user = serverEmail;
+ from = serverEmail;
+ passwordFile = config.sops.secrets.mailerPassword.path;
+ };
+ recipient = serverEmail;
+ };
+
+ # Enable zed mailer module
+ services.zfs._zedMailer.enable = true;
+
+ # Enable smartd notifications
+ services.smartd.notifications.mail = {
+ enable = true;
+ sender = serverEmail;
+ recipient = serverEmail;
+ };
+}
diff --git a/hosts/poweredge/nvidia.nix b/hosts/poweredge/nvidia.nix
new file mode 100644
index 0000000..257a46c
--- /dev/null
+++ b/hosts/poweredge/nvidia.nix
@@ -0,0 +1,22 @@
+{ config, pkgs, ... }: {
+ # Configure nixpkgs for nvidia/cuda
+ nixpkgs.config.allowUnfree = true;
+ nixpkgs.config.cudaSupport = true;
+
+ # NVIDIA
+ services.xserver.videoDrivers = [ "nvidia" ]; # xserver.videoDrivers does not imply X
+ hardware.graphics.enable = true;
+ hardware.nvidia = {
+ modesetting.enable = true; # Required
+ nvidiaSettings = false; # Don't need gui
+ open = false;
+ package = config.boot.kernelPackages.nvidiaPackages.legacy_580; # Support for P600
+ powerManagement.enable = false; # Can cause bugs
+ powerManagement.finegrained = false; # Only works on turing and newer
+ };
+
+ # Packages
+ environment.systemPackages = with pkgs; [
+ config.hardware.nvidia.package # nvidia-smi
+ ];
+}
diff --git a/hosts/poweredge/resources/secrets/ddns-updater-config.yaml b/hosts/poweredge/resources/secrets/ddns-updater-config.yaml
new file mode 100644
index 0000000..3be017b
--- /dev/null
+++ b/hosts/poweredge/resources/secrets/ddns-updater-config.yaml
@@ -0,0 +1,16 @@
+ddns-updater-config: ENC[AES256_GCM,data:vJ3z4R6P1gHKfkm6L2mQl68MKDJwpMNmrAOQo+4GkO2NC6EjKTLoSKhFiaGWVjMm7nrVfYRV+U/6b4VJXV4qURWhsm41t3x8zXAtt0viLC6pv+uMtuxadhU2Zxij4U2bSiMn6sSbfHd3uGIym7FnfOIL3LPEanVMuRUk20a0ZgHBdq1BPk6r5V8AoGfsu1XWHTvnO4ggg9oQPtGhurKTXixTD0Rb1Iv43JXLXqK/O3JGD5h4XbDmXB9eTqiBHUgZ0E4F5SE23L5mO0kI0TNNph2lTHXdfB+5,iv:xFry3gzdvvYh127yhYySvp5UHDa8Y+t/bg2+mwJ/HXo=,tag:pH2CE2l2UpNJiLJ+tjVvqQ==,type:str]
+sops:
+ age:
+ - recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtWitQcVlaTmFVaHIraHlT
+ VFBDVEtlQUlqckN4eFF3YU95N3ZNU3JQcFNzCnkrR2xmTEtyUHRWQlRnTWZSaGVT
+ U0wvcGt6R0w4L3dSakVDVWVpTUhWbWMKLS0tIGVKSXVTL1B2L2FlSkQwSDVYd3Fk
+ WE8rLy81UEU5ZG9SaHRLOHNqOWUzWnMKBFtzJ9frroYk6hoW+1ww/3LpxCEa1Vtr
+ KNNnHKry8lQQDmalN5ZVYMTVAlTnQQ6QE7DxBukUwWYmizQ+BY8HDg==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-03-31T01:47:37Z"
+ mac: ENC[AES256_GCM,data:8ozC5JWR/s3nNK+njc7zO32/7ptd//wuWGWZPHXrPV1iVyYndczGgu0ekEyKeRCn/WwGE5pyt32gy0l2Y+k7j7mV6GJguy6qhltani6Mz2Gfy5sRohn5s2rBDTiSYEVAgGTRt56DLxGD36P6xFPm+wHGspjCzNALrPretuN5xFg=,iv:+/mlXEMEO80pDVpFwZmnyywvHR/V9zHkbloF/e/dJ6Q=,tag:O+Ox0xUzERjeB+VftiUNEg==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.12.1
diff --git a/hosts/poweredge/resources/secrets/grafana.yaml b/hosts/poweredge/resources/secrets/grafana.yaml
new file mode 100644
index 0000000..91b9188
--- /dev/null
+++ b/hosts/poweredge/resources/secrets/grafana.yaml
@@ -0,0 +1,16 @@
+secret_key: ENC[AES256_GCM,data:9GHuENHGk+VKukRuUJi+fxOX+ZFa5q5t5UiGtxSQOEctu9LOJqvHJerFv3U=,iv:6OJTeWbotDVj/ogg9WC170Q4Kdfd1PXf0hHwXd/YaUQ=,tag:qlnEl2uzDuhwOKp17R76Tg==,type:str]
+sops:
+ age:
+ - enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrc0tWWHhtM2Q5UC9weSsr
+ WDdaOXJjZGVoekJKaXUyUXhsRXMxMjQrYUFnCnBqTGJEMFF6clQ1RXd5NE9CenQ5
+ U0swNHdNUUxndnlycGVGM0lpY0dFaVEKLS0tIHVlK2MySUJzaGVXd21iWFFzcEJM
+ Nmh3V2hIbUhWNUxlOE1idTdWNWZ6alEKWae75mhvfpTNOcvm7x1zf19QkmZqrAf7
+ jpkyKDE//d4Rn3h4Ogpl5bdXb9RyOoSEJUG+EDPFyynqdodCJ2ucpw==
+ -----END AGE ENCRYPTED FILE-----
+ recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz
+ lastmodified: "2026-06-18T22:40:22Z"
+ mac: ENC[AES256_GCM,data:5HoPrQU8Q+SGEH4jQHy1otFXGILqttDpcbv+edNoKI/QFoUE74M4PrGzltlManZstqK3nfWV3HNIcY6GRe8TMP5ANC/8UIXfEDerP1Q0kj6xeyLumMCJ6iCjygF2SzL5YMWqBSsCJvH5O10sWncc2iPnGrQPJ8PksIxkXCe4YYk=,iv:f0WpJwENciWkyDKpGM/PaE2lmXWvNiMAxNpqlCEFhM8=,tag:nQAPTJPYsgRIUkDxktIvmA==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.13.1
diff --git a/hosts/poweredge/resources/secrets/mailer-pass.yaml b/hosts/poweredge/resources/secrets/mailer-pass.yaml
new file mode 100644
index 0000000..331bd66
--- /dev/null
+++ b/hosts/poweredge/resources/secrets/mailer-pass.yaml
@@ -0,0 +1,16 @@
+pass: ENC[AES256_GCM,data:RHOvLwbDIb8FZ+dG66e5U43qR0aXlLLZGAnlbRjSl8hxCMEtJ4940nggiaIV75jCaiWyLutay7MrKPKZBHDZwBIqcJYQRWm1zWGkoZi0/bX38vUFWOpI4qku9fIB2qll,iv:bqEnTagxlRqlAmMgFCtXXCSSlODE598yoV4fU0jSYL8=,tag:c/ZiGCDSb8quDoYiIKbMeQ==,type:str]
+sops:
+ age:
+ - recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEUXlZaUhSUkNGK2xpVzRp
+ OEhYMTV6bnpPZC9tdHZWbnZxcUp6WWVLMnhFCmZmckVBckdRS1g0MjJQdE80S2Js
+ aGlNek1nSmU2aGI4cWVXR0NmbjJwa00KLS0tIDJ3N3BoenQ5ZW02K3BLNWxkWU5y
+ Ym56YzI5Zk9KeFhzZXJXR3NoOUl0ckEKOLweZrk/Pe6BG48+RrwOxyOy0Zb768aZ
+ YIxTBv/qSzZei6VqZHiIwTUEMyE7z3CS0dBFws6q4fB4LfIpv6fiYg==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-03-31T01:37:29Z"
+ mac: ENC[AES256_GCM,data:WIGXvuwB4bcBDfMRrrMQ7faUkxFdreyYiuy6bNPI2pzvvUFTSo/lJTv/DjisSARdYmFHFvdResIXUjg75Sc2I5IrvRxZxnYqx/3z5k/WOFWb8HSKH2H+OUHtLkqWJSCQ9YBuX2tys93mEXgwchPpn4nzVaYBgxZl54F3icX7tsE=,iv:BS9KPGkVaH0G0bAZz6+LR0NDcmqw6khOkih5DyvGyug=,tag:dA9YVL1xEqUqe6hDzOH7XQ==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.12.1
diff --git a/hosts/poweredge/resources/secrets/router.yaml b/hosts/poweredge/resources/secrets/router.yaml
new file mode 100644
index 0000000..edf7b41
--- /dev/null
+++ b/hosts/poweredge/resources/secrets/router.yaml
@@ -0,0 +1,17 @@
+wg0: ENC[AES256_GCM,data:hxZrOTMx2lS7dkQPV48jUbHxoNr32KSM/wG0OPY8mcgHX4FsHStdH/CN4qlkH9co90yFKjwqcJ3fHXaSbIhp3oIYTo1R2/8KggHV+az4skb7v4kzZgU5u5V2B57gIRFrPHBBq1UtDkBBpANr7zypeLPuEzvCQaZiexrsCcET7xwhsEYtVc5WlISMHhi6sFzo1AeEVfwFvIUN9geXJwP3JycYRpaNXOVYAbPwer8fYa6mnFZIk+sruCO85QNoWQ2T1wPiC9U7Ki9q+FOaOzJWGdpiG3nQdeYlDgAwoPRNi3c0J6t3CS9xC+qC0y4esOL7ay4S2dDoL9ftChQjhWaGdq5+LeyXAPlATGs5aFtBMwEecQ5OPXTri2D5m/gVLW/TGmUT5ZVPymm/wAX+m4IWK6X3FylWnlEoUh+4cBO+2zjTbXEccRuJTOEZAGIsEg==,iv:ycsGniDPFOvFFGqCgiyCWkCS8AxFV7JbXgUxDPXAFmk=,tag:uXkB8b7S3sW9noTIK4J/Zw==,type:str]
+wg1: ENC[AES256_GCM,data:4qwFVFszUQgBquAcgA4dB0TdBmTxprZLaL7+FQjrqG8avDFNxZyUFc9fHXiNRsvbgr+UKGVhdOast0y6kOaeQqqPiHLqQD/MN5wu8+SLFTaNppOKiwsjrK9x43GCxRT/ytmQWgb3pBVf7ruwbmykjDhZ577f3yhnNYhJuHoGLvTdJ18pxSE6DaukrJTnwa6BhxKv62IGlGHLYi5u9keR3LFCBwYyAIEjU9E+Na7mZ4cM2Y61/e0KTekMcQHqlBqvWmIDbdnLsoC/JVl/A8gIlU1TiOqo7geiUte/wwzc3dtkK+WQWuvaDrdtWdsUaKJEFPZhmx6KWZk4gTMdIlbP+KMm0X39rXIkMMyfWqs2rklnwm+BIg1vayQYsuAH3DXofxNlWcOEsjvnw19kPy8CrYOFAx2tPeyUWs1mFEQYeB0gafvRrFJfMUkh4uQuJnznjJH+8fmVM66EA6GLSRFUsjK/v4yEKftQQkYKuWRO9MpO5kTYo0YiSO9xOPbxNe+VVC1oJnQxaTeXNV2JZLjce/3+aZ3oFu5KG11yx+OqfRDBdJVcg6jEgVBlNY88ZrNTUUb9tEqlSXwuradURX1GekYwM0fBRTW+f/oiJFsbbBA8SODWl/50HnxcTqkeiYmnB2PA167nFLFOL/MYRh+txXbpcH4lwoPshPjGnM3XGxUxNIU+xqFFLHn/h6WhiyUZ6SkRgNGaGQ4w6RuRvZm3aHb0wAW0pwOnPoMbh2XDVTkdjFLxmJpAUHfy2W+FIYAGIU0DcT3/r2dW91hDSzjnysRxrPRiOc37nvsrqxjZd0+WI4KtHhpgxGkPyoV6CcFcKr7GrImlh2cVDr3oxrUHI9JrmD7VWKJV/ws6tSZsfHc=,iv:zXw3y9MIyaw/zXXl4CgavbP7QbAritZdhAWBL40vhtw=,tag:SXsG2HtActAlJrt7foqldA==,type:str]
+sops:
+ age:
+ - enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxOFFVRFl2MDhiUVRPdnZJ
+ b3BSZ0pSaG8vR3o3SGlmVENvN3ZObHU3eUc4CmJrdk8rbXpBMWhTZ2hvdlk0VWo1
+ ejdnTDR4bFlUUXc4Smd0NEp6b3crZFUKLS0tIFBsQUdRWjZLSDY1ZlFaRDdLOVhs
+ MFFGY0NSU3NaK2U0U1NndUIyYm54ZWsKZ6q9j1fNaNSzBA0rbZYyUWt3U2V/7/9/
+ FZTKd8mH/uCoxvK8unlVZ9uYNADRh2smp7LwK1AWie/6khAIFqVBeg==
+ -----END AGE ENCRYPTED FILE-----
+ recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz
+ lastmodified: "2026-06-22T19:36:47Z"
+ mac: ENC[AES256_GCM,data:Dvl3nQrqpPiYUfRHJuLQCbt0QF1duV0/E+S8A2kQ3zjP5NxmO4lKFRXgcWwsDzm+IQl4pcIMQ+FqU/C7ujTqxQ3/PSRx4o51POWDwJA5DmNpVNzH87BuXDxXPiItlPKzAfpbs8TwGCIOvmOcEP7Pd55eWJucRAq29N6nnixFHHQ=,iv:G91DcLnshSlBrtTTyJdJyuOa4wgjNhWsoVRDUk+WJOU=,tag:9Fcj8Oxu5q/u9pAiU18xwg==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.13.1
diff --git a/hosts/poweredge/resources/secrets/transmission.yaml b/hosts/poweredge/resources/secrets/transmission.yaml
new file mode 100644
index 0000000..5945804
--- /dev/null
+++ b/hosts/poweredge/resources/secrets/transmission.yaml
@@ -0,0 +1,17 @@
+ovpn-auth: ENC[AES256_GCM,data:3F3/4v0xOsdWguywaKxE6IB6pzHONWk6F6Es,iv:7ZJUyeb0EZTVkTIk5W2rhBjXmUHm2TLe5/YKgOJiqPA=,tag:idiz76VtruJ2NOhUhtbXjw==,type:str]
+ovpn-config: ENC[AES256_GCM,data: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,iv:Uy0Z4W4PUcwfkGfR6Mtf6AWShsU3dmLz+H9JOJd463c=,tag:B1KAAANSnRvb0ECwYP0QxQ==,type:str]
+sops:
+ age:
+ - enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVNkthU0srVEFZMk43ZFFu
+ dEJ3QkJyMEJ2OUhCR1Nwc3pQSHEzaXhjZFRzCm9idXlpUVM2WTRXQWY2SzFJei93
+ bzdEdEdiS1NHYy9mYWY3UU5xcmJMN2MKLS0tIGtSblFnZDdHK1ltUTNyWUFVdFd5
+ czZxZzUvSjlzcmtZRURleURwWGZVSjQK+DsUTq8yTkKZGK2rj1c9tFPm9nRoAjkQ
+ D5HFkN8uRVTB91JEx077I8VZLXJgJ6HFGOpW2EGV4nDilL9YP288Eg==
+ -----END AGE ENCRYPTED FILE-----
+ recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz
+ lastmodified: "2026-06-20T18:05:47Z"
+ mac: ENC[AES256_GCM,data:+w9BPkacf5X9hJhh5Bmm8nf5eANZs07K/BS3c1q6jVdUoHPkVbOz0EbAEQkheW8xQAH5lIQl0D5GXSbGyhLnV3mKwL3RgV532aAY02IT68ygDvFPx6ziBEmqBlWaTZ3DopKOqPzje0olMuefYF1Li1LihmLEX3kMxwBFOr8oSn0=,iv:Lkq8NdRjbMI21bfVVoq08kgZQmgdFrh+xKgd8j/VAkc=,tag:bMwLv5ABrMSzwq5t6/s65g==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.13.1
diff --git a/hosts/poweredge/router-hosts.nix b/hosts/poweredge/router-hosts.nix
new file mode 100644
index 0000000..2a08558
--- /dev/null
+++ b/hosts/poweredge/router-hosts.nix
@@ -0,0 +1,84 @@
+let
+ localDomain = "home.lan"; # TODO RFC8375 .home.arpa
+ dhcp = {
+ inherit staticLeases;
+ defaultGateway = "192.168.1.1";
+ localhostIp = "192.168.1.1";
+ rangeStart = "192.168.1.50";
+ rangeEnd = "192.168.1.250";
+ };
+ staticLeases = {
+ # Network
+ idrac-7N94GK2 = {
+ macAddress = "50:9a:4c:5d:c3:7c";
+ staticIp = "192.168.1.2";
+ };
+ OpenWrt-Attic = {
+ macAddress = "34:98:b5:60:5e:be";
+ staticIp = "192.168.1.3";
+ };
+ OpenWrt-Basement = {
+ macAddress = "8c:3b:ad:35:c7:8c";
+ staticIp = "192.168.1.4";
+ };
+ ArcherC54 = {
+ macAddress = "12:eb:b6:13:f9:e2";
+ staticIp = "192.168.1.5";
+ };
+ # Desktops
+ T495 = {
+ macAddress = "04:33:c2:9d:34:74";
+ staticIp = "192.168.1.11";
+ };
+ optiplex = {
+ macAddress = "e4:54:e8:bc:ba:05";
+ staticIp = "192.168.1.12";
+ };
+ X230 = {
+ macAddress = "84:3a:4b:60:34:c4";
+ staticIp = "192.168.1.13";
+ };
+ # Services
+ gnuslashprinter = {
+ macAddress = "00:23:24:5b:f0:6d";
+ staticIp = "192.168.1.40";
+ };
+ immich = {
+ macAddress = "02:00:00:00:00:01";
+ staticIp = "192.168.1.41";
+ };
+ jellyfin = {
+ macAddress = "02:00:00:00:00:02";
+ staticIp = "192.168.1.42";
+ };
+ gitea = {
+ macAddress = "02:00:00:00:00:03";
+ staticIp = "192.168.1.43";
+ };
+ bitcoind = {
+ macAddress = "02:00:00:00:00:04";
+ staticIp = "192.168.1.44";
+ };
+ filebrowser = {
+ macAddress = "02:00:00:00:00:05";
+ staticIp = "192.168.1.45";
+ };
+ grafana = {
+ macAddress = "02:00:00:00:00:06";
+ staticIp = "192.168.1.46";
+ };
+ transmission = {
+ macAddress = "02:00:00:00:00:07";
+ staticIp = "192.168.1.47";
+ };
+ };
+ dns.hostOverrides = {
+ "router.${localDomain}" = "192.168.1.1";
+ "poweredge.${localDomain}" = "192.168.1.10";
+ };
+in {
+ services._router.dnsDhcpConfig = {
+ inherit localDomain dhcp dns;
+ };
+ networking.hosts."192.168.1.1" = [ "router.${localDomain}" "router" ];
+}
diff --git a/hosts/poweredge/transmission.nix b/hosts/poweredge/transmission.nix
new file mode 100644
index 0000000..cded95d
--- /dev/null
+++ b/hosts/poweredge/transmission.nix
@@ -0,0 +1,118 @@
+{ config, pkgs, ... }: {
+ # Secrets
+ sops.secrets.transmission-ovpn-config = { sopsFile = ./resources/secrets/transmission.yaml; key = "ovpn-config"; };
+ sops.secrets.transmission-ovpn-auth = { sopsFile = ./resources/secrets/transmission.yaml; key = "ovpn-auth"; };
+
+ # Container
+ containers.transmission = let
+ home = "/var/lib/transmission";
+ download-dir = "${home}/complete";
+ incomplete-dir = "${home}/incomplete";
+ in {
+ autoStart = true;
+ privateNetwork = true;
+ enableTun = true; # OpenVPN requires
+ hostBridge = "br-lan0";
+ localMacAddress = "02:00:00:00:00:07";
+
+ # Download dirs
+ bindMounts = {
+ "${download-dir}" = {
+ hostPath = "/media/ingens/media/.incomplete";
+ isReadOnly = false;
+ };
+ "${incomplete-dir}" = {
+ hostPath = "/media/ingens/media/.complete";
+ isReadOnly = false;
+ };
+ };
+
+ # Bind secrets
+ bindMounts."/run/secrets/ovpn-config.ovpn" = {
+ hostPath = config.sops.secrets.transmission-ovpn-config.path;
+ isReadOnly = true;
+ };
+ bindMounts."/run/secrets/ovpn-auth" = {
+ hostPath = config.sops.secrets.transmission-ovpn-auth.path;
+ isReadOnly = true;
+ };
+
+ config = { lib, config, ... }: {
+ # Network
+ networking.enableIPv6 = false; # Prevent ip leaks
+ networking.interfaces.eth0.useDHCP = true;
+ networking.firewall.interfaces = {
+ eth0.allowedTCPPorts = [ 80 ]; # RPC interface
+ # Torrent ports
+ tun0 = {
+ allowedTCPPorts = [ 51413 ];
+ allowedUDPPorts = [ 51413 ];
+ };
+ };
+
+ # Transmission
+ services.transmission = {
+ inherit home;
+ enable = true;
+
+ settings = {
+ inherit download-dir incomplete-dir;
+ };
+ };
+
+ # TODO remove (#258793)
+ systemd.services.transmission.serviceConfig = {
+ RootDirectoryStartOnly = lib.mkForce null;
+ RootDirectory = lib.mkForce null;
+ };
+
+ # Reverse proxy
+ services.caddy = {
+ enable = true;
+ virtualHosts.":80".extraConfig = ''
+ reverse_proxy localhost:9091
+ '';
+ };
+
+ # OpenVPN
+ services.openvpn.servers.main = {
+ config = ''
+ config /run/secrets/ovpn-config.ovpn
+ auth-user-pass /run/secrets/ovpn-auth
+ '';
+ autoStart = true;
+ updateResolvConf = true;
+ };
+
+ # VPN killswitch
+ networking.firewall.extraCommands = ''
+ # Get domain name host and port from ovpn config
+ SERVER_HOST=$(${pkgs.gawk}/bin/awk '/^remote /{print $2;exit}' /run/secrets/ovpn-config.ovpn)
+ SERVER_PORT=$(${pkgs.gawk}/bin/awk '/^remote /{print $3;exit}' /run/secrets/ovpn-config.ovpn)
+
+ # Resolve server ip from host
+ while [ -z "$SERVER_IP" ]; do
+ sleep 3
+ SERVER_IP=$(${pkgs.getent}/bin/getent hosts "$SERVER_HOST" 2>/dev/null | ${pkgs.gawk}/bin/awk '{print $1}')
+ echo "SERVER_IP: $SERVER_IP"
+ done
+
+ # Only allow out traffic from tun0
+ ${pkgs.iptables}/bin/iptables -P OUTPUT DROP
+ ${pkgs.iptables}/bin/iptables -A OUTPUT -o lo -j ACCEPT
+ ${pkgs.iptables}/bin/iptables -A OUTPUT -o tun0 -j ACCEPT
+ ${pkgs.iptables}/bin/iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # Exception: allow established connections
+ ${pkgs.iptables}/bin/iptables -A OUTPUT -p udp -d "$SERVER_IP" --dport "$SERVER_PORT" -j ACCEPT
+
+ # Allow DNS
+ DNS_IP=$(${pkgs.gawk}/bin/awk '/^nameserver /{print $2; exit}' /etc/resolv.conf)
+ ${pkgs.iptables}/bin/iptables -A OUTPUT -o eth0 -p udp -d "$DNS_IP" --dport 53 -j ACCEPT
+ ${pkgs.iptables}/bin/iptables -A OUTPUT -o eth0 -p tcp -d "$DNS_IP" --dport 53 -j ACCEPT
+
+ # Allow transmission RPC
+ '';
+
+ system.stateVersion = "26.05";
+ };
+ };
+}
diff --git a/hosts/sweetiepc/configuration.nix b/hosts/sweetiepc/configuration.nix
new file mode 100644
index 0000000..7bf5fcb
--- /dev/null
+++ b/hosts/sweetiepc/configuration.nix
@@ -0,0 +1,51 @@
+{ config, lib, pkgs, home-manager, ... }: {
+ #imports = [ ./wg.nix ];
+
+ # Setup bootloader
+ boot._loader = {
+ enable = true;
+ loader = "grub";
+ };
+
+ # Enable common options
+ _archetypes = {
+ profiles = {
+ desktop = {
+ enable = true;
+ home.users.timmy.enable = true;
+ };
+ zfs.enable = true;
+ btrfs.enable = true;
+ gnome.enable = true;
+ };
+ collections = {
+ desktop = {
+ office.enable = true;
+ };
+ };
+ };
+
+ # Import zfs pools
+ boot.zfs.extraPools = [ "geminus" "quattuor" ];
+
+ # Enable user timmy
+ _users.timmy = {
+ enable = true;
+ };
+
+ # Disable suspend
+ systemd._suspend.disable = true;
+
+ # Configure home
+ home-manager.users.timmy = {
+ fonts.fontconfig = {
+ subpixelRendering = "rgb";
+ hinting = "none";
+ };
+ };
+
+ # Without this, "ZFS requires networking.hostId to be set" will be raised
+ networking.hostId = "fdde503a";
+
+ system.stateVersion = "26.05";
+}
diff --git a/hosts/sweetiepc/hardware-configuration.nix b/hosts/sweetiepc/hardware-configuration.nix
new file mode 100644
index 0000000..5ed162c
--- /dev/null
+++ b/hosts/sweetiepc/hardware-configuration.nix
@@ -0,0 +1,38 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+ imports =
+ [ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = [ "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
+ boot.initrd.kernelModules = [ ];
+ boot.kernelModules = [ "kvm-intel" ];
+ boot.extraModulePackages = [ ];
+
+ fileSystems."/" =
+ { device = "/dev/disk/by-uuid/91572803-436d-4f43-b41f-dfba2103752e";
+ fsType = "btrfs";
+ options = [ "subvol=@" ];
+ };
+
+ fileSystems."/home" =
+ { device = "/dev/disk/by-uuid/91572803-436d-4f43-b41f-dfba2103752e";
+ fsType = "btrfs";
+ options = [ "subvol=@home" ];
+ };
+
+ fileSystems."/boot" =
+ { device = "/dev/disk/by-uuid/F618-D6C2";
+ fsType = "vfat";
+ options = [ "fmask=0022" "dmask=0022" ];
+ };
+
+ swapDevices = [ ];
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/sweetiepc/resources/secrets/wg.yaml b/hosts/sweetiepc/resources/secrets/wg.yaml
new file mode 100644
index 0000000..9edaea5
--- /dev/null
+++ b/hosts/sweetiepc/resources/secrets/wg.yaml
@@ -0,0 +1,16 @@
+wg0: ENC[AES256_GCM,data:xow2iL5l7MqlUeCZ2e9R4ygKlK3+ZP2yIR73aHWiIaO24aF02I88cWajOvs44/CP2tTHJLM6OltN/rKr3+4DLCbhOrngl/uFyWv/Xva4n9ZCWBv0DgtQ0Qbk/Bi6Q+h3Uf7XXlOtsUNvzSXvW4L+EcwsTMQRU5CIpSHcIsyG/IsQ1ypHpzDPM8j3HHtoPp6zaYPpQiELoMIIuIjvnvjTAAEWYMPuiV4OgJy2kiAUFkEwDud3MV/e/D/q861lKSz5qiKWnxE/So9kmA4HaSw9EOi46EcFojat7r3LiludcMify4V+IliVs7LPYf8kaHSOPWdmd8cgDeYP3Np0ydhB2+2xSbRLCgZvJWuxkStbeNjA5sC3tAmA86I+axhjVGRD9NdbpksdDORBMJhx2iWVhBBvM5p/Ya0f5nXHlsLw4MLSCKOZlTf9OM5YlYGw/GA30nJbcOVx1TzK+BJ0cYsz7U4YJWEMHTEPIQ==,iv:TM0Csdn65smwseVzq36IJAcapEZx+yjhRyG3Atz2Jk0=,tag:A6wWrO8PJAPFaI2IFq5Ucw==,type:str]
+sops:
+ age:
+ - enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlS0pyVGtTekV0MXQ5WHYy
+ MlRZYnVVaXdKVDlGNlZOekxUWng4NUlWTXlBCnBzbzNwUWtTdExBUXpCNy83YStm
+ Y0xvTlo5bkczS2xSdUtjenNjQlhDNlkKLS0tIGd6RjlZWGtZNVpXeVNpd0EyTk5h
+ cWN0b0dXQjNwNFZjdXdiUDZCUDZETkkKJlDzHxpRHbh4+LPEaWjrmiFFGMOL4Hdi
+ lMhSrn/EmMmf6p82FVpWMfr7fEFc9lCRw1VytKNUc1Le3nIpkM8iJQ==
+ -----END AGE ENCRYPTED FILE-----
+ recipient: age1fl4rk54n2xv4vssjkw4nywy93s4yqv69uzq2f64zuwd8fjq5u4cq9wmmsw
+ lastmodified: "2026-06-22T19:17:43Z"
+ mac: ENC[AES256_GCM,data:o+p9xd02QZ9VcUwiAMAGExf5FBrDaKCkOen58d1zTzZuiH2Svcmg5xrA+N23XuEi9kVTcqby9fpymgTOlwNZn8JbmMYjLLiykBIWQvOPKNjAGzp2A9KFA1DpIzTegM+xXbnKv+FEZdRagAojkAUftUUzQYAUsmQVDnflzwNsSUI=,iv:s0oM+TgNNUgNZQ99Pb33UCUbhdZJ93Dv1X+GK6oFUwo=,tag:306d418MjlEZnPi6L2qd/g==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.13.1
diff --git a/hosts/sweetiepc/wg.nix b/hosts/sweetiepc/wg.nix
new file mode 100644
index 0000000..bf459d9
--- /dev/null
+++ b/hosts/sweetiepc/wg.nix
@@ -0,0 +1,7 @@
+{ config, pkgs, inputs, ... }: {
+ sops.secrets.wg0 = { sopsFile = ./resources/secrets/wg.yaml; key = "wg0"; };
+
+ networking.wg-quick.interfaces = {
+ wg0.configFile = config.sops.secrets.wg0.path;
+ };
+}