Commit 1dae7872efa5

Vincent Demeester <vincent@sbr.pm>
2026-01-14 12:14:17
feat(xmpp): add Prosody XMPP server on aion
Add XMPP messaging server on aion for automated research workflow. This enables sending research requests from mobile via XMPP bot. Changes: - Add xmpp.sbr.pm DNS entry pointing to aion VPN IP - Configure Prosody XMPP server with TLS via Gandi ACME - Add aion to gandi.env.age secret for DNS-01 challenge - Open firewall port 5222 for XMPP client connections - VPN-only access (10.100.0.49) Next steps: - Re-encrypt gandi.env.age secret to include aion - Deploy to aion and create user accounts - Implement research bot in Phase 2 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent b697f6b
systems/aion/extra.nix
@@ -45,6 +45,7 @@ in
     ../../modules/audible-sync
     ../../modules/music-playlist-dl
     ../../modules/harmonia
+    ./xmpp.nix
   ];
 
   users.users.vincent.linger = true;
systems/aion/xmpp.nix
@@ -0,0 +1,59 @@
+{
+  config,
+  ...
+}:
+{
+  # XMPP server with Prosody - simplified configuration
+  # VPN-only access at xmpp.sbr.pm (10.100.0.49)
+
+  services.prosody = {
+    enable = true;
+    admins = [ "vincent@xmpp.sbr.pm" ];
+
+    # SSL/TLS configuration
+    ssl = {
+      cert = "/var/lib/acme/xmpp.sbr.pm/fullchain.pem";
+      key = "/var/lib/acme/xmpp.sbr.pm/key.pem";
+    };
+
+    # Virtual host configuration
+    virtualHosts."xmpp.sbr.pm" = {
+      enabled = true;
+      domain = "xmpp.sbr.pm";
+      ssl = {
+        cert = "/var/lib/acme/xmpp.sbr.pm/fullchain.pem";
+        key = "/var/lib/acme/xmpp.sbr.pm/key.pem";
+      };
+    };
+
+    # Disable XEP-0423 compliance checking for now
+    xmppComplianceSuite = false;
+
+    # Security settings
+    c2sRequireEncryption = true; # Force TLS for client connections
+    allowRegistration = false; # Disable public registration
+  };
+
+  # ACME configuration
+  security.acme.acceptTerms = true;
+  security.acme.defaults.email = "vincent@sbr.pm";
+
+  # SSL certificates via ACME with Gandi DNS-01 challenge
+  security.acme.certs."xmpp.sbr.pm" = {
+    domain = "xmpp.sbr.pm";
+    dnsProvider = "gandiv5";
+    credentialsFile = config.age.secrets."gandi.env".path;
+    group = "prosody"; # Allow prosody to read certificates
+  };
+
+  # Age secret for Gandi API (shared with rhea for DNS-01 challenge)
+  age.secrets."gandi.env" = {
+    file = ../../secrets/rhea/gandi.env.age;
+    mode = "400";
+  };
+
+  # Firewall configuration - XMPP ports
+  networking.firewall.allowedTCPPorts = [
+    5222 # C2S (client-to-server) - main XMPP port
+  ];
+}
globals.nix
@@ -560,5 +560,7 @@ _: {
       host = "rhea";
       aliases = [ "llm" ];
     };
+    # XMPP messaging server on aion (VPN-only, direct access)
+    xmpp.host = "aion";
   };
 }
secrets.nix
@@ -99,7 +99,10 @@ in
   # Others
   "secrets/minica.pem.age".publicKeys = users ++ systems;
   "secrets/shikoku/aria2rpcsecret.age".publicKeys = users ++ [ shikoku ];
-  "secrets/rhea/gandi.env.age".publicKeys = users ++ [ rhea ];
+  "secrets/rhea/gandi.env.age".publicKeys = users ++ [
+    rhea
+    aion # For XMPP ACME DNS-01 challenge
+  ];
   "secrets/rhea/exportarr-sonarr-apikey.age".publicKeys = users ++ [
     rhea
     aion