Commit b64e897b5776

Vincent Demeester <vincent@sbr.pm>
2026-02-10 15:16:27
feat: add SearXNG metasearch engine on aomi
Deployed SearXNG as a private, API-focused metasearch engine for Pi agent consumption. Configured with curated search engines, JSON API support, and Traefik routing via search.sbr.pm on rhea. Added agenix-managed secret key and DNS auto-registration through globals.services.
1 parent acdc710
secrets/aomi/searxng-secret-key.age
Binary file
systems/aomi/extra.nix
@@ -63,6 +63,12 @@
   '';
 
   # Age secrets
+  age.secrets."searxng-secret-key" = {
+    file = ../../secrets/aomi/searxng-secret-key.age;
+    mode = "400";
+    owner = "searx";
+    group = "searx";
+  };
   age.secrets."ntfy-token" = {
     file = ../../secrets/sakhalin/ntfy-token.age;
     mode = "440";
@@ -233,6 +239,84 @@
     };
   };
 
+  # SearXNG metasearch engine (private, API-focused for Pi agent)
+  services.searx = {
+    enable = true;
+    environmentFile = config.age.secrets."searxng-secret-key".path;
+    settings = {
+      use_default_settings = true;
+      server = {
+        port = 8888;
+        bind_address = "0.0.0.0";
+        secret_key = "$SEARXNG_SECRET_KEY";
+        limiter = false; # Private instance, no rate limiting needed
+        image_proxy = false;
+      };
+      search = {
+        safe_search = 0;
+        autocomplete = "";
+        default_lang = "en";
+        formats = [
+          "html"
+          "json"
+        ];
+      };
+      # Curated engines for quality results
+      engines = [
+        {
+          name = "duckduckgo";
+          engine = "duckduckgo";
+          shortcut = "ddg";
+          disabled = false;
+        }
+        {
+          name = "google";
+          engine = "google";
+          shortcut = "g";
+          disabled = false;
+        }
+        {
+          name = "brave";
+          engine = "brave";
+          shortcut = "br";
+          disabled = false;
+        }
+        {
+          name = "wikipedia";
+          engine = "wikipedia";
+          shortcut = "wp";
+          disabled = false;
+        }
+        {
+          name = "github";
+          engine = "github";
+          shortcut = "gh";
+          disabled = false;
+        }
+        {
+          name = "stackoverflow";
+          engine = "stackoverflow";
+          shortcut = "so";
+          disabled = false;
+        }
+        {
+          name = "arch wiki";
+          engine = "arch_linux_wiki";
+          shortcut = "aw";
+          disabled = false;
+        }
+        {
+          name = "nixos wiki";
+          engine = "mediawiki";
+          shortcut = "nw";
+          disabled = false;
+          base_url = "https://wiki.nixos.org/";
+          search_type = "text";
+        }
+      ];
+    };
+  };
+
   services = {
     logind.settings.Login = {
       HandleLidSwitch = "ignore";
@@ -367,8 +451,11 @@
     };
   };
 
-  # Open firewall for Ollama exporter
-  networking.firewall.allowedTCPPorts = [ 8000 ];
+  # Open firewall for Ollama exporter + SearXNG
+  networking.firewall.allowedTCPPorts = [
+    8000
+    8888
+  ];
 
   # Builder user for remote builds
   users.users.builder = {
systems/rhea/extra.nix
@@ -346,6 +346,11 @@ in
                   "ollama.sbr.pm"
                   "llm.sbr.pm"
                 ];
+                # SearXNG metasearch engine on aomi
+                search = mkRouter "search" [
+                  "search.sbr.pm"
+                  "searxng.sbr.pm"
+                ];
                 # Traefik dashboard
                 traefik-dashboard = {
                   rule = "Host(`traefik.sbr.pm`)";
@@ -369,6 +374,7 @@ in
                 audiobookshelf = mkService "http://${builtins.head globals.machines.aion.net.ips}:13378";
                 lidarr = mkService "http://${builtins.head globals.machines.aion.net.ips}:8686";
                 ollama = mkService "http://${builtins.head globals.machines.aomi.net.ips}:8000";
+                search = mkService "http://${builtins.head globals.machines.aomi.net.ips}:8888";
               };
             middlewares =
               syncthingMiddlewares
globals.nix
@@ -619,6 +619,11 @@ _: {
       host = "rhea";
       aliases = [ "llm" ];
     };
+    # SearXNG metasearch engine on aomi (routed through rhea/traefik)
+    search = {
+      host = "rhea";
+      aliases = [ "searxng" ];
+    };
     # XMPP messaging server on aion (VPN-only, direct access)
     xmpp.host = "aion";
   };
secrets.nix
@@ -145,6 +145,7 @@ in
   "secrets/aion/restic-aix-password.age".publicKeys = users ++ [ aion ];
   "secrets/aomi/xmpp-research-bot-password.age".publicKeys = users ++ [ aomi ];
   "secrets/aomi/gemini-api-key.age".publicKeys = users ++ [ aomi ];
+  "secrets/aomi/searxng-secret-key.age".publicKeys = users ++ [ aomi ];
   "secrets/rhea/restic-aix-password.age".publicKeys = users ++ [ rhea ];
 
   # Harmonia binary cache signing keys