{ config, pkgs, inputs, ... }: let # Netbird mesh VPN range — dashboard only accessible from mesh peers. # m3-atlas Traefik proxies to this port over Netbird. netbirdRange = "100.64.0.0/16"; # Reference the hermes-agent package from the running service config hermesPkg = config.services.hermes-agent.package or (inputs.hermes-agent.packages.${pkgs.stdenv.hostPlatform.system}.default or pkgs.hermes-agent); in { # ── Hermes Dashboard systemd service ─────────────────────────────────── # Web UI for managing Hermes Agent — sessions, config, kanban, cron, etc. # # Flow: Browser → dash.m3ta.dev (TLS via m3-atlas Traefik) → Netbird → :9119 # # --insecure is required to bind 0.0.0.0 (hermes refuses non-localhost otherwise). # Safe because firewall restricts port 9119 to Netbird mesh only. systemd.services.hermes-dashboard = { description = "Hermes Agent Web Dashboard"; after = ["network.target" "hermes-agent.service"]; wants = ["hermes-agent.service"]; wantedBy = ["multi-user.target"]; serviceConfig = { Type = "simple"; User = "hermes"; Group = "hermes"; ExecStart = "${hermesPkg}/bin/hermes dashboard --host 0.0.0.0 --port 9119 --no-open --insecure"; # Environment matching the hermes-agent service Environment = [ "HERMES_HOME=/var/lib/hermes/.hermes" "HERMES_MANAGED=true" "HOME=/var/lib/hermes" ]; # Security hardening (matching hermes-agent service pattern) NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = "read-only"; ReadWritePaths = ["/var/lib/hermes" "/tmp"]; PrivateTmp = true; # Restart policy Restart = "on-failure"; RestartSec = 5; }; }; # ── Firewall: Dashboard only from Netbird mesh ───────────────────────── networking.firewall = { extraCommands = '' # Allow Hermes Dashboard (9119/tcp) only from Netbird mesh VPN ip46tables -A nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept ''; extraStopCommands = '' ip46tables -D nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true ''; }; }