{ config, lib, pkgs, ... }: let cfg = config.services.sift; settingsFormat = pkgs.formats.toml { }; in { options.services.sift = { enable = lib.mkEnableOption "sift LLM-oriented fetch and search"; package = lib.mkOption { type = lib.types.package; description = "sift package. The flake module sets this to packages.sift."; }; listen = lib.mkOption { type = lib.types.str; default = "127.0.0.1:8890"; description = "Address the HTTP server binds to (loopback by default)."; }; tailscaleServe = lib.mkOption { type = lib.types.bool; default = true; description = "Advertise the listener on tailscale serve at https://:8443."; }; tailscaleHttpsPort = lib.mkOption { type = lib.types.port; default = 8443; description = "HTTPS port for tailscale serve (443 is SearXNG on orion)."; }; denyPrefixes = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = '' Extra CIDRs the SSRF guard blocks at pre-check and connect time. Put home WAN /32s here (hairpin into LAN). Set on the host; do not commit residential IPs to the sift repo. ''; }; settings = lib.mkOption { type = settingsFormat.type; default = { }; description = "Written to /etc/sift/config.toml. Unused keys are reserved for P1+."; }; }; config = lib.mkIf cfg.enable { assertions = [ { assertion = cfg.package != null; message = "services.sift.package must be set (the flake module does this)."; } ]; users.users.sift = { isSystemUser = true; group = "sift"; home = "/var/lib/sift"; }; users.groups.sift = { }; environment.etc."sift/config.toml".source = settingsFormat.generate "sift.toml" ( { listen = cfg.listen; } // cfg.settings ); # Keys and env secrets are created on-box, never in the store. systemd.tmpfiles.rules = [ "d /var/lib/sift 0750 root sift -" ]; system.activationScripts.sift-secrets = { deps = [ "users" ]; text = '' install -d -m 0750 -o root -g sift /var/lib/sift if [ ! -f /var/lib/sift/keys.toml ]; then umask 027 printf '%s\n' '# key-hash → { name, workspace }. Generated empty; fill on-box.' > /var/lib/sift/keys.toml chown root:sift /var/lib/sift/keys.toml chmod 0640 /var/lib/sift/keys.toml fi if [ ! -f /var/lib/sift/secret.env ]; then umask 027 printf '%s\n' '# BRAVE_API_KEY= (set on-box; do not read SearXNG secret.env)' > /var/lib/sift/secret.env chown root:sift /var/lib/sift/secret.env chmod 0640 /var/lib/sift/secret.env fi ''; }; systemd.services.sift = { description = "sift LLM-oriented fetch and search"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "simple"; User = "sift"; Group = "sift"; ExecStart = "${lib.getExe cfg.package} -listen ${cfg.listen} -config /etc/sift/config.toml" + lib.concatMapStrings (p: " -deny-prefix ${lib.escapeShellArg p}") cfg.denyPrefixes; WorkingDirectory = "/var/lib/sift"; Restart = "on-failure"; RestartSec = "5s"; NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = true; PrivateTmp = true; PrivateDevices = true; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; ProtectHostname = true; ProtectClock = true; RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; RestrictNamespaces = true; RestrictRealtime = true; RestrictSUIDSGID = true; LockPersonality = true; MemoryDenyWriteExecute = true; SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" "~@privileged" ]; CapabilityBoundingSet = ""; AmbientCapabilities = ""; ReadWritePaths = [ "/var/lib/sift" ]; ConfigurationDirectory = "sift"; StateDirectory = "sift"; }; }; systemd.services.tailscale-serve-sift = lib.mkIf cfg.tailscaleServe { description = "Advertise sift on Tailscale Serve :${toString cfg.tailscaleHttpsPort}"; after = [ "tailscaled.service" "network-online.target" "sift.service" ]; wants = [ "tailscaled.service" "network-online.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; Restart = "on-failure"; RestartSec = "5s"; ExecStart = "${lib.getExe pkgs.tailscale} serve --bg --yes --https=${toString cfg.tailscaleHttpsPort} http://${cfg.listen}"; }; }; }; }