diff --git a/SwarselSystems.org b/SwarselSystems.org index cf41a25..c93f571 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -563,8 +563,7 @@ Nix on Android also demands an own flake output, which is provided here. lib.swarselsystems.forEachSystem (pkgs: import inputs.nix-topology { inherit pkgs; modules = [ - # Your own file to define global topology. Works in principle like a nixos module but uses different options. - # ./topology.nix + "${self}/topology" { inherit (self) nixosConfigurations; } ]; }); @@ -836,6 +835,7 @@ My work machine. Built for more security, this is the gold standard of my config swarselsystems = lib.recursiveUpdate { + info = "Framework Laptop 16, 7940HS, RX7700S, 64GB RAM"; firewall = lib.mkForce true; wallpaper = self + /wallpaper/lenovowp.png; hasBluetooth = true; @@ -1093,6 +1093,7 @@ This is my main server that I run at home. It handles most tasks that require bi swarselsystems = lib.recursiveUpdate { + info = "ASRock J4105-ITX, 32GB RAM"; isImpermanence = false; isSecureBoot = true; isCrypted = true; @@ -1336,7 +1337,7 @@ This machine mainly acts as an external sync helper. It manages the following th acmeRoot = null; locations = { "/" = { - proxyPass = "http://localhost:8384/"; + proxyPass = "http://localhost:8384"; extraConfig = '' client_max_body_size 0; ''; @@ -1428,6 +1429,7 @@ This machine mainly acts as an external sync helper. It manages the following th swarselsystems = lib.recursiveUpdate { + info = "VM.Standard.E2.1.Micro"; flakePath = "/root/.dotfiles"; isImpermanence = false; isSecureBoot = false; @@ -1493,6 +1495,12 @@ This machine mainly acts as an external sync helper. It manages the following th ]; }; + topology.self.interfaces.wg = { + addresses = ["192.168.3.4"]; + renderer.hidePhysicalConnections = true; + virtual = true; + type = "wireguard"; + }; networking = { nftables.enable = lib.mkForce false; @@ -1534,26 +1542,13 @@ This machine mainly acts as an external sync helper. It manages the following th services = { nginx = { virtualHosts = { - # "newway.swarsel.win" = { - # enableACME = true; - # forceSSL = true; - # acmeRoot = null; - # locations = { - # "/" = { - # proxyPass = "http://192.168.1.2:8080"; - # extraConfig = '' - # client_max_body_size 0; - # ''; - # }; - # }; - # }; "syncthing.swarsel.win" = { enableACME = true; forceSSL = true; acmeRoot = null; locations = { "/" = { - proxyPass = "http://localhost:8384/"; + proxyPass = "http://localhost:8384"; extraConfig = '' client_max_body_size 0; ''; @@ -1675,6 +1670,7 @@ This machine mainly acts as an external sync helper. It manages the following th swarselsystems = lib.recursiveUpdate { + info = "VM.Standard.A1.Flex, 4 OCPUs, 24GB RAM"; flakePath = "/home/swarsel/.dotfiles"; isImpermanence = true; isSecureBoot = false; @@ -1929,6 +1925,7 @@ This is a slim setup for developing base configuration. I do not track the hardw swarselsystems = lib.recursiveUpdate { + info = "~SwarselSystems~ remote install helper"; wallpaper = self + /wallpaper/lenovowp.png; isImpermanence = true; isCrypted = false; @@ -2130,6 +2127,7 @@ Also, an initial bash history is provided to allow for a very quick local deploy "${self}/modules/iso/minimal.nix" "${self}/modules/nixos/common/sharedsetup.nix" + "${self}/modules/nixos/common/topology.nix" "${self}/modules/home/common/sharedsetup.nix" inputs.home-manager.nixosModules.home-manager @@ -2153,7 +2151,10 @@ Also, an initial bash history is provided to allow for a very quick local deploy }; }; config = { - node.name = "drugstore"; + node.name = lib.mkForce "drugstore"; + swarselsystems = { + info = "~SwarselSystems~ installer ISO"; + }; home-manager.users."${primaryUser}" = { home = { stateVersion = "23.05"; @@ -2163,7 +2164,9 @@ Also, an initial bash history is provided to allow for a very quick local deploy }; }; }; - swarselsystems.modules.general = lib.mkForce true; + swarselsystems = { + modules.general = lib.mkForce true; + }; }; home-manager.users.root.home = { stateVersion = "23.05"; @@ -2359,9 +2362,9 @@ I also set the =WLR_RENDERER_ALLOW_SOFTWARE=1= to allow this configuration to ru firewall.enable = true; }; - swarselsystems = lib.recursiveUpdate { + info = "~SwarselSystems~ demo host"; wallpaper = self + /wallpaper/lenovowp.png; initialSetup = true; isImpermanence = true; @@ -5024,6 +5027,7 @@ TODO pkgsFor = lib.genAttrs (import systems) (system: import inputs.nixpkgs { inherit system; + overlays = [ self.overlays.default ]; config.allowUnfree = true; } ); @@ -5071,7 +5075,9 @@ TODO _module.args.primaryUser = linuxUser; } ] ++ - (if (host == "iso") then [ ] else + (if (host == "iso") then [ + inputs.nix-topology.nixosModules.default + ] else ([ # put nixos imports here that are for all servers and normal hosts inputs.nix-topology.nixosModules.default @@ -5268,6 +5274,165 @@ in cat "$out" fi #+end_src +**** nix-topology + +#+begin_src nix :tangle topology/default.nix + { config, ... }: + let + inherit (config.lib.topology) + mkInternet + mkDevice + mkSwitch + mkRouter + mkConnection + ; + in + { + renderer = "elk"; + + networks = { + home-lan = { + name = "Home LAN"; + cidrv4 = "192.168.1.0/24"; + }; + wg = { + name = "Wireguard Tunnel"; + cidrv4 = "192.168.3.0/24"; + }; + }; + + nodes = { + internet = mkInternet { + connections = [ + (mkConnection "moonside" "wan") + (mkConnection "pfsense" "wan") + (mkConnection "sync" "wan") + ]; + }; + + sync.interfaces.wan = { }; + moonside.interfaces.wan = { }; + + pfsense = mkRouter "pfSense" { + info = "HUNSN RM02"; + image = ../topology/images/hunsn.png; + interfaceGroups = [ + [ + "eth2" + "eth3" + "eth4" + "eth5" + "eth6" + ] + [ "wan" ] + ]; + interfaces.wg0 = { + addresses = [ "192.168.3.1" ]; + network = "wg"; + virtual = true; + type = "wireguard"; + }; + + connections = { + eth2 = mkConnection "switch-livingroom" "eth1"; + eth4 = mkConnection "winters" "eth1"; + eth3 = mkConnection "switch-bedroom" "eth1"; + eth6 = mkConnection "wifi-ap" "eth1"; + wg = mkConnection "moonside" "wg"; + }; + interfaces = { + eth2 = { + addresses = [ "192.168.1.1" ]; + network = "home-lan"; + }; + eth3 = { + addresses = [ "192.168.1.1" ]; + network = "home-lan"; + }; + eth4 = { + addresses = [ "192.168.1.1" ]; + network = "home-lan"; + }; + eth6 = { + addresses = [ "192.168.1.1" ]; + network = "home-lan"; + }; + }; + }; + + winters.interfaces."eth1" = { }; + + wifi-ap = mkSwitch "Wi-Fi AP" { + info = "Huawei"; + image = ../topology/images/huawei.png; + interfaceGroups = [ + [ + "eth1" + "wifi" + ] + ]; + }; + + switch-livingroom = mkSwitch "Switch Livingroom" { + info = "TL-SG108"; + image = ../topology/images/TL-SG108.png; + interfaceGroups = [ + [ + "eth1" + "eth2" + "eth3" + "eth4" + "eth5" + "eth6" + "eth7" + "eth8" + ] + ]; + connections = { + eth2 = mkConnection "nswitch" "eth1"; + eth7 = mkConnection "pc" "eth1"; + eth8 = mkConnection "nbl-imba-2" "eth1"; + }; + }; + + nswitch = mkDevice "Nintendo Switch" { + info = "Nintendo Switch"; + image = ../topology/images/nintendo-switch.png; + interfaces.eth1 = { }; + }; + + pc = mkDevice "Windows Gaming Server" { + info = "i7-4790k, GTX970, 32GB RAM"; + image = ../topology/images/pc.png; + interfaces.eth1 = { }; + }; + + nbl-imba-2.interfaces.eth1 = { }; + + switch-bedroom = mkSwitch "Switch Bedroom" { + info = "TL-SG1005D"; + image = ../topology/images/TL-SG1005D.png; + interfaceGroups = [ + [ + "eth1" + "eth2" + "eth3" + "eth4" + "eth5" + ] + ]; + connections.eth2 = mkConnection "printer" "eth1"; + }; + + printer = mkDevice "Printer" { + info = "DELL C2665dnf"; + image = ../topology/images/DELL-C2665dnf.png; + interfaces.eth1 = { }; + }; + }; + } + +#+end_src ** NixOS :PROPERTIES: :CUSTOM_ID: h:6da812f5-358c-49cb-aff2-0a94f20d70b3 @@ -5637,6 +5802,8 @@ Mostly used to install some compilers and lsp's that I want to have available wh zls ansible-language-server + elk-to-svg + ]; nixpkgs.config.permittedInsecurePackages = [ @@ -6244,14 +6411,37 @@ Setup timezone and locale. I want to use the US layout, but have the rest adapte #+begin_src nix :tangle modules/nixos/common/meta.nix { lib, ... }: { - options.node.secretsDir = lib.mkOption { - description = "Path to the secrets directory for this node."; - type = lib.types.path; - default = ./.; + options = { + node = { + secretsDir = lib.mkOption { + description = "Path to the secrets directory for this node."; + type = lib.types.path; + default = ./.; + }; + name = lib.mkOption { + description = "Node Name."; + type = lib.types.str; + }; + }; }; - options.node.name = lib.mkOption { - description = "Node Name."; + } +#+end_src + +**** Topology + +#+begin_src nix :tangle modules/nixos/common/topology.nix + { self, lib, config, ... }: + { + options.swarselsystems.info = lib.mkOption { type = lib.types.str; + default = ""; + }; + config.topology = { + id = config.node.name; + self = { + hardware.info = config.swarselsystems.info; + icon = lib.mkIf config.swarselsystems.isLaptop "devices.laptop"; + }; }; } #+end_src @@ -7613,7 +7803,7 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t :END: #+begin_src nix :tangle modules/nixos/server/kavita.nix - { pkgs, lib, config, ... }: + { self, lib, config, pkgs, ... }: let serviceName = "kavita"; serviceUser = "kavita"; @@ -7635,6 +7825,12 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t networking.firewall.allowedTCPPorts = [ 8080 ]; + topology.self.services.kavita = { + name = "Kavita"; + info = "https://${serviceDomain}"; + icon = "${self}/topology/images/kavita.png"; + }; + services.kavita = { enable = true; user = serviceUser; @@ -7676,64 +7872,67 @@ Here I am forcing =startWhenNeeded= to false so that the value will not be set t :END: #+begin_src nix :tangle modules/nixos/server/jellyfin.nix -{ pkgs, lib, config, ... }: -let - serviceDomain = "screen.swarsel.win"; - servicePort = 8096; - serviceName = "jellyfin"; - serviceUser = "jellyfin"; -in -{ - options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server"; - config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" { - users.users."${serviceUser}" = { - extraGroups = [ "video" "render" "users" ]; - }; - nixpkgs.config.packageOverrides = pkgs: { - vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; - }; - hardware.graphics = { - enable = true; - extraPackages = with pkgs; [ - intel-media-driver # LIBVA_DRIVER_NAME=iHD - vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium) - vaapiVdpau - libvdpau-va-gl - ]; - }; - services.jellyfin = { - enable = true; - user = serviceUser; - openFirewall = true; # this works only for the default ports - }; + { pkgs, lib, config, ... }: + let + serviceDomain = "screen.swarsel.win"; + servicePort = 8096; + serviceName = "jellyfin"; + serviceUser = "jellyfin"; + in + { + options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" { + users.users."${serviceUser}" = { + extraGroups = [ "video" "render" "users" ]; + }; + nixpkgs.config.packageOverrides = pkgs: { + vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; + }; + hardware.graphics = { + enable = true; + extraPackages = with pkgs; [ + intel-media-driver # LIBVA_DRIVER_NAME=iHD + vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium) + vaapiVdpau + libvdpau-va-gl + ]; + }; - nodes.moonside.services.nginx = { - upstreams = { - "${serviceName}" = { - servers = { - "192.168.1.2:${builtins.toString servicePort}" = { }; + topology.self.services.jellyfin.info = "https://${serviceDomain}"; + + services.jellyfin = { + enable = true; + user = serviceUser; + openFirewall = true; # this works only for the default ports + }; + + nodes.moonside.services.nginx = { + upstreams = { + "${serviceName}" = { + servers = { + "192.168.1.2:${builtins.toString servicePort}" = { }; + }; }; }; - }; - virtualHosts = { - "${serviceDomain}" = { - enableACME = true; - forceSSL = true; - acmeRoot = null; - locations = { - "/" = { - proxyPass = "http://${serviceName}"; - extraConfig = '' - client_max_body_size 0; - ''; + virtualHosts = { + "${serviceDomain}" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "http://${serviceName}"; + extraConfig = '' + client_max_body_size 0; + ''; + }; }; }; }; }; }; - }; -} + } #+end_src **** navidrome @@ -7969,7 +8168,7 @@ in :END: #+begin_src nix :tangle modules/nixos/server/mpd.nix - { pkgs, lib, config, ... }: + { self, lib, config, pkgs, ... }: { options.swarselsystems.modules.server.mpd = lib.mkEnableOption "enable mpd on server"; config = lib.mkIf config.swarselsystems.modules.server.mpd { @@ -7997,6 +8196,12 @@ in mpv ]; + topology.self.services.mpd = { + name = "MPD"; + info = "http://localhost:3254"; + icon = "${self}/topology/images/mpd.png"; + }; + services.mpd = { enable = true; musicDirectory = "/media"; @@ -8498,6 +8703,8 @@ in extraGroups = [ "video" "render" "users" ]; }; + topology.self.services.immich.info = "https://${serviceDomain}"; + services.immich = { enable = true; host = "0.0.0.0"; @@ -8694,7 +8901,10 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= :END: #+begin_src nix :tangle modules/nixos/server/transmission.nix - { pkgs, lib, config, ... }: + { self, pkgs, lib, config, ... }: + let + serviceDomain = "store.swarsel.win"; + in { options.swarselsystems.modules.server.transmission = lib.mkEnableOption "enable transmission and friends on server"; config = lib.mkIf config.swarselsystems.modules.server.transmission { @@ -8751,6 +8961,18 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of =.eml= docker ]; + topology.self.services = { + radarr.info = "https://${serviceDomain}/radarr"; + readarr = { + name = "Readarr"; + info = "https://${serviceDomain}/readarr"; + icon = "${self}/topology/images/readarr.png"; + }; + sonarr.info = "https://${serviceDomain}/sonarr"; + lidarr.info = "https://${serviceDomain}/lidarr"; + prowlarr.info = "https://${serviceDomain}/prowlarr"; + }; + services = { radarr = { enable = true; @@ -9073,6 +9295,8 @@ This section exposes several metrics that I use to check the health of my server networking.firewall.allowedTCPPorts = [ servicePort prometheusPort ]; + topology.self.services.prometheus.info = "https://${serviceDomain}/${prometheusWebRoot}"; + services = { grafana = { enable = true; @@ -9342,9 +9566,10 @@ I am using this with CapyReader on my phone, set it up as a FreshRSS account wit FreshRSS claims to support HTTP header auth, but at least it does not work with my oauth2-proxy setup. Until this is fixed, I resorted to the "form" login, since I mostly do not use the web version anyways. #+begin_src nix :tangle modules/nixos/server/freshrss.nix - { lib, config, ... }: + { self, lib, config, ... }: let serviceName = "freshrss"; + serviceDomain = "signpost.swarsel.win"; in { options.swarselsystems.modules.server.freshrss = lib.mkEnableOption "enable freshrss on server"; @@ -9385,10 +9610,16 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with # }; }; + topology.self.services.freshrss = { + name = "FreshRSS"; + info = "https://${serviceDomain}"; + icon = "${self}/topology/images/freshrss.png"; + }; + services.freshrss = { enable = true; - virtualHost = "signpost.swarsel.win"; - baseUrl = "https://signpost.swarsel.win"; + virtualHost = serviceDomain; + baseUrl = "https://${serviceDomain}"; authType = "form"; dataDir = "/Vault/data/tt-rss"; defaultUser = "Swarsel"; @@ -9408,7 +9639,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with }; }; virtualHosts = { - "signpost.swarsel.win" = { + "${serviceDomain}" = { enableACME = true; forceSSL = true; acmeRoot = null; @@ -9454,6 +9685,9 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with proxy_pass_request_body off; ''; }; + "/api" = { + proxyPass = "http://${serviceName}"; + }; }; }; }; @@ -9621,6 +9855,9 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with #+begin_src nix :tangle modules/nixos/server/ankisync.nix { lib, config, ... }: + let + serviceDomain = "synki.swarsel.win"; + in { options.swarselsystems.modules.server.ankisync = lib.mkEnableOption "enable ankisync on server"; config = lib.mkIf config.swarselsystems.modules.server.ankisync { @@ -9629,6 +9866,11 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with sops.secrets.swarsel = { owner = "root"; }; + topology.self.services.anki = { + name = lib.mkForce "Anki Sync Server"; + info = "https://${serviceDomain}"; + }; + services.anki-sync-server = { enable = true; port = 27701; @@ -9644,7 +9886,7 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with services.nginx = { virtualHosts = { - "synki.swarsel.win" = { + "${serviceDomain}" = { enableACME = true; forceSSL = true; acmeRoot = null; @@ -10029,7 +10271,7 @@ To get other URLs (token, etc.), use https:///oauth2/openid//oauth2/openid//oauth2/openid//oauth2/openid/ - + SwarselSystems: NixOS + Emacs Configuration @@ -249,6 +249,13 @@
  • 3.1.3. Virtual hosts
  • 3.1.4. Utility hosts @@ -263,9 +270,9 @@
  • 3.1.4.3. Home-manager only (default non-NixOS)
  • 3.1.4.4. ChaosTheatre (Demo Physical/VM)
  • @@ -305,8 +312,8 @@
  • 3.2.1.27. fhs
  • 3.2.1.28. swarsel-displaypower
  • 3.2.1.29. swarsel-mgba
  • -
  • 3.2.1.30. swarsel-deploy
  • -
  • 3.2.1.31. sshrm
  • +
  • 3.2.1.30. swarsel-deploy
  • +
  • 3.2.1.31. sshrm
  • 3.2.2. Overlays (additions, overrides, nixpkgs-stable)
  • @@ -314,37 +321,39 @@
  • 3.2.4. Library functions
  • -
  • 3.2.5. Auxiliary files +
  • 3.2.5. Auxiliary files
  • @@ -356,58 +365,60 @@
  • 3.3.1.1. Imports, non-server settings
  • 3.3.1.2. Shared Configuration Options
  • 3.3.1.3. General NixOS settings (stateVersion)
  • -
  • 3.3.1.4. System Packages
  • -
  • 3.3.1.5. Setup home-manager base
  • -
  • 3.3.1.6. Setup home-manager specialArgs
  • -
  • 3.3.1.7. Setup login keymap
  • -
  • 3.3.1.8. User setup, Make users non-mutable
  • -
  • 3.3.1.9. Environment setup
  • -
  • 3.3.1.10. Security
  • -
  • 3.3.1.11. Reduce systemd timeouts
  • -
  • 3.3.1.12. Hardware settings
  • -
  • 3.3.1.13. Pulseaudio
  • -
  • 3.3.1.14. Pipewire
  • -
  • 3.3.1.15. Common network settings
  • -
  • 3.3.1.16. Time, locale settings
  • -
  • 3.3.1.17. Meta options
  • -
  • 3.3.1.18. sops
  • -
  • 3.3.1.19. PII management
  • -
  • 3.3.1.20. Theme (stylix)
  • -
  • 3.3.1.21. Programs (including zsh setup) +
  • 3.3.1.4. Share configuration between nodes
  • +
  • 3.3.1.5. System Packages
  • +
  • 3.3.1.6. Setup home-manager base
  • +
  • 3.3.1.7. Setup home-manager specialArgs
  • +
  • 3.3.1.8. Setup login keymap
  • +
  • 3.3.1.9. User setup, Make users non-mutable
  • +
  • 3.3.1.10. Environment setup
  • +
  • 3.3.1.11. Security
  • +
  • 3.3.1.12. Reduce systemd timeouts
  • +
  • 3.3.1.13. Hardware settings
  • +
  • 3.3.1.14. Pulseaudio
  • +
  • 3.3.1.15. Pipewire
  • +
  • 3.3.1.16. Common network settings
  • +
  • 3.3.1.17. Time, locale settings
  • +
  • 3.3.1.18. Meta options
  • +
  • 3.3.1.19. Topology
  • +
  • 3.3.1.20. sops
  • +
  • 3.3.1.21. PII management
  • +
  • 3.3.1.22. Theme (stylix)
  • +
  • 3.3.1.23. Programs (including zsh setup)
  • -
  • 3.3.1.22. Services +
  • 3.3.1.24. Services
  • -
  • 3.3.1.23. Hardware compatibility settings (Yubikey, Ledger, Keyboards) - udev rules +
  • 3.3.1.25. Hardware compatibility settings (Yubikey, Ledger, Keyboards) - udev rules
  • -
  • 3.3.1.24. System Login
  • -
  • 3.3.1.25. nix-ld
  • -
  • 3.3.1.26. Impermanence
  • -
  • 3.3.1.27. Summary of nixos-rebuild diff
  • -
  • 3.3.1.28. gnome-keyring
  • -
  • 3.3.1.29. Sway
  • -
  • 3.3.1.30. xdg-portal
  • -
  • 3.3.1.31. Podmam (distrobox)
  • -
  • 3.3.1.32. Appimage
  • -
  • 3.3.1.33. Handle lid switch correctly
  • -
  • 3.3.1.34. Low battery notification
  • -
  • 3.3.1.35. Lanzaboote
  • +
  • 3.3.1.26. System Login
  • +
  • 3.3.1.27. nix-ld
  • +
  • 3.3.1.28. Impermanence
  • +
  • 3.3.1.29. Summary of nixos-rebuild diff
  • +
  • 3.3.1.30. gnome-keyring
  • +
  • 3.3.1.31. Sway
  • +
  • 3.3.1.32. xdg-portal
  • +
  • 3.3.1.33. Podmam (distrobox)
  • +
  • 3.3.1.34. Appimage
  • +
  • 3.3.1.35. Handle lid switch correctly
  • +
  • 3.3.1.36. Low battery notification
  • +
  • 3.3.1.37. Lanzaboote
  • 3.3.2. Server @@ -438,8 +449,10 @@
  • 3.3.2.24. FreshRSS
  • 3.3.2.25. forgejo (git server)
  • 3.3.2.26. Anki Sync Server
  • -
  • 3.3.2.27. IDM (kanidm + oauth2-proxy)
  • -
  • 3.3.2.28. Firefly-III
  • +
  • 3.3.2.27. kanidm
  • +
  • 3.3.2.28. oauth2-proxy
  • +
  • 3.3.2.29. Firefly-III
  • +
  • 3.3.2.30. Koillection
  • 3.3.3. Darwin @@ -454,11 +467,11 @@
  • 3.3.4.3. VmWare
  • 3.3.4.4. Auto-login
  • 3.3.4.5. nswitch-rcm
  • -
  • 3.3.4.6. Framework
  • -
  • 3.3.4.7. AMD CPU
  • -
  • 3.3.4.8. AMD GPU
  • -
  • 3.3.4.9. Hibernation
  • -
  • 3.3.4.10. BTRFS
  • +
  • 3.3.4.6. Framework
  • +
  • 3.3.4.7. AMD CPU
  • +
  • 3.3.4.8. AMD GPU
  • +
  • 3.3.4.9. Hibernation
  • +
  • 3.3.4.10. BTRFS
  • 3.3.4.11. work
  • 3.3.4.12. Minimal Install
  • @@ -507,7 +520,7 @@
  • 3.4.1.29.1. gnome-keyring
  • 3.4.1.29.2. KDE Connect
  • 3.4.1.29.3. Mako
  • -
  • 3.4.1.29.4. SwayOSD
  • +
  • 3.4.1.29.4. SwayOSD
  • 3.4.1.29.5. yubikey-touch-detector
  • @@ -532,7 +545,7 @@ @@ -710,7 +723,7 @@ @@ -719,7 +732,7 @@

    -This file has 67098 words spanning 17669 lines and was last revised on 2025-06-13 03:31:20 +0200. +This file has 69596 words spanning 18782 lines and was last revised on 2025-06-16 00:41:03 +0200.

    @@ -772,7 +785,7 @@ This section defines my Emacs configuration. For a while, I considered to use ry

    -My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: 2025-06-13 03:31:20 +0200) +My emacs is built using the emacs-overlay nix flake, which builds a bleeding edge emacs on wayland (pgtk) with utilities like treesitter support. By executing the below source block, the current build setting can be updated at any time, and you can see my most up-to-date build options (last updated: 2025-06-16 00:41:03 +0200)

    @@ -1084,8 +1097,7 @@ In outputs = inputs@ [...], the inputs@ makes it so th lib.swarselsystems.forEachSystem (pkgs: import inputs.nix-topology { inherit pkgs; modules = [ - # Your own file to define global topology. Works in principle like a nixos module but uses different options. - # ./topology.nix + "${self}/topology" { inherit (self) nixosConfigurations; } ]; }); @@ -1530,8 +1542,7 @@ lib.swarselsystems.mkHalfHostConfigs (lib.swarselsystems.readHosts "android") "a lib.swarselsystems.forEachSystem (pkgs: import inputs.nix-topology { inherit pkgs; modules = [ - # Your own file to define global topology. Works in principle like a nixos module but uses different options. - # ./topology.nix + "${self}/topology" { inherit (self) nixosConfigurations; } ]; }); @@ -1808,7 +1819,6 @@ in ]; - node.secretsDir = ./secrets; swarselsystems = lib.recursiveUpdate { firewall = lib.mkForce true; @@ -1834,6 +1844,7 @@ in # home.stateVersion = lib.mkForce "23.05"; swarselsystems = lib.recursiveUpdate { + info = "Framework Laptop 16, 7940HS, RX7700S, 64GB RAM"; isLaptop = true; isNixos = true; isSecondaryGpu = true; @@ -2070,9 +2081,9 @@ in }; - node.secretsDir = ./secrets; swarselsystems = lib.recursiveUpdate { + info = "ASRock J4105-ITX, 32GB RAM"; isImpermanence = false; isSecureBoot = true; isCrypted = true; @@ -2325,7 +2336,6 @@ in system.stateVersion = "23.11"; - node.secretsDir = ./secrets; services = { nginx = { virtualHosts = { @@ -2335,7 +2345,7 @@ in acmeRoot = null; locations = { "/" = { - proxyPass = "http://localhost:8384/"; + proxyPass = "http://localhost:8384"; extraConfig = '' client_max_body_size 0; ''; @@ -2427,6 +2437,7 @@ in swarselsystems = lib.recursiveUpdate { + info = "VM.Standard.E2.1.Micro"; flakePath = "/root/.dotfiles"; isImpermanence = false; isSecureBoot = false; @@ -2450,6 +2461,421 @@ in +
    +
    3.1.3.2. Moonside (OCI)
    +
    +
    +
    +
    3.1.3.2.1. Main Configuration
    +
    +
    +
    { lib, config, primaryUser, ... }:
    +let
    +  inherit (config.repo.secrets.common) workHostName;
    +  inherit (config.repo.secrets.local.syncthing) dev1 dev2 dev3 loc1;
    +  sharedOptions = {
    +    isBtrfs = true;
    +    isLinux = true;
    +  };
    +in
    +{
    +  imports = [
    +    ./hardware-configuration.nix
    +    ./disk-config.nix
    +  ];
    +
    +  sops = {
    +    age.sshKeyPaths = lib.mkDefault [ "/etc/ssh/ssh_host_ed25519_key" ];
    +    defaultSopsFile = lib.mkForce "/home/swarsel/.dotfiles/secrets/moonside/secrets.yaml";
    +    secrets = {
    +      wireguard-private-key = { };
    +    };
    +  };
    +
    +  boot = {
    +    loader.systemd-boot.enable = true;
    +    tmp.cleanOnBoot = true;
    +  };
    +
    +  environment = {
    +    etc."issue".text = "\4";
    +
    +    persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [
    +      {
    +        directory = "/var/lib/syncthing";
    +        user = "syncthing";
    +        group = "syncthing";
    +        mode = "0700";
    +      }
    +    ];
    +  };
    +
    +  topology.self.interfaces.wg = {
    +    addresses = ["192.168.3.4"];
    +    renderer.hidePhysicalConnections = true;
    +    virtual = true;
    +    type = "wireguard";
    +  };
    +
    +  networking = {
    +    nftables.enable = lib.mkForce false;
    +    hostName = "moonside";
    +    enableIPv6 = false;
    +    domain = "subnet03291956.vcn03291956.oraclevcn.com";
    +    firewall = {
    +      allowedTCPPorts = [ 80 443 8384 ];
    +    };
    +    wireguard = {
    +      enable = true;
    +      interfaces = {
    +        home-vpn = {
    +          privateKeyFile = config.sops.secrets.wireguard-private-key.path;
    +          ips = [ "192.168.3.4/32" ];
    +          peers = [
    +            {
    +              publicKey = "NNGvakADslOTCmN9HJOW/7qiM+oJ3jAlSZGoShg4ZWw=";
    +              name = "moonside";
    +              persistentKeepalive = 25;
    +              endpoint = "${config.repo.secrets.common.ipv4}:51820";
    +              allowedIPs = [
    +                "192.168.3.0/24"
    +                "192.168.1.0/24"
    +              ];
    +            }
    +          ];
    +        };
    +      };
    +    };
    +  };
    +
    +  hardware = {
    +    enableAllFirmware = lib.mkForce false;
    +  };
    +
    +  system.stateVersion = "23.11";
    +
    +  services = {
    +    nginx = {
    +      virtualHosts = {
    +        "syncthing.swarsel.win" = {
    +          enableACME = true;
    +          forceSSL = true;
    +          acmeRoot = null;
    +          locations = {
    +            "/" = {
    +              proxyPass = "http://localhost:8384";
    +              extraConfig = ''
    +                client_max_body_size 0;
    +              '';
    +            };
    +          };
    +        };
    +      };
    +    };
    +
    +    syncthing = {
    +      enable = true;
    +      guiAddress = "0.0.0.0:8384";
    +      openDefaultPorts = true;
    +      relay.enable = false;
    +      settings = {
    +        urAccepted = -1;
    +        devices = {
    +          "magicant" = {
    +            id = "VMWGEE2-4HDS2QO-KNQOVGN-LXLX6LA-666E4EK-ZBRYRRO-XFEX6FB-6E3XLQO";
    +          };
    +          "winters" = {
    +            id = "O7RWDMD-AEAHPP7-7TAVLKZ-BSWNBTU-2VA44MS-EYGUNBB-SLHKB3C-ZSLMOAA";
    +          };
    +          "${workHostName}" = {
    +            id = "YAPV4BV-I26WPTN-SIP32MV-SQP5TBZ-3CHMTCI-Z3D6EP2-MNDQGLP-53FT3AB";
    +          };
    +          "${dev1}" = {
    +            id = "OCCDGDF-IPZ6HHQ-5SSLQ3L-MSSL5ZW-IX5JTAM-PW4PYEK-BRNMJ7E-Q7YDMA7";
    +          };
    +          "${dev2}" = {
    +            id = "LPCFIIB-ENUM2V6-F2BWVZ6-F2HXCL2-BSBZXUF-TIMNKYB-7CATP7H-YU5D3AH";
    +          };
    +          "${dev3}" = {
    +            id = "LAUT2ZP-KEZY35H-AHR3ARD-URAREJI-2B22P5T-PIMUNWW-PQRDETU-7KIGNQR";
    +          };
    +        };
    +        folders = {
    +          "Default Folder" = lib.mkForce {
    +            path = "/sync/Sync";
    +            type = "receiveonly";
    +            versioning = null;
    +            devices = [ "winters" "magicant" "${workHostName}" ];
    +            id = "default";
    +          };
    +          "Obsidian" = {
    +            path = "/sync/Obsidian";
    +            type = "receiveonly";
    +            versioning = {
    +              type = "simple";
    +              params.keep = "5";
    +            };
    +            devices = [ "winters" "magicant" "${workHostName}" ];
    +            id = "yjvni-9eaa7";
    +          };
    +          "Org" = {
    +            path = "/sync/Org";
    +            type = "receiveonly";
    +            versioning = {
    +              type = "simple";
    +              params.keep = "5";
    +            };
    +            devices = [ "winters" "magicant" "${workHostName}" ];
    +            id = "a7xnl-zjj3d";
    +          };
    +          "Vpn" = {
    +            path = "/sync/Vpn";
    +            type = "receiveonly";
    +            versioning = {
    +              type = "simple";
    +              params.keep = "5";
    +            };
    +            devices = [ "winters" "magicant" "${workHostName}" ];
    +            id = "hgp9s-fyq3p";
    +          };
    +          ".elfeed" = {
    +            path = "/sync/elfeed";
    +            type = "receiveonly";
    +            versioning = {
    +              type = "simple";
    +              params.keep = "5";
    +            };
    +            devices = [ "winters" ];
    +            id = "h7xbs-fs9v1";
    +          };
    +          "Documents" = {
    +            path = "/sync/Documents";
    +            type = "receiveonly";
    +            versioning = {
    +              type = "simple";
    +              params.keep = "2";
    +            };
    +            devices = [ "winters" ];
    +            id = "hgr3d-pfu3w";
    +          };
    +          "runandbun" = {
    +            path = "/sync/runandbun";
    +            type = "receiveonly";
    +            versioning = {
    +              type = "simple";
    +              params.keep = "5";
    +            };
    +            devices = [ "winters" "magicant" ];
    +            id = "kwnql-ev64v";
    +          };
    +          "${loc1}" = {
    +            path = "/sync/${loc1}";
    +            type = "receiveonly";
    +            versioning = {
    +              type = "simple";
    +              params.keep = "3";
    +            };
    +            devices = [ dev1 dev2 dev3 ];
    +            id = "5gsxv-rzzst";
    +          };
    +        };
    +      };
    +    };
    +  };
    +
    +  swarselsystems = lib.recursiveUpdate
    +    {
    +      info = "VM.Standard.A1.Flex, 4 OCPUs, 24GB RAM";
    +      flakePath = "/home/swarsel/.dotfiles";
    +      isImpermanence = true;
    +      isSecureBoot = false;
    +      isCrypted = false;
    +      isSwap = false;
    +      rootDisk = "/dev/sda";
    +      profiles = {
    +        server.moonside = true;
    +      };
    +    }
    +    sharedOptions;
    +
    +  home-manager.users."${primaryUser}" = {
    +    home.stateVersion = lib.mkForce "23.11";
    +    swarselsystems = lib.recursiveUpdate
    +      { }
    +      sharedOptions;
    +  };
    +
    +}
    +
    +
    +
    +
    +
    +
    +
    3.1.3.2.2. hardware-configuration
    +
    +

    +loader.grub = { + efiSupport = true; + efiInstallAsRemovable = true; + device = "nodev"; +}; +

    +
    +
    { lib, modulesPath, ... }:
    +{
    +  imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
    +
    +  boot = {
    +    initrd = {
    +      availableKernelModules = [ "xhci_pci" "virtio_pci" "virtio_scsi" "usbhid" ];
    +      kernelModules = [  ];
    +    };
    +    kernelModules = [ ];
    +    extraModulePackages = [ ];
    +  };
    +
    +  nixpkgs.hostPlatform = lib.mkForce "aarch64-linux";
    +}
    +
    +
    +
    +
    +
    +
    3.1.3.2.3. disko
    +
    +
    +
    # NOTE: ... is needed because dikso passes diskoFile
    +{ lib
    +, config
    +, rootDisk
    +, ...
    +}:
    +let
    +  type = "btrfs";
    +  extraArgs = [ "-L" "nixos" "-f" ]; # force overwrite
    +  subvolumes = {
    +    "/root" = {
    +      mountpoint = "/";
    +      mountOptions = [
    +        "subvol=root"
    +        "compress=zstd"
    +        "noatime"
    +      ];
    +    };
    +    "/home" = lib.mkIf config.swarselsystems.isImpermanence {
    +      mountpoint = "/home";
    +      mountOptions = [
    +        "subvol=home"
    +        "compress=zstd"
    +        "noatime"
    +      ];
    +    };
    +    "/persist" = lib.mkIf config.swarselsystems.isImpermanence {
    +      mountpoint = "/persist";
    +      mountOptions = [
    +        "subvol=persist"
    +        "compress=zstd"
    +        "noatime"
    +      ];
    +    };
    +    "/log" = lib.mkIf config.swarselsystems.isImpermanence {
    +      mountpoint = "/var/log";
    +      mountOptions = [
    +        "subvol=log"
    +        "compress=zstd"
    +        "noatime"
    +      ];
    +    };
    +    "/nix" = {
    +      mountpoint = "/nix";
    +      mountOptions = [
    +        "subvol=nix"
    +        "compress=zstd"
    +        "noatime"
    +      ];
    +    };
    +    "/swap" = lib.mkIf config.swarselsystems.isSwap {
    +      mountpoint = "/.swapvol";
    +      swap.swapfile.size = config.swarselsystems.swapSize;
    +    };
    +  };
    +in
    +{
    +  disko.devices = {
    +    disk = {
    +      disk0 = {
    +        type = "disk";
    +        device = config.swarselsystems.rootDisk;
    +        content = {
    +          type = "gpt";
    +          partitions = {
    +            ESP = {
    +              priority = 1;
    +              name = "ESP";
    +              size = "512M";
    +              type = "EF00";
    +              content = {
    +                type = "filesystem";
    +                format = "vfat";
    +                mountpoint = "/boot";
    +                mountOptions = [ "defaults" ];
    +              };
    +            };
    +            root = {
    +              size = "100%";
    +              content = {
    +                inherit type subvolumes extraArgs;
    +                postCreateHook = lib.mkIf config.swarselsystems.isImpermanence ''
    +                  MNTPOINT=$(mktemp -d)
    +                  mount "/dev/disk/by-label/nixos" "$MNTPOINT" -o subvolid=5
    +                  trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT
    +                  btrfs subvolume snapshot -r $MNTPOINT/root $MNTPOINT/root-blank
    +                '';
    +              };
    +            };
    +          };
    +        };
    +      };
    +      disk1 = {
    +        type = "disk";
    +        device = "/dev/sdb";
    +        content = {
    +          type = "gpt";
    +          partitions = {
    +            sync = {
    +              size = "100%";
    +              content = {
    +                type = "btrfs";
    +                extraArgs = [ "-L" "sync" "-f" ]; # force overwrite
    +                subvolumes = {
    +                  "/sync" = {
    +                    mountpoint = "/sync";
    +                    mountOptions = [
    +                      "subvol=root"
    +                      "compress=zstd"
    +                      "noatime"
    +                    ];
    +                  };
    +                };
    +              };
    +            };
    +          };
    +        };
    +      };
    +    };
    +  };
    +
    +  fileSystems."/persist".neededForBoot = lib.mkIf config.swarselsystems.isImpermanence true;
    +  fileSystems."/home".neededForBoot = lib.mkIf config.swarselsystems.isImpermanence true;
    +}
    +
    +
    +
    +
    +
    +
    +

    3.1.4. Utility hosts

    @@ -2525,14 +2951,16 @@ in swarselsystems = lib.recursiveUpdate { + info = "~SwarselSystems~ remote install helper"; wallpaper = self + /wallpaper/lenovowp.png; isImpermanence = true; - isCrypted = true; + isCrypted = false; isSecureBoot = false; - isSwap = true; + isSwap = false; swapSize = "8G"; # rootDisk = "/dev/nvme0n1"; - rootDisk = "/dev/vda"; + rootDisk = "/dev/sda"; + # rootDisk = "/dev/vda"; } sharedOptions; @@ -2735,6 +3163,7 @@ in "${self}/modules/iso/minimal.nix" "${self}/modules/nixos/common/sharedsetup.nix" + "${self}/modules/nixos/common/topology.nix" "${self}/modules/home/common/sharedsetup.nix" inputs.home-manager.nixosModules.home-manager @@ -2746,8 +3175,36 @@ in } ]; - home-manager.users."${primaryUser}" = { - home = { + options.node = { + name = lib.mkOption { + description = "Node Name."; + type = lib.types.str; + }; + secretsDir = lib.mkOption { + description = "Path to the secrets directory for this node."; + type = lib.types.path; + default = ./.; + }; + }; + config = { + node.name = lib.mkForce "drugstore"; + swarselsystems = { + info = "~SwarselSystems~ installer ISO"; + }; + home-manager.users."${primaryUser}" = { + home = { + stateVersion = "23.05"; + file = { + ".bash_history" = { + source = self + /programs/bash/.bash_history; + }; + }; + }; + swarselsystems = { + modules.general = lib.mkForce true; + }; + }; + home-manager.users.root.home = { stateVersion = "23.05"; file = { ".bash_history" = { @@ -2755,89 +3212,79 @@ in }; }; }; - swarselsystems.modules.general = lib.mkForce true; - }; - home-manager.users.root.home = { - stateVersion = "23.05"; - file = { - ".bash_history" = { - source = self + /programs/bash/.bash_history; - }; + + # environment.etc."issue".text = "\x1B[32m~SwarselSystems~\x1B[0m\nIP of primary interface: \x1B[31m\\4\x1B[0m\nThe Password for all users & root is '\x1B[31msetup\x1B[0m'.\nInstall the system remotely by running '\x1B[33mbootstrap -n <HOSTNAME> -d <IP_FROM_ABOVE> [--impermanence] [--encryption]\x1B[0m' on a machine with deployed secrets.\nAlternatively, run '\x1B[33mswarsel-install -d <DISK> -f <flake>\x1B[0m' for a local install.\n"; + environment.etc."issue".source = "${self}/programs/etc/issue"; + networking.dhcpcd.runHook = "${pkgs.utillinux}/bin/agetty --reload"; + + isoImage = { + makeEfiBootable = true; + makeUsbBootable = true; + squashfsCompression = "zstd -Xcompression-level 3"; }; - }; - # environment.etc."issue".text = "\x1B[32m~SwarselSystems~\x1B[0m\nIP of primary interface: \x1B[31m\\4\x1B[0m\nThe Password for all users & root is '\x1B[31msetup\x1B[0m'.\nInstall the system remotely by running '\x1B[33mbootstrap -n <HOSTNAME> -d <IP_FROM_ABOVE> [--impermanence] [--encryption]\x1B[0m' on a machine with deployed secrets.\nAlternatively, run '\x1B[33mswarsel-install -d <DISK> -f <flake>\x1B[0m' for a local install.\n"; - environment.etc."issue".source = "${self}/programs/etc/issue"; - networking.dhcpcd.runHook = "${pkgs.utillinux}/bin/agetty --reload"; + nixpkgs = { + hostPlatform = lib.mkDefault "x86_64-linux"; + config.allowUnfree = true; + }; - isoImage = { - makeEfiBootable = true; - makeUsbBootable = true; - squashfsCompression = "zstd -Xcompression-level 3"; - }; + services.getty.autologinUser = lib.mkForce primaryUser; - nixpkgs = { - hostPlatform = lib.mkDefault "x86_64-linux"; - config.allowUnfree = true; - }; - - services.getty.autologinUser = lib.mkForce primaryUser; - - users = { - allowNoPasswordLogin = true; - groups.swarsel = { }; users = { - swarsel = { - name = primaryUser; - group = primaryUser; - isNormalUser = true; - password = "setup"; # this is overwritten after install - openssh.authorizedKeys.keys = lib.lists.forEach pubKeys (key: builtins.readFile key); - extraGroups = [ "wheel" ]; - }; - root = { - # password = lib.mkForce config.users.users.swarsel.password; # this is overwritten after install - openssh.authorizedKeys.keys = config.users.users."${primaryUser}".openssh.authorizedKeys.keys; + allowNoPasswordLogin = true; + groups.swarsel = { }; + users = { + swarsel = { + name = primaryUser; + group = primaryUser; + isNormalUser = true; + password = "setup"; # this is overwritten after install + openssh.authorizedKeys.keys = lib.lists.forEach pubKeys (key: builtins.readFile key); + extraGroups = [ "wheel" ]; + }; + root = { + # password = lib.mkForce config.users.users.swarsel.password; # this is overwritten after install + openssh.authorizedKeys.keys = config.users.users."${primaryUser}".openssh.authorizedKeys.keys; + }; }; }; - }; - boot = { - loader.systemd-boot.enable = lib.mkForce true; - loader.efi.canTouchEfiVariables = true; - }; + boot = { + loader.systemd-boot.enable = lib.mkForce true; + loader.efi.canTouchEfiVariables = true; + }; - programs.bash.shellAliases = { - "swarsel-install" = "nix run github:Swarsel/.dotfiles#swarsel-install --"; - }; + programs.bash.shellAliases = { + "swarsel-install" = "nix run github:Swarsel/.dotfiles#swarsel-install --"; + }; - system.activationScripts.cache = { - text = '' - mkdir -p -m=0777 /home/${primaryUser}/.local/state/nix/profiles - mkdir -p -m=0777 /home/${primaryUser}/.local/state/home-manager/gcroots - mkdir -p -m=0777 /home/${primaryUser}/.local/share/nix/ - printf '{\"extra-substituters\":{\"https://nix-community.cachix.org\":true,\"https://nix-community.cachix.org https://cache.ngi0.nixos.org/\":true},\"extra-trusted-public-keys\":{\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=\":true,\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= cache.ngi0.nixos.org-1:KqH5CBLNSyX184S9BKZJo1LxrxJ9ltnY2uAs5c/f1MA=\":true}}' | tee /home/${primaryUser}/.local/share/nix/trusted-settings.json > /dev/null - mkdir -p /root/.local/share/nix/ - printf '{\"extra-substituters\":{\"https://nix-community.cachix.org\":true,\"https://nix-community.cachix.org https://cache.ngi0.nixos.org/\":true},\"extra-trusted-public-keys\":{\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=\":true,\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= cache.ngi0.nixos.org-1:KqH5CBLNSyX184S9BKZJo1LxrxJ9ltnY2uAs5c/f1MA=\":true}}' | tee /root/.local/share/nix/trusted-settings.json > /dev/null - ''; - }; - systemd = { - services.sshd.wantedBy = lib.mkForce [ "multi-user.target" ]; - targets = { - sleep.enable = false; - suspend.enable = false; - hibernate.enable = false; - hybrid-sleep.enable = false; + system.activationScripts.cache = { + text = '' + mkdir -p -m=0777 /home/${primaryUser}/.local/state/nix/profiles + mkdir -p -m=0777 /home/${primaryUser}/.local/state/home-manager/gcroots + mkdir -p -m=0777 /home/${primaryUser}/.local/share/nix/ + printf '{\"extra-substituters\":{\"https://nix-community.cachix.org\":true,\"https://nix-community.cachix.org https://cache.ngi0.nixos.org/\":true},\"extra-trusted-public-keys\":{\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=\":true,\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= cache.ngi0.nixos.org-1:KqH5CBLNSyX184S9BKZJo1LxrxJ9ltnY2uAs5c/f1MA=\":true}}' | tee /home/${primaryUser}/.local/share/nix/trusted-settings.json > /dev/null + mkdir -p /root/.local/share/nix/ + printf '{\"extra-substituters\":{\"https://nix-community.cachix.org\":true,\"https://nix-community.cachix.org https://cache.ngi0.nixos.org/\":true},\"extra-trusted-public-keys\":{\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=\":true,\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= cache.ngi0.nixos.org-1:KqH5CBLNSyX184S9BKZJo1LxrxJ9ltnY2uAs5c/f1MA=\":true}}' | tee /root/.local/share/nix/trusted-settings.json > /dev/null + ''; + }; + systemd = { + services.sshd.wantedBy = lib.mkForce [ "multi-user.target" ]; + targets = { + sleep.enable = false; + suspend.enable = false; + hibernate.enable = false; + hybrid-sleep.enable = false; + }; + }; + + system.stateVersion = lib.mkForce "23.05"; + + networking = { + hostName = "drugstore"; + wireless.enable = false; }; }; - - system.stateVersion = lib.mkForce "23.05"; - - networking = { - hostName = "drugstore"; - wireless.enable = false; - }; - } @@ -2908,8 +3355,8 @@ This is just a demo host. It applies all the configuration found in the common p I also set the WLR_RENDERER_ALLOW_SOFTWARE=1 to allow this configuration to run in a virtualized environment. I also enable qemuGuest for a smoother experience when testing on QEMU.

    -
    -
    3.1.4.4.1. Main configuration
    +
    +
    3.1.4.4.1. Main configuration
    { self, inputs, config, pkgs, lib, primaryUser, ... }:
    @@ -2959,9 +3406,9 @@ in
         firewall.enable = true;
       };
     
    -
       swarselsystems = lib.recursiveUpdate
         {
    +      info = "~SwarselSystems~ demo host";
           wallpaper = self + /wallpaper/lenovowp.png;
           initialSetup = true;
           isImpermanence = true;
    @@ -2988,8 +3435,8 @@ in
     
    -
    -
    3.1.4.4.2. NixOS dummy options configuration
    +
    +
    3.1.4.4.2. NixOS dummy options configuration
    _:
    @@ -2999,8 +3446,8 @@ in
     
    -
    -
    3.1.4.4.3. home-manager dummy options configuration
    +
    +
    3.1.4.4.3. home-manager dummy options configuration
    _:
    @@ -4002,7 +4449,7 @@ sops updatekeys --yes --enable-local-keyservice "${git_root}"/secrets/*/secrets.
     green "Making ssh_host_ed25519_key available to home-manager for user $target_user"
     sed -i "/$target_hostname/d; /$target_destination/d" ~/.ssh/known_hosts
     $scp_cmd root@"$target_destination":/etc/ssh/ssh_host_ed25519_key root@"$target_destination":/home/"$target_user"/.ssh/ssh_host_ed25519_key
    -$ssh_root_cmd "chown $target_user:users /home/$target_user/.ssh/ssh_host_ed25519_key"
    +$ssh_root_cmd "mkdir -p /home/$target_user/.ssh; chown $target_user:users /home/$target_user/.ssh/ssh_host_ed25519_key"
     # __________________________
     
     if yes_or_no "Add ssh host fingerprints for git upstream repositories? (This is needed for building the full config)"; then
    @@ -4781,8 +5228,8 @@ appimageTools.wrapType2 {
     
    -
    -
    3.2.1.30. swarsel-deploy
    +
    +
    3.2.1.30. swarsel-deploy
    # heavily inspired from https://github.com/oddlama/nix-config/blob/d42cbde676001a7ad8a3cace156e050933a4dcc3/pkgs/deploy.nix
    @@ -4913,8 +5360,8 @@ writeShellApplication {
     
    -
    -
    3.2.1.31. sshrm
    +
    +
    3.2.1.31. sshrm

    This programs simply runs ssh-keygen on the last host that I tried to ssh into. I need this frequently when working with cloud-init usually. @@ -5071,8 +5518,8 @@ in

    -
    -
    3.2.3.1.1. Personal
    +
    +
    3.2.3.1.1. Personal
    { lib, config, ... }:
    @@ -5142,8 +5589,8 @@ in
     
    -
    -
    3.2.3.1.2. Chaostheatre
    +
    +
    3.2.3.1.2. Chaostheatre
    { lib, config, ... }:
    @@ -5205,8 +5652,8 @@ in
     
    -
    -
    3.2.3.1.3. toto
    +
    +
    3.2.3.1.3. toto
    { lib, config, ... }:
    @@ -5238,8 +5685,8 @@ in
     
    -
    -
    3.2.3.1.4. Work
    +
    +
    3.2.3.1.4. Work
    { lib, config, ... }:
    @@ -5260,8 +5707,8 @@ in
     
    -
    -
    3.2.3.1.5. Framework
    +
    +
    3.2.3.1.5. Framework
    { lib, config, ... }:
    @@ -5282,8 +5729,8 @@ in
     
    -
    -
    3.2.3.1.6. AMD CPU
    +
    +
    3.2.3.1.6. AMD CPU
    { lib, config, ... }:
    @@ -5304,8 +5751,8 @@ in
     
    -
    -
    3.2.3.1.7. AMD GPU
    +
    +
    3.2.3.1.7. AMD GPU
    { lib, config, ... }:
    @@ -5326,8 +5773,8 @@ in
     
    -
    -
    3.2.3.1.8. Hibernation
    +
    +
    3.2.3.1.8. Hibernation
    { lib, config, ... }:
    @@ -5348,8 +5795,8 @@ in
     
    -
    -
    3.2.3.1.9. BTRFS
    +
    +
    3.2.3.1.9. BTRFS
    { lib, config, ... }:
    @@ -5370,8 +5817,8 @@ in
     
    -
    -
    3.2.3.1.10. Local Server
    +
    +
    3.2.3.1.10. Local Server
    { lib, config, ... }:
    @@ -5381,7 +5828,6 @@ in
         swarselsystems = {
           modules = {
             general = lib.mkDefault true;
    -        nix-ld = lib.mkDefault true;
             pii = lib.mkDefault true;
             home-manager = lib.mkDefault true;
             home-managerExtra = lib.mkDefault true;
    @@ -5413,6 +5859,7 @@ in
               jenkins = lib.mkDefault false;
               kanidm = lib.mkDefault true;
               firefly = lib.mkDefault true;
    +          koillection = lib.mkDefault true;
             };
           };
         };
    @@ -5424,8 +5871,8 @@ in
     
    -
    -
    3.2.3.1.11. OCI Sync Server
    +
    +
    3.2.3.1.11. OCI Sync Server
    { lib, config, ... }:
    @@ -5446,7 +5893,6 @@ in
               general = lib.mkDefault true;
               packages = lib.mkDefault true;
               sops = lib.mkDefault true;
    -          nfs = lib.mkDefault true;
               nginx = lib.mkDefault true;
               ssh = lib.mkDefault true;
               forgejo = lib.mkDefault true;
    @@ -5458,6 +5904,42 @@ in
     
     }
     
    +
    +
    +
    +
    +
    +
    3.2.3.1.12. Moonside
    +
    +
    +
    { lib, config, ... }:
    +{
    +  options.swarselsystems.profiles.server.moonside = lib.mkEnableOption "is this a moonside server";
    +  config = lib.mkIf config.swarselsystems.profiles.server.moonside {
    +    swarselsystems = {
    +      modules = {
    +        general = lib.mkDefault true;
    +        pii = lib.mkDefault true;
    +        home-manager = lib.mkDefault true;
    +        home-managerExtra = lib.mkDefault true;
    +        xserver = lib.mkDefault true;
    +        time = lib.mkDefault true;
    +        users = lib.mkDefault true;
    +        impermanence = lib.mkDefault true;
    +        server = {
    +          general = lib.mkDefault true;
    +          packages = lib.mkDefault true;
    +          sops = lib.mkDefault true;
    +          nginx = lib.mkDefault true;
    +          ssh = lib.mkDefault true;
    +          oauth2Proxy = lib.mkDefault true;
    +        };
    +      };
    +    };
    +  };
    +
    +}
    +
     
    @@ -5481,8 +5963,8 @@ in
    -
    -
    3.2.3.2.1. Personal
    +
    +
    3.2.3.2.1. Personal
    { lib, config, ... }:
    @@ -5539,8 +6021,8 @@ in
     
    -
    -
    3.2.3.2.2. Chaostheatre
    +
    +
    3.2.3.2.2. Chaostheatre
    { lib, config, ... }:
    @@ -5592,8 +6074,8 @@ in
     
    -
    -
    3.2.3.2.3. toto
    +
    +
    3.2.3.2.3. toto
    { lib, config, ... }:
    @@ -5613,8 +6095,8 @@ in
     
    -
    -
    3.2.3.2.4. Work
    +
    +
    3.2.3.2.4. Work
    { lib, config, ... }:
    @@ -5634,8 +6116,8 @@ in
     
    -
    -
    3.2.3.2.5. Framework
    +
    +
    3.2.3.2.5. Framework
    { lib, config, ... }:
    @@ -5656,8 +6138,8 @@ in
     
    -
    -
    3.2.3.2.6. Darwin
    +
    +
    3.2.3.2.6. Darwin
    { lib, config, ... }:
    @@ -5675,8 +6157,8 @@ in
     
    -
    -
    3.2.3.2.7. Local Server
    +
    +
    3.2.3.2.7. Local Server
    { lib, config, ... }:
    @@ -5751,6 +6233,7 @@ in
       pkgsFor = lib.genAttrs (import systems) (system:
         import inputs.nixpkgs {
           inherit system;
    +      overlays = [ self.overlays.default ];
           config.allowUnfree = true;
         }
       );
    @@ -5783,6 +6266,10 @@ in
           systemFunc {
             specialArgs = { inherit inputs outputs lib self; };
             modules = [
    +          {
    +            node.name = host;
    +            node.secretsDir = ../hosts/${type}/${host}/secrets;
    +          }
               # put inports here that are for all hosts
               inputs.disko.nixosModules.disko
               inputs.sops-nix.nixosModules.sops
    @@ -5794,7 +6281,9 @@ in
                 _module.args.primaryUser = linuxUser;
               }
             ] ++
    -        (if (host == "iso") then [ ] else
    +        (if (host == "iso") then [
    +          inputs.nix-topology.nixosModules.default
    +        ] else
             ([
               # put nixos imports here that are for all servers and normal hosts
               inputs.nix-topology.nixosModules.default
    @@ -5916,12 +6405,12 @@ in
     
    -
    -

    3.2.5. Auxiliary files

    +
    +

    3.2.5. Auxiliary files

    -
    -
    3.2.5.1. extra-builtins
    +
    +
    3.2.5.1. extra-builtins
    @@ -5956,8 +6445,8 @@ in
     
    -
    -
    3.2.5.2. sops-decrypt-and-cache
    +
    +
    3.2.5.2. sops-decrypt-and-cache
    #!/usr/bin/env bash
    @@ -6000,6 +6489,169 @@ if [[ $print_out_path == true ]]; then
     else
         cat "$out"
     fi
    +
    +
    +
    +
    +
    +
    3.2.5.3. nix-topology
    +
    +
    +
    { config, ... }:
    +let
    +  inherit (config.lib.topology)
    +    mkInternet
    +    mkDevice
    +    mkSwitch
    +    mkRouter
    +    mkConnection
    +    ;
    +in
    +{
    +  renderer = "elk";
    +
    +  networks = {
    +    home-lan = {
    +      name = "Home LAN";
    +      cidrv4 = "192.168.1.0/24";
    +    };
    +    wg = {
    +      name = "Wireguard Tunnel";
    +      cidrv4 = "192.168.3.0/24";
    +    };
    +  };
    +
    +  nodes = {
    +    internet = mkInternet {
    +      connections = [
    +        (mkConnection "moonside" "wan")
    +        (mkConnection "pfsense" "wan")
    +        (mkConnection "sync" "wan")
    +      ];
    +    };
    +
    +    sync.interfaces.wan = { };
    +    moonside.interfaces.wan = { };
    +
    +    pfsense = mkRouter "pfSense" {
    +      info = "HUNSN RM02";
    +      image = ../topology/images/hunsn.png;
    +      interfaceGroups = [
    +        [
    +          "eth2"
    +          "eth3"
    +          "eth4"
    +          "eth5"
    +          "eth6"
    +        ]
    +        [ "wan" ]
    +      ];
    +      interfaces.wg0 = {
    +        addresses = [ "192.168.3.1" ];
    +        network = "wg";
    +        virtual = true;
    +        type = "wireguard";
    +      };
    +
    +      connections = {
    +        eth2 = mkConnection "switch-livingroom" "eth1";
    +        eth4 = mkConnection "winters" "eth1";
    +        eth3 = mkConnection "switch-bedroom" "eth1";
    +        eth6 = mkConnection "wifi-ap" "eth1";
    +        wg = mkConnection "moonside" "wg";
    +      };
    +      interfaces = {
    +        eth2 = {
    +          addresses = [ "192.168.1.1" ];
    +          network = "home-lan";
    +        };
    +        eth3 = {
    +          addresses = [ "192.168.1.1" ];
    +          network = "home-lan";
    +        };
    +        eth4 = {
    +          addresses = [ "192.168.1.1" ];
    +          network = "home-lan";
    +        };
    +        eth6 = {
    +          addresses = [ "192.168.1.1" ];
    +          network = "home-lan";
    +        };
    +      };
    +    };
    +
    +    winters.interfaces."eth1" = { };
    +
    +    wifi-ap = mkSwitch "Wi-Fi AP" {
    +      info = "Huawei";
    +      image = ../topology/images/huawei.png;
    +      interfaceGroups = [
    +        [
    +          "eth1"
    +          "wifi"
    +        ]
    +      ];
    +    };
    +
    +    switch-livingroom = mkSwitch "Switch Livingroom" {
    +      info = "TL-SG108";
    +      image = ../topology/images/TL-SG108.png;
    +      interfaceGroups = [
    +        [
    +          "eth1"
    +          "eth2"
    +          "eth3"
    +          "eth4"
    +          "eth5"
    +          "eth6"
    +          "eth7"
    +          "eth8"
    +        ]
    +      ];
    +      connections = {
    +        eth2 = mkConnection "nswitch" "eth1";
    +        eth7 = mkConnection "pc" "eth1";
    +        eth8 = mkConnection "nbl-imba-2" "eth1";
    +      };
    +    };
    +
    +    nswitch = mkDevice "Nintendo Switch" {
    +      info = "Nintendo Switch";
    +      image = ../topology/images/nintendo-switch.png;
    +      interfaces.eth1 = { };
    +    };
    +
    +    pc = mkDevice "Windows Gaming Server" {
    +      info = "i7-4790k, GTX970, 32GB RAM";
    +      image = ../topology/images/pc.png;
    +      interfaces.eth1 = { };
    +    };
    +
    +    nbl-imba-2.interfaces.eth1 = { };
    +
    +    switch-bedroom = mkSwitch "Switch Bedroom" {
    +      info = "TL-SG1005D";
    +      image = ../topology/images/TL-SG1005D.png;
    +      interfaceGroups = [
    +        [
    +          "eth1"
    +          "eth2"
    +          "eth3"
    +          "eth4"
    +          "eth5"
    +        ]
    +      ];
    +      connections.eth2 = mkConnection "printer" "eth1";
    +    };
    +
    +    printer = mkDevice "Printer" {
    +      info = "DELL C2665dnf";
    +      image = ../topology/images/DELL-C2665dnf.png;
    +      interfaces.eth1 = { };
    +    };
    +  };
    +}
    +
     
    @@ -6226,8 +6878,94 @@ A breakdown of the flags being set:
    +
    +
    3.3.1.4. Share configuration between nodes
    +
    +
    +
    { config, lib, outputs, ... }:
    +let
    +  inherit (lib)
    +    attrNames
    +    concatMap
    +    concatStringsSep
    +    foldl'
    +    getAttrFromPath
    +    mkMerge
    +    mkOption
    +    mkOptionType
    +    optionals
    +    recursiveUpdate
    +    setAttrByPath
    +    types
    +    ;
    +
    +  nodeName = config.node.name;
    +  mkForwardedOption =
    +    path:
    +    mkOption {
    +      type = mkOptionType {
    +        name = "Same type that the receiving option `${concatStringsSep "." path}` normally accepts.";
    +        merge =
    +          _loc: defs:
    +          builtins.filter (x: builtins.isAttrs x -> ((x._type or "") != "__distributed_config_empty")) (
    +            map (x: x.value) defs
    +          );
    +      };
    +      default = {
    +        _type = "__distributed_config_empty";
    +      };
    +      description = ''
    +        Anything specified here will be forwarded to `${concatStringsSep "." path}`
    +        on the given node. Forwarding happens as-is to the raw values,
    +        so validity can only be checked on the receiving node.
    +      '';
    +    };
    +
    +  forwardedOptions = [
    +    [
    +      "services"
    +      "nginx"
    +      "upstreams"
    +    ]
    +    [
    +      "services"
    +      "nginx"
    +      "virtualHosts"
    +    ]
    +  ];
    +
    +  attrsForEachOption =
    +    f: foldl' (acc: path: recursiveUpdate acc (setAttrByPath path (f path))) { } forwardedOptions;
    +in
    +{
    +  options.nodes = mkOption {
    +    description = "Options forwarded to the given node.";
    +    default = { };
    +    type = types.attrsOf (
    +      types.submodule {
    +        options = attrsForEachOption mkForwardedOption;
    +      }
    +    );
    +  };
    +
    +  config =
    +    let
    +      getConfig =
    +        path: otherNode:
    +        let
    +          cfg = outputs.nixosConfigurations.${otherNode}.config.nodes.${nodeName} or null;
    +        in
    +        optionals (cfg != null) (getAttrFromPath path cfg);
    +      mergeConfigFromOthers = path: mkMerge (concatMap (getConfig path) (attrNames outputs.nixosConfigurations));
    +    in
    +    attrsForEachOption mergeConfigFromOthers;
    +}
    +
    +
    +
    +
    -
    3.3.1.4. System Packages
    +
    3.3.1.5. System Packages

    Mostly used to install some compilers and lsp's that I want to have available when not using a devShell flake. Most other packages should go in Installed packages. @@ -6308,6 +7046,8 @@ Mostly used to install some compilers and lsp's that I want to have available wh zls ansible-language-server + elk-to-svg + ]; nixpkgs.config.permittedInsecurePackages = [ @@ -6322,7 +7062,7 @@ Mostly used to install some compilers and lsp's that I want to have available wh

    -
    3.3.1.5. Setup home-manager base
    +
    3.3.1.6. Setup home-manager base

    We enable the use of home-manager as a NixoS module. A nice trick here is the extraSpecialArgs = inputs line, which enables the use of seflf in most parts of the configuration. This is useful to refer to the root of the flake (which is otherwise quite hard while maintaining flake purity). @@ -6354,7 +7094,7 @@ We enable the use of home-manager as a NixoS module. A nice trick h

    -
    3.3.1.6. Setup home-manager specialArgs
    +
    3.3.1.7. Setup home-manager specialArgs

    This sets up the nix-secrets extraSpeciaArgs. This should not be present on the chaostheatre configuration, which is why I split this section into its own file, which makes removal easier when setting that system up. @@ -6375,7 +7115,7 @@ This sets up the nix-secrets extraSpeciaArgs. This should not be pr

    -
    3.3.1.7. Setup login keymap
    +
    3.3.1.8. Setup login keymap

    Next, we setup the keymap in case we are not in a graphical session. At this point, I always resort to us/altgr-intl, as it is comfortable to use and I do not write too much German anyways. @@ -6399,7 +7139,7 @@ Next, we setup the keymap in case we are not in a graphical session. At this poi

    -
    3.3.1.8. User setup, Make users non-mutable
    +
    3.3.1.9. User setup, Make users non-mutable

    This ensures that all user-configuration happens here in the config file. @@ -6411,11 +7151,14 @@ For that reason, make sure that sops-nix is properly working before

    -
    { pkgs, config, lib, ... }:
    +
    { self, pkgs, config, lib, ... }:
    +let
    +  sopsFile = self + /secrets/general/secrets.yaml;
    +in
     {
       options.swarselsystems.modules.users = lib.mkEnableOption "user config";
       config = lib.mkIf config.swarselsystems.modules.users {
    -    sops.secrets.swarseluser = lib.mkIf (!config.swarselsystems.isPublic) { neededForUsers = true; };
    +    sops.secrets.swarseluser = lib.mkIf (!config.swarselsystems.isPublic) { inherit sopsFile; neededForUsers = true; };
     
         users = {
           mutableUsers = lib.mkIf (!config.swarselsystems.initialSetup) false;
    @@ -6435,7 +7178,7 @@ For that reason, make sure that sops-nix is properly working before
     
    -
    3.3.1.9. Environment setup
    +
    3.3.1.10. Environment setup

    Next, we will setup some environment variables that need to be set on the system-side. We apply some compatibility options for chromium apps on wayland, enable the wordlist and make metadata reading possible for my file explorer (nautilus). @@ -6465,7 +7208,7 @@ Next, we will setup some environment variables that need to be set on the system

    -
    3.3.1.10. Security
    +
    3.3.1.11. Security

    Needed for control over system-wide privileges etc. Also I make sure that the root user has access to SSH_AUTH_SOCK (without this, root will not be able to read my nix-secrets repository). @@ -6499,7 +7242,7 @@ Needed for control over system-wide privileges etc. Also I make sure that the ro

    -
    3.3.1.11. Reduce systemd timeouts
    +
    3.3.1.12. Reduce systemd timeouts

    There is a persistent bug over Linux kernels that makes the user wait 1m30s on system shutdown due to the reason a stop job is running for session 1 of user .... I do not want to wait that long and am confident no important data is lost by doing this. @@ -6522,7 +7265,7 @@ There is a persistent bug over Linux kernels that makes the user wait 1m30s on s

    -
    3.3.1.12. Hardware settings
    +
    3.3.1.13. Hardware settings

    Enable OpenGL, Sound, Bluetooth and various drivers. @@ -6582,7 +7325,7 @@ Enable OpenGL, Sound, Bluetooth and various drivers.

    -
    3.3.1.13. Pulseaudio
    +
    3.3.1.14. Pulseaudio

    This is only used on systems not running Pipewire. @@ -6606,7 +7349,7 @@ This is only used on systems not running Pipewire.

    -
    3.3.1.14. Pipewire
    +
    3.3.1.15. Pipewire

    Pipewire handles communication on Wayland. This enables several sound tools as well as screen sharing in combinaton with xdg-desktop-portal-wlr. @@ -6637,7 +7380,7 @@ Pipewire handles communication on Wayland. This enables several sound tools as w

    -
    3.3.1.15. Common network settings
    +
    3.3.1.16. Common network settings

    Here I only enable networkmanager and a few default networks. The rest of the network config is done separately in System specific configuration. @@ -6900,7 +7643,7 @@ Here I only enable networkmanager and a few default networks. The r

    -
    3.3.1.16. Time, locale settings
    +
    3.3.1.17. Time, locale settings

    Setup timezone and locale. I want to use the US layout, but have the rest adapted to my country and timezone. Also, there is an issue with running Windows/Linux dualboot on the same machine where the hardware clock desyncs between the two OS'es. We fix that bug here as well. @@ -6936,16 +7679,46 @@ Setup timezone and locale. I want to use the US layout, but have the rest adapte

    -
    -
    3.3.1.17. Meta options
    -
    +
    +
    3.3.1.18. Meta options
    +
    { lib, ... }:
     {
    -  options.node.secretsDir = lib.mkOption {
    -    description = "Path to the secrets directory for this node.";
    -    type = lib.types.path;
    -    default = ./.;
    +  options = {
    +    node = {
    +      secretsDir = lib.mkOption {
    +        description = "Path to the secrets directory for this node.";
    +        type = lib.types.path;
    +        default = ./.;
    +      };
    +      name = lib.mkOption {
    +        description = "Node Name.";
    +        type = lib.types.str;
    +      };
    +    };
    +  };
    +}
    +
    +
    +
    +
    +
    +
    3.3.1.19. Topology
    +
    +
    +
    { self, lib, config, ... }:
    +{
    +  options.swarselsystems.info = lib.mkOption {
    +    type = lib.types.str;
    +    default = "";
    +  };
    +  config.topology = {
    +    id = config.node.name;
    +    self = {
    +      hardware.info = config.swarselsystems.info;
    +      icon = lib.mkIf config.swarselsystems.isLaptop "devices.laptop";
    +    };
       };
     }
     
    @@ -6953,7 +7726,7 @@ Setup timezone and locale. I want to use the US layout, but have the rest adapte
    -
    3.3.1.18. sops
    +
    3.3.1.20. sops

    I use sops-nix to handle secrets that I want to have available on my machines at all times. Procedure to add a new machine: @@ -7025,9 +7798,9 @@ in

    -
    -
    3.3.1.19. PII management
    -
    +
    +
    3.3.1.21. PII management
    +
    { config, inputs, lib, ... }:
     let
    @@ -7107,7 +7880,7 @@ in
     
    -
    3.3.1.20. Theme (stylix)
    +
    3.3.1.22. Theme (stylix)

    By default, stylix wants to style GRUB as well. However, I think that looks horrible. @@ -7137,7 +7910,7 @@ By default, stylix wants to style

    -
    3.3.1.21. Programs (including zsh setup)
    +
    3.3.1.23. Programs (including zsh setup)

    Some programs profit from being installed through dedicated NixOS settings on system-level; these go here. Notably the zsh setup goes here and cannot be deleted under any circumstances. @@ -7159,7 +7932,7 @@ Some programs profit from being installed through dedicated NixOS settings on sy

    -
    3.3.1.21.1. zsh
    +
    3.3.1.23.1. zsh

    Here I disable global completion to prevent redundant compinit calls and cache invalidation that slow down shell startup (enabled on the home-manager side). @@ -7184,7 +7957,7 @@ Here I disable global completion to prevent redundant compinit calls and cache i

    -
    3.3.1.21.2. syncthing
    +
    3.3.1.23.2. syncthing
    { lib, config, ... }:
    @@ -7211,31 +7984,34 @@ in
               "winters" = {
                 id = "O7RWDMD-AEAHPP7-7TAVLKZ-BSWNBTU-2VA44MS-EYGUNBB-SLHKB3C-ZSLMOAA";
               };
    +          "moonside (@oracle)" = {
    +            id = "VPCDZB6-MGVGQZD-Q6DIZW3-IZJRJTO-TCC3QUQ-2BNTL7P-AKE7FBO-N55UNQE";
    +          };
             };
             folders = {
               "Default Folder" = lib.mkDefault {
                 path = "${homeDir}/Sync";
    -            devices = [ "sync (@oracle)" "magicant" "winters" ];
    +            devices = [ "sync (@oracle)" "magicant" "winters" "moonside (@oracle)" ];
                 id = "default";
               };
               "Obsidian" = {
                 path = "${homeDir}/Nextcloud/Obsidian";
    -            devices = [ "sync (@oracle)" "magicant" "winters" ];
    +            devices = [ "sync (@oracle)" "magicant" "winters" "moonside (@oracle)" ];
                 id = "yjvni-9eaa7";
               };
               "Org" = {
                 path = "${homeDir}/Nextcloud/Org";
    -            devices = [ "sync (@oracle)" "magicant" "winters" ];
    +            devices = [ "sync (@oracle)" "magicant" "winters" "moonside (@oracle)" ];
                 id = "a7xnl-zjj3d";
               };
               "Vpn" = {
                 path = "${homeDir}/Vpn";
    -            devices = [ "sync (@oracle)" "magicant" "winters" ];
    +            devices = [ "sync (@oracle)" "magicant" "winters" "moonside (@oracle)" ];
                 id = "hgp9s-fyq3p";
               };
               ".elfeed" = {
                 path = "${homeDir}/.elfeed";
    -            devices = [ "sync (@oracle)" "magicant" "winters" ];
    +            devices = [ "sync (@oracle)" "magicant" "winters" "moonside (@oracle)" ];
                 id = "h7xbs-fs9v1";
               };
             };
    @@ -7249,14 +8025,14 @@ in
     
    -
    3.3.1.22. Services
    +
    3.3.1.24. Services

    Setting up some hardware services as well as keyboard related settings. Here we make sure that we can use the CAPS key as a ESC/CTRL double key, which is a lifesaver.

    -
    3.3.1.22.1. blueman
    +
    3.3.1.24.1. blueman

    Enables the blueman service including the nice system tray icon. @@ -7276,7 +8052,7 @@ Enables the blueman service including the nice system tray icon.

    -
    3.3.1.22.2. Network devices
    +
    3.3.1.24.2. Network devices

    In this section we enable compatibility with several network devices I have at home, mainly printers and scanners. @@ -7327,7 +8103,7 @@ Avahi is the service used for the network discovery.

    -
    3.3.1.22.3. enable GVfs
    +
    3.3.1.24.3. enable GVfs

    This is being set to allow myself to use all functions of nautilus in NixOS @@ -7346,7 +8122,7 @@ This is being set to allow myself to use all functions of nautilus in NixOS

    -
    3.3.1.22.4. interception-tools: Make CAPS work as ESC/CTRL
    +
    3.3.1.24.4. interception-tools: Make CAPS work as ESC/CTRL

    This is a super-convenient package that lets my remap my CAPS key to ESC if pressed shortly, and CTRL if being held. @@ -7390,7 +8166,7 @@ This is a super-convenient package that lets my remap my CAPS key t

    -
    3.3.1.22.5. power-profiles-daemon
    +
    3.3.1.24.5. power-profiles-daemon

    This enables power profile management. The available modes are: @@ -7418,9 +8194,9 @@ Most of the time I am using power-saver, however, it is good to be

    -
    -
    3.3.1.22.6. SwayOSD
    -
    +
    +
    3.3.1.24.6. SwayOSD
    +
    { lib, pkgs, config, ... }:
     {
    @@ -7450,11 +8226,11 @@ Most of the time I am using power-saver, however, it is good to be
     
    -
    3.3.1.23. Hardware compatibility settings (Yubikey, Ledger, Keyboards) - udev rules
    +
    3.3.1.25. Hardware compatibility settings (Yubikey, Ledger, Keyboards) - udev rules
    -
    3.3.1.23.1. Yubikey
    +
    3.3.1.25.1. Yubikey

    This takes care of the main Yubikey related configuration on the NixOS side - note that the starting of the gpg-agent is done in the sway settings, to also perform this step of the setup for non NixOS-machines at the same time. @@ -7494,7 +8270,7 @@ Also, since I use a GPG key in sops, it seems that scdaemon creates an instance

    -
    3.3.1.23.2. Ledger
    +
    3.3.1.25.2. Ledger

    This performs the necessary configuration to support this hardware. @@ -7518,7 +8294,7 @@ This performs the necessary configuration to support this hardware.

    -
    3.3.1.23.3. Keyboards
    +
    3.3.1.25.3. Keyboards

    This loads some udev rules that I need for my split keyboards. @@ -7542,7 +8318,7 @@ This loads some udev rules that I need for my split keyboards.

    -
    3.3.1.24. System Login
    +
    3.3.1.26. System Login

    This section houses the greetd related settings. I do not really want to use a display manager, but it is useful to have setup in some ways - in my case for starting sway on system startup. Notably the default user login setting that is commented out here goes into the system specific settings, make sure to update it there @@ -7577,7 +8353,7 @@ This section houses the greetd related settings. I do not really want to use a d

    -
    3.3.1.25. nix-ld
    +
    3.3.1.27. nix-ld

    This provides libraries for binaries that are not patched for use on NixOS. This really makes the biggest gripe with NixOS go away, that being having to run a binary that is only found in a single spot. It is most of the times possible to patch such a file, but this makes such a situation take much less time to resolve. @@ -7710,7 +8486,7 @@ When a program does not work, start with nix-ldd <program>. T

    -
    3.3.1.26. Impermanence
    +
    3.3.1.28. Impermanence

    This is where the impermanence magic happens. When this is enabled, the root directory is rolled back to a blanket state on each reboot. @@ -7724,7 +8500,7 @@ Normally, doing that also resets the lecture that happens on the first use of { config, lib, ... }: let mapperTarget = lib.swarselsystems.mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos"; - inherit (config.swarselsystems) homeDir isImpermanence isCrypted; + inherit (config.swarselsystems) isImpermanence isCrypted; in { options.swarselsystems.modules.impermanence = lib.mkEnableOption "impermanence config"; @@ -7793,23 +8569,20 @@ in hideMounts = true; directories = [ - "/.cache/nix" - "/srv" - "/etc/nixos" "/etc/nix" "/etc/NetworkManager/system-connections" + "/var/lib/nixos" + { + directory = "/var/tmp/nix-import-encrypted"; # Decrypted repo-secrets can be kept + mode = "1777"; + } # "/etc/secureboot" - "${homeDir}/.dotfiles" - "/var/db/sudo" - "/var/cache" - "/var/lib" ]; files = [ "/etc/ssh/ssh_host_ed25519_key" "/etc/ssh/ssh_host_ed25519_key.pub" - "/etc/ssh/ssh_host_rsa_key" - "/etc/ssh/ssh_host_rsa_key.pub" + "/etc/machine-id" ]; }; }; @@ -7820,7 +8593,7 @@ in

    -
    3.3.1.27. Summary of nixos-rebuild diff
    +
    3.3.1.29. Summary of nixos-rebuild diff

    This snipped is added to the activation script that is run after every rebuild and shows what packages have been added and removed. This is actually not the optimal place to add that snipped, but the correct spot is in some perl file that I have not had the leisure to take a look at yet. @@ -7845,7 +8618,7 @@ This snipped is added to the activation script that is run after every rebuild a

    -
    3.3.1.28. gnome-keyring
    +
    3.3.1.30. gnome-keyring

    Used for storing sessions in e.g. Nextcloud. Using this on a system level keeps the login information when logging out of the session as well. @@ -7868,7 +8641,7 @@ Used for storing sessions in e.g. Nextcloud. Using this on a system level keeps

    -
    3.3.1.29. Sway
    +
    3.3.1.31. Sway

    This is used to better integrate Sway into the system on NixOS hosts. On the home-manager side, the package attribute will be null for such an host, using the systems derivation instead. @@ -7904,7 +8677,7 @@ This is used to better integrate Sway into the system on NixOS hosts. On the hom

    -
    3.3.1.30. xdg-portal
    +
    3.3.1.32. xdg-portal

    This allows me to use screen sharing on Wayland. The implementation is a bit crude and only the whole screen can be shared. However, most of the time that is all I need to do anyways. @@ -7936,7 +8709,7 @@ This allows me to use screen sharing on Wayland. The implementation is a bit cru

    -
    3.3.1.31. Podmam (distrobox)
    +
    3.3.1.33. Podmam (distrobox)

    I am using distrobox to quickly circumvent isses that I cannot immediately solve on NixOS. It is always the goal to quickly get things working on NixOS, but this prevents me from getting completely stuck. @@ -7964,7 +8737,7 @@ I am using distrobox to quickly circumvent isses that I cannot immediately solve

    -
    3.3.1.32. Appimage
    +
    3.3.1.34. Appimage

    Adds the necessary tools to allow .appimage programs easily. @@ -7987,7 +8760,7 @@ Adds the necessary tools to allow .appimage programs easily.

    -
    3.3.1.33. Handle lid switch correctly
    +
    3.3.1.35. Handle lid switch correctly

    This turns off the display when the lid is closed. @@ -8036,7 +8809,7 @@ This turns off the display when the lid is closed.

    -
    3.3.1.34. Low battery notification
    +
    3.3.1.36. Low battery notification

    Since I hide the waybar completely during normal operation, I run the risk of not noticing when my battery is about to run out. This module sends a notification when the battery level falls below 10%. Written by cafkafk. @@ -8077,7 +8850,7 @@ Since I hide the waybar completely during normal operation, I run the risk of no

    -
    3.3.1.35. Lanzaboote
    +
    3.3.1.37. Lanzaboote

    This dynamically uses systemd boot or Lanzaboote depending on `config.swarselsystems.initialSetup` and `config.swarselsystems.isSecureBoot`. @@ -8209,10 +8982,13 @@ in gnupg nix-index nvd + nix-output-monitor ssh-to-age git emacs vim + sops + swarsel-deploy ]; }; } @@ -8394,39 +9170,57 @@ Here I am forcing startWhenNeeded to false so that the value will n

    3.3.2.8. kavita
    -
    { pkgs, lib, config, ... }:
    +
    { self, lib, config, pkgs, ... }:
    +let
    +  serviceName = "kavita";
    +  serviceUser = "kavita";
    +  serviceDomain = "scroll.swarsel.win";
    +  servicePort = 8080;
    +in
     {
    -  options.swarselsystems.modules.server.kavita = lib.mkEnableOption "enable kavita on server";
    -  config = lib.mkIf config.swarselsystems.modules.server.kavita {
    +  options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
         environment.systemPackages = with pkgs; [
           calibre
         ];
     
    -
    -    users.users.kavita = {
    +    users.users."${serviceUser}" = {
           extraGroups = [ "users" ];
         };
     
    -    sops.secrets.kavita = { owner = "kavita"; };
    +    sops.secrets.kavita = { owner = serviceUser; };
     
         networking.firewall.allowedTCPPorts = [ 8080 ];
     
    +    topology.self.services.kavita = {
    +      name = "Kavita";
    +      info = "https://${serviceDomain}";
    +      icon = "${self}/topology/images/kavita.png";
    +    };
    +
         services.kavita = {
           enable = true;
    -      user = "kavita";
    -      settings.Port = 8080;
    +      user = serviceUser;
    +      settings.Port = servicePort;
           tokenKeyFile = config.sops.secrets.kavita.path;
         };
     
    -    services.nginx = {
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString servicePort}" = { };
    +          };
    +        };
    +      };
           virtualHosts = {
    -        "scroll.swarsel.win" = {
    +        "${serviceDomain}" = {
               enableACME = true;
               forceSSL = true;
               acmeRoot = null;
               locations = {
                 "/" = {
    -              proxyPass = "http://localhost:8080";
    +              proxyPass = "http://${serviceName}";
                   extraConfig = ''
                     client_max_body_size 0;
                   '';
    @@ -8446,10 +9240,16 @@ Here I am forcing startWhenNeeded to false so that the value will n
     
    { pkgs, lib, config, ... }:
    +let
    +  serviceDomain = "screen.swarsel.win";
    +  servicePort = 8096;
    +  serviceName = "jellyfin";
    +  serviceUser = "jellyfin";
    +in
     {
    -  options.swarselsystems.modules.server.jellyfin = lib.mkEnableOption "enable jellyfin on server";
    -  config = lib.mkIf config.swarselsystems.modules.server.jellyfin {
    -    users.users.jellyfin = {
    +  options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
    +    users.users."${serviceUser}" = {
           extraGroups = [ "video" "render" "users" ];
         };
         nixpkgs.config.packageOverrides = pkgs: {
    @@ -8464,21 +9264,31 @@ Here I am forcing startWhenNeeded to false so that the value will n
             libvdpau-va-gl
           ];
         };
    +
    +    topology.self.services.jellyfin.info = "https://${serviceDomain}";
    +
         services.jellyfin = {
           enable = true;
    -      user = "jellyfin";
    +      user = serviceUser;
           openFirewall = true; # this works only for the default ports
         };
     
    -    services.nginx = {
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString servicePort}" = { };
    +          };
    +        };
    +      };
           virtualHosts = {
    -        "screen.swarsel.win" = {
    +        "${serviceDomain}" = {
               enableACME = true;
               forceSSL = true;
               acmeRoot = null;
               locations = {
                 "/" = {
    -              proxyPass = "http://localhost:8096";
    +              proxyPass = "http://${serviceName}";
                   extraConfig = ''
                     client_max_body_size 0;
                   '';
    @@ -8499,9 +9309,16 @@ Here I am forcing startWhenNeeded to false so that the value will n
     
    { pkgs, config, lib, ... }:
    +let
    +  serviceDomain = "sound.swarsel.win";
    +  servicePort = 4040;
    +  serviceName = "navidrome";
    +  serviceUser = "navidrome";
    +  serviceGroup = serviceUser;
    +in
     {
    -  options.swarselsystems.modules.server.navidrome = lib.mkEnableOption "enable navidrome on server";
    -  config = lib.mkIf config.swarselsystems.modules.server.navidrome {
    +  options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
         environment.systemPackages = with pkgs; [
           pciutils
           alsa-utils
    @@ -8510,16 +9327,16 @@ Here I am forcing startWhenNeeded to false so that the value will n
     
         users = {
           groups = {
    -        navidrome = {
    +        "$(serviceGroup}" = {
               gid = 61593;
             };
           };
     
           users = {
    -        navidrome = {
    +        "${serviceUser}" = {
               isSystemUser = true;
               uid = 61593;
    -          group = "navidrome";
    +          group = serviceGroup;
               extraGroups = [ "audio" "utmp" "users" "pipewire" ];
             };
           };
    @@ -8537,8 +9354,8 @@ Here I am forcing startWhenNeeded to false so that the value will n
           openFirewall = true;
           settings = {
             LogLevel = "debug";
    -        Address = "127.0.0.1";
    -        Port = 4040;
    +        Address = "0.0.0.0";
    +        Port = servicePort;
             MusicFolder = "/Vault/Eternor/Music";
             PlaylistsPath = "./Playlists";
             EnableSharing = true;
    @@ -8570,15 +9387,22 @@ Here I am forcing startWhenNeeded to false so that the value will n
           };
         };
     
    -    services.nginx = {
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString servicePort}" = { };
    +          };
    +        };
    +      };
           virtualHosts = {
    -        "sound.swarsel.win" = {
    +        "${serviceDomain}" = {
               enableACME = true;
               forceSSL = true;
               acmeRoot = null;
               locations = {
                 "/" = {
    -              proxyPass = "http://localhost:4040";
    +              proxyPass = "http://navidrome";
                   proxyWebsockets = true;
                   extraConfig = ''
                     auth_request /oauth2/auth;
    @@ -8625,7 +9449,7 @@ Here I am forcing startWhenNeeded to false so that the value will n
                   '';
                 };
                 "/share" = {
    -              proxyPass = "http://localhost:4040";
    +              proxyPass = "http://navidrome";
                   proxyWebsockets = true;
                   extraConfig = ''
                     proxy_redirect          http:// https://;
    @@ -8639,7 +9463,7 @@ Here I am forcing startWhenNeeded to false so that the value will n
                   '';
                 };
                 "/rest" = {
    -              proxyPass = "http://localhost:4040";
    +              proxyPass = "http://navidrome";
                   proxyWebsockets = true;
                   extraConfig = ''
                     proxy_redirect          http:// https://;
    @@ -8711,7 +9535,7 @@ Here I am forcing startWhenNeeded to false so that the value will n
     
    3.3.2.12. mpd
    -
    { pkgs, lib, config, ... }:
    +
    { self, lib, config, pkgs, ... }:
     {
       options.swarselsystems.modules.server.mpd = lib.mkEnableOption "enable mpd on server";
       config = lib.mkIf config.swarselsystems.modules.server.mpd {
    @@ -8739,6 +9563,12 @@ Here I am forcing startWhenNeeded to false so that the value will n
           mpv
         ];
     
    +    topology.self.services.mpd = {
    +      name = "MPD";
    +      info = "http://localhost:3254";
    +      icon = "${self}/topology/images/mpd.png";
    +    };
    +
         services.mpd = {
           enable = true;
           musicDirectory = "/media";
    @@ -8802,6 +9632,13 @@ Here I am forcing startWhenNeeded to false so that the value will n
     
    { config, lib, pkgs, sops, ... }:
     let
       matrixDomain = "swatrix.swarsel.win";
    +  serviceName = "matrix";
    +  synapsePort = 8008;
    +  synapseUser = "matrix-synapse";
    +  whatsappPort = 29318;
    +  telegramPort = 29317;
    +  signalPort = 29328;
    +
       baseUrl = "https://${matrixDomain}";
       clientConfig."m.homeserver".base_url = baseUrl;
       serverConfig."m.server" = "${matrixDomain}:443";
    @@ -8812,8 +9649,8 @@ let
       '';
     in
     {
    -  options.swarselsystems.modules.server.matrix = lib.mkEnableOption "enable matrix on server";
    -  config = lib.mkIf config.swarselsystems.modules.server.matrix {
    +  options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
         environment.systemPackages = with pkgs; [
           matrix-synapse
           lottieconverter
    @@ -8822,24 +9659,24 @@ in
     
         sops = {
           secrets = {
    -        matrixsharedsecret = { owner = "matrix-synapse"; };
    -        mautrixtelegram_as = { owner = "matrix-synapse"; };
    -        mautrixtelegram_hs = { owner = "matrix-synapse"; };
    -        mautrixtelegram_api_id = { owner = "matrix-synapse"; };
    -        mautrixtelegram_api_hash = { owner = "matrix-synapse"; };
    +        matrixsharedsecret = { owner = synapseUser; };
    +        mautrixtelegram_as = { owner = synapseUser; };
    +        mautrixtelegram_hs = { owner = synapseUser; };
    +        mautrixtelegram_api_id = { owner = synapseUser; };
    +        mautrixtelegram_api_hash = { owner = synapseUser; };
           };
           templates = {
             "matrix_user_register.sh".content = ''
    -          register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008
    +          register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:${builtins.toString synapsePort}
             '';
             matrixshared = {
    -          owner = "matrix-synapse";
    +          owner = synapseUser;
               content = ''
                 registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret}
               '';
             };
             mautrixtelegram = {
    -          owner = "matrix-synapse";
    +          owner = synapseUser;
               content = ''
                 MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as}
                 MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs}
    @@ -8850,6 +9687,8 @@ in
           };
         };
     
    +    networking.firewall.allowedTCPPorts = [ 8008 8448 ];
    +
         systemd = {
           timers."restart-bridges" = {
             wantedBy = [ "timers.target" ];
    @@ -8919,9 +9758,9 @@ in
               public_baseurl = "https://${matrixDomain}";
               listeners = [
                 {
    -              port = 8008;
    +              port = synapsePort;
                   bind_addresses = [
    -                "127.0.0.1"
    +                "0.0.0.0"
                     # "::1"
                   ];
                   type = "http";
    @@ -8947,13 +9786,13 @@ in
             registerToSynapse = false;
             settings = {
               homeserver = {
    -            address = "http://localhost:8008";
    +            address = "http://localhost:${builtins.toString synapsePort}";
                 domain = matrixDomain;
               };
               appservice = {
    -            address = "http://localhost:29317";
    -            hostname = "localhost";
    -            port = "29317";
    +            address = "http://localhost:${builtins.toString telegramPort}";
    +            hostname = "0.0.0.0";
    +            port = telegramPort;
                 provisioning.enabled = true;
                 id = "telegram";
                 # ephemeral_events = true; # not needed due to double puppeting
    @@ -8993,13 +9832,13 @@ in
             registerToSynapse = false;
             settings = {
               homeserver = {
    -            address = "http://localhost:8008";
    +            address = "http://localhost:${builtins.toString synapsePort}";
                 domain = matrixDomain;
               };
               appservice = {
    -            address = "http://localhost:29318";
    -            hostname = "127.0.0.1";
    -            port = 29318;
    +            address = "http://localhost:${builtins.toString whatsappPort}";
    +            hostname = "0.0.0.0";
    +            port = whatsappPort;
                 database = {
                   type = "postgres";
                   uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql";
    @@ -9040,14 +9879,13 @@ in
             registerToSynapse = false;
             settings = {
               homeserver = {
    -            address = "http://localhost:8008";
    +            address = "http://localhost:${builtins.toString synapsePort}";
                 domain = matrixDomain;
               };
               appservice = {
    -
    -            address = "http://localhost:29328";
    -            hostname = "127.0.0.1";
    -            port = 29328;
    +            address = "http://localhost:${builtins.toString signalPort}";
    +            hostname = "0.0.0.0";
    +            port = signalPort;
                 database = {
                   type = "postgres";
                   uri = "postgresql:///mautrix-signal?host=/run/postgresql";
    @@ -9066,65 +9904,68 @@ in
               };
             };
           };
    +    };
     
    -      # restart the bridges daily. this is done for the signal bridge mainly which stops carrying
    -      # messages out after a while.
    +    # restart the bridges daily. this is done for the signal bridge mainly which stops carrying
    +    # messages out after a while.
     
     
    -      nginx = {
    -        virtualHosts = {
    -          "swatrix.swarsel.win" = {
    -            enableACME = true;
    -            forceSSL = true;
    -            acmeRoot = null;
    -            listen = [
    -              {
    -                addr = "0.0.0.0";
    -                port = 8448;
    -                ssl = true;
    -                extraParameters = [
    -                  "default_server"
    -                ];
    -              }
    -              {
    -                addr = "[::0]";
    -                port = 8448;
    -                ssl = true;
    -                extraParameters = [
    -                  "default_server"
    -                ];
    -              }
    -              {
    -                addr = "0.0.0.0";
    -                port = 443;
    -                ssl = true;
    -              }
    -              {
    -                addr = "[::0]";
    -                port = 443;
    -                ssl = true;
    -              }
    -            ];
    -            locations = {
    -              "~ ^(/_matrix|/_synapse/client)" = {
    -                # proxyPass = "http://localhost:8008";
    -                proxyPass = "http://localhost:8008";
    -                extraConfig = ''
    -                  client_max_body_size 0;
    -                '';
    -              };
    -              "= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
    -              "= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig;
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString synapsePort}" = { };
    +          };
    +        };
    +      };
    +      virtualHosts = {
    +        "${matrixDomain}" = {
    +          enableACME = true;
    +          forceSSL = true;
    +          acmeRoot = null;
    +          listen = [
    +            {
    +              addr = "0.0.0.0";
    +              port = 8448;
    +              ssl = true;
    +              extraParameters = [
    +                "default_server"
    +              ];
    +            }
    +            {
    +              addr = "[::0]";
    +              port = 8448;
    +              ssl = true;
    +              extraParameters = [
    +                "default_server"
    +              ];
    +            }
    +            {
    +              addr = "0.0.0.0";
    +              port = 443;
    +              ssl = true;
    +            }
    +            {
    +              addr = "[::0]";
    +              port = 443;
    +              ssl = true;
    +            }
    +          ];
    +          locations = {
    +            "~ ^(/_matrix|/_synapse/client)" = {
    +              proxyPass = "http://${serviceName}";
    +              extraConfig = ''
    +                client_max_body_size 0;
    +              '';
                 };
    +            "= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
    +            "= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig;
               };
             };
           };
         };
       };
    -
    -
     }
    -
     
    @@ -9135,21 +9976,24 @@ in
    { pkgs, lib, config, ... }:
     let
    -  nextcloudDomain = "stash.swarsel.win";
    +  serviceDomain = "stash.swarsel.win";
    +  serviceUser = "nextcloud";
    +  serviceGroup = serviceUser;
    +  serviceName = "nextcloud";
     in
     {
    -  options.swarselsystems.modules.server.nextcloud = lib.mkEnableOption "enable nextcloud on server";
    -  config = lib.mkIf config.swarselsystems.modules.server.nextcloud {
    +  options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
     
         sops.secrets = {
           nextcloudadminpass = {
    -        owner = "nextcloud";
    -        group = "nextcloud";
    +        owner = serviceUser;
    +        group = serviceGroup;
             mode = "0440";
           };
           kanidm-nextcloud-client = {
    -        owner = "nextcloud";
    -        group = "nextcloud";
    +        owner = serviceUser;
    +        group = serviceGroup;
             mode = "0440";
           };
         };
    @@ -9157,8 +10001,12 @@ in
         services = {
           nextcloud = {
             enable = true;
    +        settings = {
    +          trusted_proxies = [ "0.0.0.0" ];
    +          overwriteprotocol = "https";
    +        };
             package = pkgs.nextcloud31;
    -        hostName = nextcloudDomain;
    +        hostName = serviceDomain;
             home = "/Vault/apps/nextcloud";
             datadir = "/Vault/data/nextcloud";
             https = true;
    @@ -9174,21 +10022,30 @@ in
               dbtype = "sqlite";
             };
           };
    +    };
     
    -      nginx = {
    -        virtualHosts = {
    -          "${nextcloudDomain}" = {
    -            enableACME = true;
    -            forceSSL = true;
    -            acmeRoot = null;
    -            # config is automatically added by nixos nextcloud config.
    -            # hence, only provide certificate
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:80" = { };
    +          };
    +        };
    +      };
    +      virtualHosts = {
    +        "${serviceDomain}" = {
    +          enableACME = true;
    +          forceSSL = true;
    +          acmeRoot = null;
    +          locations = {
    +            "/" = {
    +              proxyPass = "http://${serviceName}";
    +            };
               };
             };
           };
         };
       };
    -
     }
     
    @@ -9199,19 +10056,26 @@ in
    { lib, config, ... }:
    +let
    +  serviceDomain = "shots.swarsel.win";
    +  servicePort = 3001;
    +  serviceUser = "immich";
    +  serviceName = "immich";
    +in
     {
    -  options.swarselsystems.modules.server.immich = lib.mkEnableOption "enable immich on server";
    -  config = lib.mkIf config.swarselsystems.modules.server.immich {
    +  options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
     
    -    users.users.immich = {
    +    users.users."${serviceUser}" = {
           extraGroups = [ "video" "render" "users" ];
         };
     
    -    # sops.secrets.nextcloudadminpass = { owner = "nextcloud"; };
    +    topology.self.services.immich.info = "https://${serviceDomain}";
     
         services.immich = {
           enable = true;
    -      port = 3001;
    +      host = "0.0.0.0";
    +      port = servicePort;
           openFirewall = true;
           mediaLocation = "/Vault/Eternor/Immich";
           environment = {
    @@ -9219,16 +10083,24 @@ in
           };
         };
     
    +    networking.firewall.allowedTCPPorts = [ 3001 ];
     
    -    services.nginx = {
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString servicePort}" = { };
    +          };
    +        };
    +      };
           virtualHosts = {
    -        "shots.swarsel.win" = {
    +        "${serviceDomain}" = {
               enableACME = true;
               forceSSL = true;
               acmeRoot = null;
               locations = {
                 "/" = {
    -              proxyPass = "http://localhost:3001";
    +              proxyPass = "http://${serviceName}";
                   extraConfig = ''
                     client_max_body_size    0;
     
    @@ -9267,32 +10139,41 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of 
     
     
    { lib, pkgs, config, ... }:
    +let
    +  serviceDomain = "scan.swarsel.win";
    +  servicePort = 28981;
    +  serviceUser = "paperless";
    +  serviceGroup = serviceUser;
    +  serviceName = "paperless";
    +in
     {
    -  options.swarselsystems.modules.server.paperless = lib.mkEnableOption "enable paperless on server";
    -  config = lib.mkIf config.swarselsystems.modules.server.paperless {
    +  options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
     
    -    users.users.paperless = {
    +    users.users."${serviceUser}" = {
           extraGroups = [ "users" ];
         };
     
         sops.secrets = {
    -      paperless_admin = { owner = "paperless"; };
    +      paperless_admin = { owner = serviceUser; };
           kanidm-paperless-client = {
    -        owner = "paperless";
    -        group = "paperless";
    +        owner = serviceUser;
    +        group = serviceGroup;
             mode = "0440";
           };
         };
     
    +    networking.firewall.allowedTCPPorts = [ servicePort ];
    +
         services = {
           paperless = {
             enable = true;
             mediaDir = "/Vault/Eternor/Paperless";
             dataDir = "/Vault/data/paperless";
    -        user = "paperless";
    -        port = 28981;
    +        user = serviceUser;
    +        port = servicePort;
             passwordFile = config.sops.secrets.paperless_admin.path;
    -        address = "127.0.0.1";
    +        address = "0.0.0.0";
             settings = {
               PAPERLESS_OCR_LANGUAGE = "deu+eng";
               PAPERLESS_URL = "https://scan.swarsel.win";
    @@ -9352,15 +10233,22 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of 
                          )
         '';
     
    -    services.nginx = {
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString servicePort}" = { };
    +          };
    +        };
    +      };
           virtualHosts = {
    -        "scan.swarsel.win" = {
    +        "${serviceDomain}" = {
               enableACME = true;
               forceSSL = true;
               acmeRoot = null;
               locations = {
                 "/" = {
    -              proxyPass = "http://localhost:28981";
    +              proxyPass = "http://${serviceName}";
                   extraConfig = ''
                     client_max_body_size    0;
                     proxy_connect_timeout   300;
    @@ -9384,7 +10272,10 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of 
     
    3.3.2.18. transmission
    -
    { pkgs, lib, config, ... }:
    +
    { self, pkgs, lib, config, ... }:
    +let
    +  serviceDomain = "store.swarsel.win";
    +in
     {
       options.swarselsystems.modules.server.transmission = lib.mkEnableOption "enable transmission and friends on server";
       config = lib.mkIf config.swarselsystems.modules.server.transmission {
    @@ -9441,6 +10332,18 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of 
           docker
         ];
     
    +    topology.self.services = {
    +      radarr.info = "https://${serviceDomain}/radarr";
    +      readarr = {
    +        name = "Readarr";
    +        info = "https://${serviceDomain}/readarr";
    +        icon = "${self}/topology/images/readarr.png";
    +      };
    +      sonarr.info = "https://${serviceDomain}/sonarr";
    +      lidarr.info = "https://${serviceDomain}/lidarr";
    +      prowlarr.info = "https://${serviceDomain}/prowlarr";
    +    };
    +
         services = {
           radarr = {
             enable = true;
    @@ -9529,26 +10432,34 @@ Also I install Tika and Gotenberg, which are needed to create PDFs out of 
     
    { lib, config, ... }:
     let
       inherit (config.repo.secrets.common) workHostName;
    +  serviceDomain = "storync.swarsel.win";
    +  servicePort = 8384;
    +  serviceUser = "syncthing";
    +  serviceGroup = serviceUser;
    +  serviceName = "syncthing";
     in
     {
    -  options.swarselsystems.modules.server.syncthing = lib.mkEnableOption "enable syncthing on server";
    -  config = lib.mkIf config.swarselsystems.modules.server.syncthing {
    +  options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
     
    -    users.users.syncthing = {
    +    users.users."${serviceUser}" = {
           extraGroups = [ "users" ];
    -      group = "syncthing";
    +      group = serviceGroup;
           isSystemUser = true;
         };
     
    -    users.groups.syncthing = { };
    +    users.groups."${serviceGroup}" = { };
    +
    +    networking.firewall.allowedTCPPorts = [ servicePort ];
     
         services.syncthing = {
           enable = true;
    -      user = "swarsel";
    +      user = serviceUser;
    +      group = serviceGroup;
           dataDir = "/Vault/data/syncthing";
           configDir = "/Vault/apps/syncthing";
    -      guiAddress = "0.0.0.0:8384";
    -      openDefaultPorts = true;
    +      guiAddress = "0.0.0.0:${builtins.toString servicePort}";
    +      openDefaultPorts = true; # opens ports TCP/UDP 22000 and UDP 21027 for discovery
           relay.enable = false;
           settings = {
             urAccepted = -1;
    @@ -9562,13 +10473,16 @@ in
               "${workHostName}" = {
                 id = "YAPV4BV-I26WPTN-SIP32MV-SQP5TBZ-3CHMTCI-Z3D6EP2-MNDQGLP-53FT3AB";
               };
    +          "moonside (@oracle)" = {
    +            id = "VPCDZB6-MGVGQZD-Q6DIZW3-IZJRJTO-TCC3QUQ-2BNTL7P-AKE7FBO-N55UNQE";
    +          };
             };
             folders = {
               "Default Folder" = lib.mkForce {
                 path = "/Vault/data/syncthing/Sync";
                 type = "receiveonly";
                 versioning = null;
    -            devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
    +            devices = [ "sync (@oracle)" "magicant" "${workHostName}" "moonside (@oracle)" ];
                 id = "default";
               };
               "Obsidian" = {
    @@ -9578,7 +10492,7 @@ in
                   type = "simple";
                   params.keep = "5";
                 };
    -            devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
    +            devices = [ "sync (@oracle)" "magicant" "${workHostName}" "moonside (@oracle)" ];
                 id = "yjvni-9eaa7";
               };
               "Org" = {
    @@ -9588,7 +10502,7 @@ in
                   type = "simple";
                   params.keep = "5";
                 };
    -            devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
    +            devices = [ "sync (@oracle)" "magicant" "${workHostName}" "moonside (@oracle)" ];
                 id = "a7xnl-zjj3d";
               };
               "Vpn" = {
    @@ -9598,7 +10512,7 @@ in
                   type = "simple";
                   params.keep = "5";
                 };
    -            devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
    +            devices = [ "sync (@oracle)" "magicant" "${workHostName}" "moonside (@oracle)" ];
                 id = "hgp9s-fyq3p";
               };
               "Documents" = {
    @@ -9608,27 +10522,29 @@ in
                   type = "simple";
                   params.keep = "5";
                 };
    -            devices = [ "magicant" "${workHostName}" ];
    +            devices = [ "magicant" "${workHostName}" "moonside (@oracle)" ];
                 id = "hgr3d-pfu3w";
               };
    -          # ".elfeed" = {
    -          #   path = "/Vault/data/syncthing/.elfeed";
    -          #   devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
    -          #   id = "h7xbs-fs9v1";
    -          # };
             };
           };
         };
     
    -    services.nginx = {
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString servicePort}" = { };
    +          };
    +        };
    +      };
           virtualHosts = {
    -        "storync.swarsel.win" = {
    +        "${serviceDomain}" = {
               enableACME = true;
               forceSSL = true;
               acmeRoot = null;
               locations = {
                 "/" = {
    -              proxyPass = "http://localhost:8384";
    +              proxyPass = "http://${serviceName}";
                   extraConfig = ''
                     client_max_body_size 0;
                   '';
    @@ -9638,7 +10554,6 @@ in
           };
         };
       };
    -
     }
     
    @@ -9721,24 +10636,24 @@ This section exposes several metrics that I use to check the health of my server
    { self, lib, config, ... }:
     let
    -  grafanaDomain = "status.swarsel.win";
    +  serviceDomain = "status.swarsel.win";
    +  servicePort = 3000;
    +  serviceUser = "grafana";
    +  serviceGroup = serviceUser;
    +  moduleName = "monitoring";
    +  grafanaUpstream = "grafana";
    +  prometheusUpstream = "prometheus";
    +  prometheusPort = 9090;
    +  prometheusWebRoot = "prometheus";
     in
     {
    -  options.swarselsystems.modules.server.monitoring = lib.mkEnableOption "enable monitoring on server";
    -  config = lib.mkIf config.swarselsystems.modules.server.monitoring {
    +  options.swarselsystems.modules.server."${moduleName}" = lib.mkEnableOption "enable ${moduleName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${moduleName}" {
     
         sops.secrets = {
    -      grafanaadminpass = {
    -        owner = "grafana";
    -      };
    -      prometheusadminpass = {
    -        owner = "grafana";
    -      };
    -      kanidm-grafana-client = {
    -        owner = "grafana";
    -        group = "grafana";
    -        mode = "0440";
    -      };
    +      grafanaadminpass = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
    +      prometheusadminpass = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
    +      kanidm-grafana-client = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
         };
     
         users = {
    @@ -9747,12 +10662,16 @@ in
               extraGroups = [ "nextcloud" ];
             };
     
    -        grafana = {
    +        "${serviceUser}" = {
               extraGroups = [ "users" ];
             };
           };
         };
     
    +    networking.firewall.allowedTCPPorts = [ servicePort prometheusPort ];
    +
    +    topology.self.services.prometheus.info = "https://${serviceDomain}/${prometheusWebRoot}";
    +
         services = {
           grafana = {
             enable = true;
    @@ -9764,7 +10683,7 @@ in
                   {
                     name = "prometheus";
                     type = "prometheus";
    -                url = "https://${grafanaDomain}/prometheus";
    +                url = "https://${serviceDomain}/prometheus";
                     editable = false;
                     access = "proxy";
                     basicAuth = true;
    @@ -9787,13 +10706,21 @@ in
             };
     
             settings = {
    -          security.admin_password = "$__file{/run/secrets/grafanaadminpass}";
    +          analytics.reporting_enabled = false;
    +          users.allow_sign_up = false;
    +          security = {
    +            admin_password = "$__file{/run/secrets/grafanaadminpass}";
    +            cookie_secure = true;
    +            disable_gravatar = true;
    +          };
               server = {
    -            domain = grafanaDomain;
    -            root_url = "https://${grafanaDomain}";
    -            http_port = 3000;
    +            domain = serviceDomain;
    +            root_url = "https://${serviceDomain}";
    +            http_port = servicePort;
                 http_addr = "0.0.0.0";
                 protocol = "http";
    +            enforce_domain = true;
    +            enable_gzip = true;
               };
               "auth.generic_oauth" = {
                 enabled = true;
    @@ -9819,9 +10746,9 @@ in
     
           prometheus = {
             enable = true;
    -        webExternalUrl = "https://status.swarsel.win/prometheus";
    -        port = 9090;
    -        listenAddress = "127.0.0.1";
    +        webExternalUrl = "https://status.swarsel.win/${prometheusWebRoot}";
    +        port = prometheusPort;
    +        listenAddress = "0.0.0.0";
             globalConfig = {
               scrape_interval = "10s";
             };
    @@ -9885,35 +10812,46 @@ in
               };
             };
           };
    +    };
     
     
    -      nginx = {
    -        virtualHosts = {
    -          "${grafanaDomain}" = {
    -            enableACME = true;
    -            forceSSL = true;
    -            acmeRoot = null;
    -            locations = {
    -              "/" = {
    -                proxyPass = "http://localhost:3000";
    -                proxyWebsockets = true;
    -                extraConfig = ''
    -                  client_max_body_size 0;
    -                '';
    -              };
    -              "/prometheus" = {
    -                proxyPass = "http://localhost:9090";
    -                extraConfig = ''
    -                  client_max_body_size 0;
    -                '';
    -              };
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${grafanaUpstream}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString servicePort}" = { };
    +          };
    +        };
    +        "${prometheusUpstream}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString prometheusPort}" = { };
    +          };
    +        };
    +      };
    +      virtualHosts = {
    +        "${serviceDomain}" = {
    +          enableACME = true;
    +          forceSSL = true;
    +          acmeRoot = null;
    +          locations = {
    +            "/" = {
    +              proxyPass = "http://${grafanaUpstream}";
    +              proxyWebsockets = true;
    +              extraConfig = ''
    +                client_max_body_size 0;
    +              '';
    +            };
    +            "/${prometheusWebRoot}" = {
    +              proxyPass = "http://${prometheusUpstream}";
    +              extraConfig = ''
    +                client_max_body_size 0;
    +              '';
                 };
               };
             };
           };
         };
       };
    -
     }
     
    @@ -10015,7 +10953,11 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with

    -
    { lib, config, ... }:
    +
    { self, lib, config, ... }:
    +let
    +  serviceName = "freshrss";
    +  serviceDomain = "signpost.swarsel.win";
    +in
     {
       options.swarselsystems.modules.server.freshrss = lib.mkEnableOption "enable freshrss on server";
       config = lib.mkIf config.swarselsystems.modules.server.freshrss {
    @@ -10055,10 +10997,16 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with
           #   };
         };
     
    +    topology.self.services.freshrss = {
    +      name = "FreshRSS";
    +      info = "https://${serviceDomain}";
    +      icon = "${self}/topology/images/freshrss.png";
    +    };
    +
         services.freshrss = {
           enable = true;
    -      virtualHost = "signpost.swarsel.win";
    -      baseUrl = "https://signpost.swarsel.win";
    +      virtualHost = serviceDomain;
    +      baseUrl = "https://${serviceDomain}";
           authType = "form";
           dataDir = "/Vault/data/tt-rss";
           defaultUser = "Swarsel";
    @@ -10069,14 +11017,22 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with
         #   config.sops.templates.freshrss-env.path
         # ];
     
    -    services.nginx = {
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:80" = { };
    +          };
    +        };
    +      };
           virtualHosts = {
    -        "signpost.swarsel.win" = {
    +        "${serviceDomain}" = {
               enableACME = true;
               forceSSL = true;
               acmeRoot = null;
               locations = {
                 "/" = {
    +              proxyPass = "http://${serviceName}";
                   extraConfig = ''
                     auth_request /oauth2/auth;
                     error_page 401 = /oauth2/sign_in;
    @@ -10116,6 +11072,9 @@ FreshRSS claims to support HTTP header auth, but at least it does not work with
                     proxy_pass_request_body           off;
                   '';
                 };
    +            "/api" = {
    +              proxyPass = "http://${serviceName}";
    +            };
               };
             };
           };
    @@ -10283,6 +11242,9 @@ in
     
    { lib, config, ... }:
    +let
    +  serviceDomain = "synki.swarsel.win";
    +in
     {
       options.swarselsystems.modules.server.ankisync = lib.mkEnableOption "enable ankisync on server";
       config = lib.mkIf config.swarselsystems.modules.server.ankisync {
    @@ -10291,6 +11253,11 @@ in
     
         sops.secrets.swarsel = { owner = "root"; };
     
    +    topology.self.services.anki = {
    +      name = lib.mkForce "Anki Sync Server";
    +      info = "https://${serviceDomain}";
    +    };
    +
         services.anki-sync-server = {
           enable = true;
           port = 27701;
    @@ -10306,7 +11273,7 @@ in
     
         services.nginx = {
           virtualHosts = {
    -        "synki.swarsel.win" = {
    +        "${serviceDomain}" = {
               enableACME = true;
               forceSSL = true;
               acmeRoot = null;
    @@ -10328,8 +11295,8 @@ in
     
    -
    -
    3.3.2.27. IDM (kanidm + oauth2-proxy)
    +
    +
    3.3.2.27. kanidm

    The forgejo configuration is a little broken and will show a 500 error when signing in through kanidm. However, when pressing back and refreshing the page, I am logged in. Currently I cannot be bothered to fix this. @@ -10348,62 +11315,52 @@ To get other URLs (token, etc.), use { self, lib, pkgs, config, ... }: let certsSopsFile = self + /secrets/certs/secrets.yaml; - kanidmDomain = "sso.swarsel.win"; + serviceDomain = "sso.swarsel.win"; + servicePort = 8300; + serviceUser = "kanidm"; + serviceGroup = serviceUser; + serviceName = "kanidm"; oauth2ProxyDomain = "soauth.swarsel.win"; - kanidmPort = 8300; - oauth2ProxyPort = 3004; in { - options.swarselsystems.modules.server.kanidm = lib.mkEnableOption "enable kanidm on server"; - config = lib.mkIf config.swarselsystems.modules.server.kanidm { + options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server"; + config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" { - users.users.kanidm = { - group = "kanidm"; + users.users."${serviceUser}" = { + group = serviceGroup; isSystemUser = true; }; - users.groups.kanidm = { }; + users.groups."${serviceGroup}" = { }; sops = { secrets = { - "oauth2-cookie-secret" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; }; - "kanidm-self-signed-crt" = { sopsFile = certsSopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-self-signed-key" = { sopsFile = certsSopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-admin-pw" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-idm-admin-pw" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-immich" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-paperless" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-forgejo" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-grafana" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-nextcloud" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-freshrss" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-oauth2-proxy" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; }; - "kanidm-oauth2-proxy-client" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; }; - }; - - templates = { - "kanidm-oauth2-proxy-client-env" = { - content = '' - OAUTH2_PROXY_CLIENT_SECRET="${config.sops.placeholder.kanidm-oauth2-proxy-client}" - OAUTH2_PROXY_COOKIE_SECRET=${config.sops.placeholder.oauth2-cookie-secret} - ''; - owner = "oauth2-proxy"; - group = "oauth2-proxy"; - mode = "0440"; - }; + "kanidm-self-signed-crt" = { sopsFile = certsSopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-self-signed-key" = { sopsFile = certsSopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-admin-pw" = { owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-idm-admin-pw" = { owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-immich" = { owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-paperless" = { owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-forgejo" = { owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-grafana" = { owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-nextcloud" = { owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-freshrss" = { owner = serviceUser; group = serviceGroup; mode = "0440"; }; + "kanidm-oauth2-proxy" = { owner = serviceUser; group = serviceGroup; mode = "0440"; }; }; }; + networking.firewall.allowedTCPPorts = [ servicePort ]; + services = { kanidm = { package = pkgs.kanidmWithSecretProvisioning; enableServer = true; serverSettings = { - domain = kanidmDomain; - origin = "https://${kanidmDomain}"; + domain = serviceDomain; + origin = "https://${serviceDomain}"; tls_chain = config.sops.secrets.kanidm-self-signed-crt.path; tls_key = config.sops.secrets.kanidm-self-signed-key.path; - bindaddress = "0.0.0.0:${toString kanidmPort}"; + bindaddress = "0.0.0.0:${toString servicePort}"; trust_x_forward_for = true; }; enableClient = true; @@ -10524,19 +11481,6 @@ in }; }; }; - # freshrss = { - # displayName = "FreshRSS"; - # originUrl = "https://signpost.swarsel.win/apps/sociallogin/custom_oidc/kanidm"; - # originLanding = "https://signpost.swarsel.win/"; - # basicSecretFile = config.sops.secrets.kanidm-freshrss.path; - # allowInsecureClientDisablePkce = true; - # scopeMaps."freshrss.access" = [ - # "openid" - # "email" - # "profile" - # ]; - # preferShortUsername = true; - # }; oauth2-proxy = { displayName = "Oauth2-Proxy"; originUrl = "https://${oauth2ProxyDomain}/oauth2/callback"; @@ -10573,6 +11517,78 @@ in }; }; }; + }; + + systemd.services = { + kanidm.serviceConfig.RestartSec = "30"; + }; + + nodes.moonside.services.nginx = { + upstreams = { + "${serviceName}" = { + servers = { + "192.168.1.2:${builtins.toString servicePort}" = { }; + }; + }; + }; + virtualHosts = { + "${serviceDomain}" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + locations = { + "/" = { + proxyPass = "https://${serviceName}"; + }; + }; + extraConfig = '' + proxy_ssl_verify off; + ''; + }; + }; + }; + }; +} +

    +
    +
    +
    +
    +
    3.3.2.28. oauth2-proxy
    +
    +
    +
    { lib, config, ... }:
    +let
    +  kanidmDomain = "sso.swarsel.win";
    +  oauth2ProxyDomain = "soauth.swarsel.win";
    +  oauth2ProxyPort = 3004;
    +in
    +{
    +  options.swarselsystems.modules.server.oauth2Proxy = lib.mkEnableOption "enable oauth2-proxy on server";
    +  config = lib.mkIf config.swarselsystems.modules.server.oauth2Proxy {
    +
    +    sops = {
    +      secrets = {
    +        "oauth2-cookie-secret" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; };
    +        "kanidm-oauth2-proxy-client" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; };
    +      };
    +
    +      templates = {
    +        "kanidm-oauth2-proxy-client-env" = {
    +          content = ''
    +            OAUTH2_PROXY_CLIENT_SECRET="${config.sops.placeholder.kanidm-oauth2-proxy-client}"
    +            OAUTH2_PROXY_COOKIE_SECRET=${config.sops.placeholder.oauth2-cookie-secret}
    +          '';
    +          owner = "oauth2-proxy";
    +          group = "oauth2-proxy";
    +          mode = "0440";
    +        };
    +      };
    +    };
    +
    +    networking.firewall.allowedTCPPorts = [ oauth2ProxyPort ];
    +
    +    services = {
           oauth2-proxy = {
             enable = true;
             cookie = {
    @@ -10607,9 +11623,8 @@ in
         };
     
         systemd.services = {
    -      kanidm.serviceConfig.RestartSec = "30";
           oauth2-proxy = {
    -        after = [ "kanidm.service" ];
    +        # after = [ "kanidm.service" ];
             serviceConfig = {
               RuntimeDirectory = "oauth2-proxy";
               RuntimeDirectoryMode = "0750";
    @@ -10624,31 +11639,13 @@ in
     
         services.nginx = {
           upstreams = {
    -        kanidm = {
    -          servers = {
    -            "192.168.1.2:${builtins.toString kanidmPort}" = { };
    -          };
    -        };
             oauth2-proxy = {
               servers = {
    -            "192.168.1.2:${builtins.toString oauth2ProxyPort}" = { };
    +            "localhost:${builtins.toString oauth2ProxyPort}" = { };
               };
             };
           };
           virtualHosts = {
    -        "${kanidmDomain}" = {
    -          enableACME = true;
    -          forceSSL = true;
    -          acmeRoot = null;
    -          locations = {
    -            "/" = {
    -              proxyPass = "https://kanidm";
    -            };
    -          };
    -          extraConfig = ''
    -            proxy_ssl_verify off;
    -          '';
    -        };
             "${oauth2ProxyDomain}" = {
               enableACME = true;
               forceSSL = true;
    @@ -10671,15 +11668,16 @@ in
     
    -
    -
    3.3.2.28. Firefly-III
    -
    +
    +
    3.3.2.29. Firefly-III
    +
    -
    { lib, config, ... }:
    +
    { self, lib, config, ... }:
     let
       cfg = config.services.firefly-iii;
       fireflyDomain = "stonks.swarsel.win";
       fireflyUser = "firefly-iii";
    +  serviceName = "firefly";
     in
     {
       options.swarselsystems.modules.server.firefly = lib.mkEnableOption "enable firefly-iii on server";
    @@ -10696,6 +11694,12 @@ in
           };
         };
     
    +    topology.self.services.firefly-iii = {
    +      name = "Firefly-III";
    +      info = "https://${fireflyDomain}";
    +      icon = "${self}/topology/images/firefly-iii.png";
    +    };
    +
         services = {
           firefly-iii = {
             enable = true;
    @@ -10708,6 +11712,7 @@ in
               APP_KEY_FILE = config.sops.secrets.firefly-iii-app-key.path;
               APP_ENV = "local";
               DB_CONNECTION = "sqlite";
    +          TRUSTED_PROXIES = "**";
               # AUTHENTICATION_GUARD = "remote_user_guard";
               # AUTHENTICATION_GUARD_HEADER = "X-User";
               # AUTHENTICATION_GUARD_EMAIL = "X-Email";
    @@ -10719,52 +11724,7 @@ in
           nginx = {
             virtualHosts = {
               "${fireflyDomain}" = {
    -            enableACME = true;
    -            forceSSL = true;
    -            acmeRoot = null;
    -            # main config is automatically added by nixos firefly config.
    -            # hence, only provide certificate
                 locations = {
    -              "/" = {
    -                extraConfig = ''
    -                  auth_request /oauth2/auth;
    -                  error_page 401 = /oauth2/sign_in;
    -
    -                  # pass information via X-User and X-Email headers to backend,
    -                  # requires running with --set-xauthrequest flag (done by NixOS)
    -                  auth_request_set $user   $upstream_http_x_auth_request_user;
    -                  auth_request_set $email  $upstream_http_x_auth_request_email;
    -                  proxy_set_header X-User  $user;
    -                  proxy_set_header X-Email $email;
    -
    -                  # if you enabled --pass-access-token, this will pass the token to the backend
    -                  auth_request_set $token  $upstream_http_x_auth_request_access_token;
    -                  proxy_set_header X-Access-Token $token;
    -
    -                  # if you enabled --cookie-refresh, this is needed for it to work with auth_request
    -                  auth_request_set $auth_cookie $upstream_http_set_cookie;
    -                  add_header Set-Cookie $auth_cookie;
    -                '';
    -              };
    -              "/oauth2/" = {
    -                proxyPass = "http://oauth2-proxy";
    -                extraConfig = ''
    -
    -                  proxy_set_header X-Scheme                $scheme;
    -                  proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
    -                '';
    -              };
    -              "= /oauth2/auth" = {
    -                proxyPass = "http://oauth2-proxy/oauth2/auth";
    -                extraConfig = ''
    -                  internal;
    -
    -                  proxy_set_header X-Scheme         $scheme;
    -                  # nginx auth_request includes headers but not body
    -                  proxy_set_header Content-Length   "";
    -                  proxy_pass_request_body           off;
    -                '';
    -              };
                   "/api" = {
                     extraConfig = ''
                       index index.php;
    @@ -10779,6 +11739,198 @@ in
             };
           };
         };
    +
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:80" = { };
    +          };
    +        };
    +      };
    +      virtualHosts = {
    +        "${fireflyDomain}" = {
    +          enableACME = true;
    +          forceSSL = true;
    +          acmeRoot = null;
    +          # main config is automatically added by nixos firefly config.
    +          # hence, only provide certificate
    +          locations = {
    +            "/" = {
    +              proxyPass = "http://${serviceName}";
    +              extraConfig = ''
    +                auth_request /oauth2/auth;
    +                error_page 401 = /oauth2/sign_in;
    +
    +                # pass information via X-User and X-Email headers to backend,
    +                # requires running with --set-xauthrequest flag (done by NixOS)
    +                auth_request_set $user   $upstream_http_x_auth_request_user;
    +                auth_request_set $email  $upstream_http_x_auth_request_email;
    +                proxy_set_header X-User  $user;
    +                proxy_set_header X-Email $email;
    +
    +                # if you enabled --pass-access-token, this will pass the token to the backend
    +                auth_request_set $token  $upstream_http_x_auth_request_access_token;
    +                proxy_set_header X-Access-Token $token;
    +
    +                # if you enabled --cookie-refresh, this is needed for it to work with auth_request
    +                auth_request_set $auth_cookie $upstream_http_set_cookie;
    +                add_header Set-Cookie $auth_cookie;
    +              '';
    +            };
    +            "/oauth2/" = {
    +              proxyPass = "http://oauth2-proxy";
    +              extraConfig = ''
    +
    +                          proxy_set_header X-Scheme                $scheme;
    +                          proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
    +                        '';
    +            };
    +            "= /oauth2/auth" = {
    +              proxyPass = "http://oauth2-proxy/oauth2/auth";
    +              extraConfig = ''
    +                internal;
    +
    +                proxy_set_header X-Scheme         $scheme;
    +                # nginx auth_request includes headers but not body
    +                proxy_set_header Content-Length   "";
    +                proxy_pass_request_body           off;
    +              '';
    +            };
    +            "/api" = {
    +              proxyPass = "http://${serviceName}";
    +            };
    +          };
    +        };
    +      };
    +    };
    +  };
    +}
    +
    +
    +
    +
    +
    +
    3.3.2.30. Koillection
    +
    +
    +
    { self, lib, config, ... }:
    +let
    +  serviceDomain = "swag.swarsel.win";
    +  serviceUser = "koillection";
    +  serviceDB = "koillection";
    +  serviceName = "koillection";
    +  servicePort = 2282;
    +  postgresUser = config.systemd.services.postgresql.serviceConfig.User; # postgres
    +  postgresPort = config.services.postgresql.settings.port; # 5432
    +  containerRev = "sha256:96693e41a6eb2aae44f96033a090378270f024ddf4e6095edf8d57674f21095d";
    +in
    +{
    +  options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
    +  config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
    +
    +    sops.secrets = {
    +      koillection-db-password = { owner = postgresUser; group = postgresUser; mode = "0440"; };
    +      koillection-env-file = { };
    +    };
    +
    +    topology.self.services.koillection = {
    +      name = "Koillection";
    +      info = "https://${serviceDomain}";
    +      icon = "${self}/topology/images/koillection.png";
    +    };
    +
    +    virtualisation.oci-containers.containers = {
    +      koillection = {
    +        image = "koillection/koillection@${containerRev}";
    +
    +        ports = [
    +          "${toString servicePort}:80"
    +        ];
    +
    +        environment = {
    +          APP_DEBUG = "0";
    +          APP_ENV = "prod";
    +          HTTPS_ENABLED = "1";
    +          UPLOAD_MAX_FILESIZE = "512M";
    +          PHP_MEMORY_LIMIT = "512M";
    +          PHP_TZ = config.repo.secrets.common.location.timezone;
    +
    +          CORS_ALLOW_ORIGIN = "https?://(localhost|swag\\.swarsel\\.win)(:[0-9]+)?$";
    +
    +          DB_DRIVER = "pdo_pgsql";
    +          DB_NAME = serviceDB;
    +          DB_HOST = "host.docker.internal";
    +          DB_USER = serviceUser;
    +          # DB_PASSWORD set in koillection-env-file
    +          DB_PORT = "${toString postgresPort}";
    +          DB_VERSION = "16";
    +        };
    +
    +        environmentFiles = [
    +          config.sops.secrets.koillection-env-file.path
    +        ];
    +
    +        extraOptions = [
    +          "--add-host=host.docker.internal:host-gateway" # podman
    +        ];
    +      };
    +    };
    +
    +    networking.firewall.allowedTCPPorts = [ servicePort postgresPort ];
    +
    +    systemd.services.postgresql.postStart =
    +      let
    +        passwordPath = config.sops.secrets.koillection-db-password.path;
    +      in
    +      ''
    +        $PSQL -tA <<'EOF'
    +          DO $$
    +          DECLARE password TEXT;
    +          BEGIN
    +            password := trim(both from replace(pg_read_file('${passwordPath}'), E'\n', '''));
    +            EXECUTE format('ALTER ROLE ${serviceDB} WITH PASSWORD '''%s''';', password);
    +          END $$;
    +        EOF
    +      '';
    +    services = {
    +      postgresql = {
    +        enable = true;
    +        enableTCPIP = true;
    +        ensureDatabases = [ serviceDB ];
    +        ensureUsers = [
    +          {
    +            name = serviceDB;
    +            ensureDBOwnership = true;
    +          }
    +        ];
    +        authentication = ''
    +          host ${serviceDB} ${serviceDB} 10.88.0.0/16 scram-sha-256
    +        '';
    +      };
    +    };
    +
    +    nodes.moonside.services.nginx = {
    +      upstreams = {
    +        "${serviceName}" = {
    +          servers = {
    +            "192.168.1.2:${builtins.toString servicePort}" = { };
    +          };
    +        };
    +      };
    +      virtualHosts = {
    +        "${serviceDomain}" = {
    +          enableACME = true;
    +          forceSSL = true;
    +          acmeRoot = null;
    +          locations = {
    +            "/" = {
    +              proxyPass = "http://${serviceName}";
    +            };
    +          };
    +        };
    +      };
    +    };
       };
     }
     
    @@ -10997,8 +12149,8 @@ This smashes Atmosphere 1.3.2 on the switch, which is what I am currenty using.
    -
    -
    3.3.4.6. Framework
    +
    +
    3.3.4.6. Framework

    This holds configuration that is specific to framework laptops. @@ -11036,8 +12188,8 @@ This holds configuration that is specific to framework laptops.

    -
    -
    3.3.4.7. AMD CPU
    +
    +
    3.3.4.7. AMD CPU
    { lib, config, ... }:
    @@ -11053,8 +12205,8 @@ This holds configuration that is specific to framework laptops.
     
    -
    -
    3.3.4.8. AMD GPU
    +
    +
    3.3.4.8. AMD GPU
    { lib, config, ... }:
    @@ -11076,8 +12228,8 @@ This holds configuration that is specific to framework laptops.
     
    -
    -
    3.3.4.9. Hibernation
    +
    +
    3.3.4.9. Hibernation
    { lib, config, ... }:
    @@ -11108,8 +12260,8 @@ This holds configuration that is specific to framework laptops.
     
    -
    -
    3.3.4.10. BTRFS
    +
    +
    3.3.4.10. BTRFS
    { lib, config, ... }:
    @@ -11309,10 +12461,13 @@ in
               "winters" = {
                 id = "O7RWDMD-AEAHPP7-7TAVLKZ-BSWNBTU-2VA44MS-EYGUNBB-SLHKB3C-ZSLMOAA";
               };
    +          "moonside (@oracle)" = {
    +            id = "VPCDZB6-MGVGQZD-Q6DIZW3-IZJRJTO-TCC3QUQ-2BNTL7P-AKE7FBO-N55UNQE";
    +          };
               folders = {
                 "Documents" = {
                   path = "${homeDir}/Documents";
    -              devices = [ "magicant" "winters" ];
    +              devices = [ "magicant" "winters" "moonside (@oracle)" ];
                   id = "hgr3d-pfu3w";
                 };
               };
    @@ -12226,6 +13381,10 @@ It is very convenient to have SSH aliases in place for machines that I use. This
               hostname = "193.122.53.173";
               user = "root";
             };
    +        "moonside" = {
    +          hostname = "130.61.238.239";
    +          user = "root";
    +        };
             "songdiver" = {
               hostname = "89.168.100.65";
               user = "ubuntu";
    @@ -14047,8 +15206,8 @@ The `extraConfig` section here CANNOT be reindented. This has something to do wi
     
    -
    -
    3.4.1.29.4. SwayOSD
    +
    +
    3.4.1.29.4. SwayOSD
    { lib, config, ... }:
    @@ -15294,8 +16453,8 @@ in
     
    -
    -
    3.4.4.3. Framework
    +
    +
    3.4.4.3. Framework

    This holds configuration that is specific to framework laptops. @@ -19135,8 +20294,8 @@ autocmd DocStart vc-impimba-1.m.imp.ac.at/ui/webconsole mode ignore

    -
    -

    6.3. tridactyl theme

    +
    +

    6.3. tridactyl theme

    @@ -19633,7 +20792,7 @@ sync USER HOST:
     

    Author: Leon Schwarzäugl

    -

    Created: 2025-06-13 Fr 03:31

    +

    Created: 2025-06-16 Mo 00:41

    Validate

    diff --git a/lib/default.nix b/lib/default.nix index 16642af..66c8f4a 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -22,6 +22,7 @@ in pkgsFor = lib.genAttrs (import systems) (system: import inputs.nixpkgs { inherit system; + overlays = [ self.overlays.default ]; config.allowUnfree = true; } ); @@ -69,43 +70,45 @@ in _module.args.primaryUser = linuxUser; } ] ++ - (if (host == "iso") then [ ] else - ([ - # put nixos imports here that are for all servers and normal hosts + (if (host == "iso") then [ inputs.nix-topology.nixosModules.default - "${self}/modules/${type}/common" - inputs.stylix.nixosModules.stylix - inputs.nswitch-rcm-nix.nixosModules.nswitch-rcm - ] ++ (if (type == "nixos") then [ - inputs.home-manager.nixosModules.home-manager - "${self}/profiles/nixos" - "${self}/modules/nixos/server" - "${self}/modules/nixos/optional" - { - home-manager.users."${linuxUser}".imports = [ - # put home-manager imports here that are for all normal hosts - "${self}/modules/home/common" - "${self}/modules/home/server" - "${self}/modules/home/optional" - "${self}/profiles/home" - ]; - } - ] else [ - # put nixos imports here that are for darwin hosts - "${self}/modules/darwin/nixos/common" - "${self}/profiles/darwin" - inputs.home-manager.darwinModules.home-manager - { - home-manager.users."${macUser}".imports = [ - # put home-manager imports here that are for darwin hosts - "${self}/modules/darwin/home" - "${self}/modules/home/server" - "${self}/modules/home/optional" - "${self}/profiles/home" - ]; - } - ]) - )); + ] else + ([ + # put nixos imports here that are for all servers and normal hosts + inputs.nix-topology.nixosModules.default + "${self}/modules/${type}/common" + inputs.stylix.nixosModules.stylix + inputs.nswitch-rcm-nix.nixosModules.nswitch-rcm + ] ++ (if (type == "nixos") then [ + inputs.home-manager.nixosModules.home-manager + "${self}/profiles/nixos" + "${self}/modules/nixos/server" + "${self}/modules/nixos/optional" + { + home-manager.users."${linuxUser}".imports = [ + # put home-manager imports here that are for all normal hosts + "${self}/modules/home/common" + "${self}/modules/home/server" + "${self}/modules/home/optional" + "${self}/profiles/home" + ]; + } + ] else [ + # put nixos imports here that are for darwin hosts + "${self}/modules/darwin/nixos/common" + "${self}/profiles/darwin" + inputs.home-manager.darwinModules.home-manager + { + home-manager.users."${macUser}".imports = [ + # put home-manager imports here that are for darwin hosts + "${self}/modules/darwin/home" + "${self}/modules/home/server" + "${self}/modules/home/optional" + "${self}/profiles/home" + ]; + } + ]) + )); }; }; diff --git a/modules/nixos/common/meta.nix b/modules/nixos/common/meta.nix index d416302..fcb79d3 100644 --- a/modules/nixos/common/meta.nix +++ b/modules/nixos/common/meta.nix @@ -1,12 +1,16 @@ { lib, ... }: { - options.node.secretsDir = lib.mkOption { - description = "Path to the secrets directory for this node."; - type = lib.types.path; - default = ./.; - }; - options.node.name = lib.mkOption { - description = "Node Name."; - type = lib.types.str; + options = { + node = { + secretsDir = lib.mkOption { + description = "Path to the secrets directory for this node."; + type = lib.types.path; + default = ./.; + }; + name = lib.mkOption { + description = "Node Name."; + type = lib.types.str; + }; + }; }; } diff --git a/modules/nixos/common/packages.nix b/modules/nixos/common/packages.nix index 3f08250..c54d275 100644 --- a/modules/nixos/common/packages.nix +++ b/modules/nixos/common/packages.nix @@ -72,6 +72,8 @@ zls ansible-language-server + elk-to-svg + ]; nixpkgs.config.permittedInsecurePackages = [ diff --git a/modules/nixos/common/topology.nix b/modules/nixos/common/topology.nix new file mode 100644 index 0000000..2127c78 --- /dev/null +++ b/modules/nixos/common/topology.nix @@ -0,0 +1,14 @@ +{ self, lib, config, ... }: +{ + options.swarselsystems.info = lib.mkOption { + type = lib.types.str; + default = ""; + }; + config.topology = { + id = config.node.name; + self = { + hardware.info = config.swarselsystems.info; + icon = lib.mkIf config.swarselsystems.isLaptop "devices.laptop"; + }; + }; +} diff --git a/modules/nixos/server/ankisync.nix b/modules/nixos/server/ankisync.nix index 70e242c..531b760 100644 --- a/modules/nixos/server/ankisync.nix +++ b/modules/nixos/server/ankisync.nix @@ -1,4 +1,7 @@ { lib, config, ... }: +let + serviceDomain = "synki.swarsel.win"; +in { options.swarselsystems.modules.server.ankisync = lib.mkEnableOption "enable ankisync on server"; config = lib.mkIf config.swarselsystems.modules.server.ankisync { @@ -7,6 +10,11 @@ sops.secrets.swarsel = { owner = "root"; }; + topology.self.services.anki = { + name = lib.mkForce "Anki Sync Server"; + info = "https://${serviceDomain}"; + }; + services.anki-sync-server = { enable = true; port = 27701; @@ -22,7 +30,7 @@ services.nginx = { virtualHosts = { - "synki.swarsel.win" = { + "${serviceDomain}" = { enableACME = true; forceSSL = true; acmeRoot = null; diff --git a/modules/nixos/server/firefly-iii.nix b/modules/nixos/server/firefly-iii.nix index 9c648e9..ea72d8d 100644 --- a/modules/nixos/server/firefly-iii.nix +++ b/modules/nixos/server/firefly-iii.nix @@ -1,4 +1,4 @@ -{ lib, config, ... }: +{ self, lib, config, ... }: let cfg = config.services.firefly-iii; fireflyDomain = "stonks.swarsel.win"; @@ -20,6 +20,12 @@ in }; }; + topology.self.services.firefly-iii = { + name = "Firefly-III"; + info = "https://${fireflyDomain}"; + icon = "${self}/topology/images/firefly-iii.png"; + }; + services = { firefly-iii = { enable = true; diff --git a/modules/nixos/server/freshrss.nix b/modules/nixos/server/freshrss.nix index 6cadab9..40f0e36 100644 --- a/modules/nixos/server/freshrss.nix +++ b/modules/nixos/server/freshrss.nix @@ -1,6 +1,7 @@ -{ lib, config, ... }: +{ self, lib, config, ... }: let serviceName = "freshrss"; + serviceDomain = "signpost.swarsel.win"; in { options.swarselsystems.modules.server.freshrss = lib.mkEnableOption "enable freshrss on server"; @@ -41,10 +42,16 @@ in # }; }; + topology.self.services.freshrss = { + name = "FreshRSS"; + info = "https://${serviceDomain}"; + icon = "${self}/topology/images/freshrss.png"; + }; + services.freshrss = { enable = true; - virtualHost = "signpost.swarsel.win"; - baseUrl = "https://signpost.swarsel.win"; + virtualHost = serviceDomain; + baseUrl = "https://${serviceDomain}"; authType = "form"; dataDir = "/Vault/data/tt-rss"; defaultUser = "Swarsel"; @@ -64,7 +71,7 @@ in }; }; virtualHosts = { - "signpost.swarsel.win" = { + "${serviceDomain}" = { enableACME = true; forceSSL = true; acmeRoot = null; @@ -110,6 +117,9 @@ in proxy_pass_request_body off; ''; }; + "/api" = { + proxyPass = "http://${serviceName}"; + }; }; }; }; diff --git a/modules/nixos/server/immich.nix b/modules/nixos/server/immich.nix index a41e738..58fd660 100644 --- a/modules/nixos/server/immich.nix +++ b/modules/nixos/server/immich.nix @@ -13,6 +13,8 @@ in extraGroups = [ "video" "render" "users" ]; }; + topology.self.services.immich.info = "https://${serviceDomain}"; + services.immich = { enable = true; host = "0.0.0.0"; diff --git a/modules/nixos/server/jellyfin.nix b/modules/nixos/server/jellyfin.nix index 3018abc..dbad967 100644 --- a/modules/nixos/server/jellyfin.nix +++ b/modules/nixos/server/jellyfin.nix @@ -23,6 +23,9 @@ in libvdpau-va-gl ]; }; + + topology.self.services.jellyfin.info = "https://${serviceDomain}"; + services.jellyfin = { enable = true; user = serviceUser; diff --git a/modules/nixos/server/kavita.nix b/modules/nixos/server/kavita.nix index 42178a7..abc1c32 100644 --- a/modules/nixos/server/kavita.nix +++ b/modules/nixos/server/kavita.nix @@ -1,4 +1,4 @@ -{ pkgs, lib, config, ... }: +{ self, lib, config, pkgs, ... }: let serviceName = "kavita"; serviceUser = "kavita"; @@ -20,6 +20,12 @@ in networking.firewall.allowedTCPPorts = [ 8080 ]; + topology.self.services.kavita = { + name = "Kavita"; + info = "https://${serviceDomain}"; + icon = "${self}/topology/images/kavita.png"; + }; + services.kavita = { enable = true; user = serviceUser; diff --git a/modules/nixos/server/koillection.nix b/modules/nixos/server/koillection.nix index 36eaad9..c57ad1f 100644 --- a/modules/nixos/server/koillection.nix +++ b/modules/nixos/server/koillection.nix @@ -1,4 +1,4 @@ -{ lib, config, ... }: +{ self, lib, config, ... }: let serviceDomain = "swag.swarsel.win"; serviceUser = "koillection"; @@ -18,6 +18,12 @@ in koillection-env-file = { }; }; + topology.self.services.koillection = { + name = "Koillection"; + info = "https://${serviceDomain}"; + icon = "${self}/topology/images/koillection.png"; + }; + virtualisation.oci-containers.containers = { koillection = { image = "koillection/koillection@${containerRev}"; diff --git a/modules/nixos/server/monitoring.nix b/modules/nixos/server/monitoring.nix index 19628c0..dc4e58e 100644 --- a/modules/nixos/server/monitoring.nix +++ b/modules/nixos/server/monitoring.nix @@ -34,6 +34,8 @@ in networking.firewall.allowedTCPPorts = [ servicePort prometheusPort ]; + topology.self.services.prometheus.info = "https://${serviceDomain}/${prometheusWebRoot}"; + services = { grafana = { enable = true; diff --git a/modules/nixos/server/mpd.nix b/modules/nixos/server/mpd.nix index 2188ec8..e8e3b18 100644 --- a/modules/nixos/server/mpd.nix +++ b/modules/nixos/server/mpd.nix @@ -1,4 +1,4 @@ -{ pkgs, lib, config, ... }: +{ self, lib, config, pkgs, ... }: { options.swarselsystems.modules.server.mpd = lib.mkEnableOption "enable mpd on server"; config = lib.mkIf config.swarselsystems.modules.server.mpd { @@ -26,6 +26,12 @@ mpv ]; + topology.self.services.mpd = { + name = "MPD"; + info = "http://localhost:3254"; + icon = "${self}/topology/images/mpd.png"; + }; + services.mpd = { enable = true; musicDirectory = "/media"; diff --git a/modules/nixos/server/transmission.nix b/modules/nixos/server/transmission.nix index cf22e77..b14fac9 100644 --- a/modules/nixos/server/transmission.nix +++ b/modules/nixos/server/transmission.nix @@ -1,4 +1,7 @@ -{ pkgs, lib, config, ... }: +{ self, pkgs, lib, config, ... }: +let + serviceDomain = "store.swarsel.win"; +in { options.swarselsystems.modules.server.transmission = lib.mkEnableOption "enable transmission and friends on server"; config = lib.mkIf config.swarselsystems.modules.server.transmission { @@ -55,6 +58,18 @@ docker ]; + topology.self.services = { + radarr.info = "https://${serviceDomain}/radarr"; + readarr = { + name = "Readarr"; + info = "https://${serviceDomain}/readarr"; + icon = "${self}/topology/images/readarr.png"; + }; + sonarr.info = "https://${serviceDomain}/sonarr"; + lidarr.info = "https://${serviceDomain}/lidarr"; + prowlarr.info = "https://${serviceDomain}/prowlarr"; + }; + services = { radarr = { enable = true; diff --git a/topology/default.nix b/topology/default.nix new file mode 100644 index 0000000..afe49b9 --- /dev/null +++ b/topology/default.nix @@ -0,0 +1,154 @@ +{ config, ... }: +let + inherit (config.lib.topology) + mkInternet + mkDevice + mkSwitch + mkRouter + mkConnection + ; +in +{ + renderer = "elk"; + + networks = { + home-lan = { + name = "Home LAN"; + cidrv4 = "192.168.1.0/24"; + }; + wg = { + name = "Wireguard Tunnel"; + cidrv4 = "192.168.3.0/24"; + }; + }; + + nodes = { + internet = mkInternet { + connections = [ + (mkConnection "moonside" "wan") + (mkConnection "pfsense" "wan") + (mkConnection "sync" "wan") + ]; + }; + + sync.interfaces.wan = { }; + moonside.interfaces.wan = { }; + + pfsense = mkRouter "pfSense" { + info = "HUNSN RM02"; + image = ../topology/images/hunsn.png; + interfaceGroups = [ + [ + "eth2" + "eth3" + "eth4" + "eth5" + "eth6" + ] + [ "wan" ] + ]; + interfaces.wg0 = { + addresses = [ "192.168.3.1" ]; + network = "wg"; + virtual = true; + type = "wireguard"; + }; + + connections = { + eth2 = mkConnection "switch-livingroom" "eth1"; + eth4 = mkConnection "winters" "eth1"; + eth3 = mkConnection "switch-bedroom" "eth1"; + eth6 = mkConnection "wifi-ap" "eth1"; + wg = mkConnection "moonside" "wg"; + }; + interfaces = { + eth2 = { + addresses = [ "192.168.1.1" ]; + network = "home-lan"; + }; + eth3 = { + addresses = [ "192.168.1.1" ]; + network = "home-lan"; + }; + eth4 = { + addresses = [ "192.168.1.1" ]; + network = "home-lan"; + }; + eth6 = { + addresses = [ "192.168.1.1" ]; + network = "home-lan"; + }; + }; + }; + + winters.interfaces."eth1" = { }; + + wifi-ap = mkSwitch "Wi-Fi AP" { + info = "Huawei"; + image = ../topology/images/huawei.png; + interfaceGroups = [ + [ + "eth1" + "wifi" + ] + ]; + }; + + switch-livingroom = mkSwitch "Switch Livingroom" { + info = "TL-SG108"; + image = ../topology/images/TL-SG108.png; + interfaceGroups = [ + [ + "eth1" + "eth2" + "eth3" + "eth4" + "eth5" + "eth6" + "eth7" + "eth8" + ] + ]; + connections = { + eth2 = mkConnection "nswitch" "eth1"; + eth7 = mkConnection "pc" "eth1"; + eth8 = mkConnection "nbl-imba-2" "eth1"; + }; + }; + + nswitch = mkDevice "Nintendo Switch" { + info = "Nintendo Switch"; + image = ../topology/images/nintendo-switch.png; + interfaces.eth1 = { }; + }; + + pc = mkDevice "Windows Gaming Server" { + info = "i7-4790k, GTX970, 32GB RAM"; + image = ../topology/images/pc.png; + interfaces.eth1 = { }; + }; + + nbl-imba-2.interfaces.eth1 = { }; + + switch-bedroom = mkSwitch "Switch Bedroom" { + info = "TL-SG1005D"; + image = ../topology/images/TL-SG1005D.png; + interfaceGroups = [ + [ + "eth1" + "eth2" + "eth3" + "eth4" + "eth5" + ] + ]; + connections.eth2 = mkConnection "printer" "eth1"; + }; + + printer = mkDevice "Printer" { + info = "DELL C2665dnf"; + image = ../topology/images/DELL-C2665dnf.png; + interfaces.eth1 = { }; + }; + }; +} diff --git a/topology/images/DELL-C2665dnf.png b/topology/images/DELL-C2665dnf.png new file mode 100644 index 0000000..798595a Binary files /dev/null and b/topology/images/DELL-C2665dnf.png differ diff --git a/topology/images/TL-SG1005D.png b/topology/images/TL-SG1005D.png new file mode 100644 index 0000000..c06f063 Binary files /dev/null and b/topology/images/TL-SG1005D.png differ diff --git a/topology/images/TL-SG108.png b/topology/images/TL-SG108.png new file mode 100644 index 0000000..3088ba8 Binary files /dev/null and b/topology/images/TL-SG108.png differ diff --git a/topology/images/firefly-iii.png b/topology/images/firefly-iii.png new file mode 100644 index 0000000..de09e5e Binary files /dev/null and b/topology/images/firefly-iii.png differ diff --git a/topology/images/freshrss.png b/topology/images/freshrss.png new file mode 100644 index 0000000..d1a7511 Binary files /dev/null and b/topology/images/freshrss.png differ diff --git a/topology/images/huawei.png b/topology/images/huawei.png new file mode 100644 index 0000000..20fd367 Binary files /dev/null and b/topology/images/huawei.png differ diff --git a/topology/images/hunsn.png b/topology/images/hunsn.png new file mode 100644 index 0000000..f2cc4c2 Binary files /dev/null and b/topology/images/hunsn.png differ diff --git a/topology/images/kavita.png b/topology/images/kavita.png new file mode 100644 index 0000000..dc1cd45 Binary files /dev/null and b/topology/images/kavita.png differ diff --git a/topology/images/koillection.png b/topology/images/koillection.png new file mode 100644 index 0000000..c38d95a Binary files /dev/null and b/topology/images/koillection.png differ diff --git a/topology/images/mpd.png b/topology/images/mpd.png new file mode 100644 index 0000000..d8b6d96 Binary files /dev/null and b/topology/images/mpd.png differ diff --git a/topology/images/nintendo-switch.png b/topology/images/nintendo-switch.png new file mode 100644 index 0000000..21f8de3 Binary files /dev/null and b/topology/images/nintendo-switch.png differ diff --git a/topology/images/pc.png b/topology/images/pc.png new file mode 100644 index 0000000..df176c8 Binary files /dev/null and b/topology/images/pc.png differ diff --git a/topology/images/readarr.png b/topology/images/readarr.png new file mode 100644 index 0000000..2ce0639 Binary files /dev/null and b/topology/images/readarr.png differ