From 93abd4c3eae0c9419103b7e8a7d2f5c49ea82ae3 Mon Sep 17 00:00:00 2001 From: Spencer Brower Date: Tue, 13 Oct 2026 17:00:45 -0400 Subject: [PATCH] feat: Added nixos module. --- README.md | 27 +++++++++++ flake.nix | 2 + nixos-module.nix | 121 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 nixos-module.nix diff --git a/README.md b/README.md index 098d652..8379168 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,33 @@ run the built binary (`result/libexec/omnisearch`) from a directory containing Docker also builds from `vendor/beaker`; non-Nix local builds still require Beaker to be installed separately. +### NixOS service + +Import the module from this flake and enable the service: + +```nix +{ + imports = [ omnisearch.nixosModules.default ]; + + services.omnisearch = { + enable = true; + openFirewall = true; + settings = { + server = { host = "127.0.0.1"; port = 8087; }; + engines.engines = "*"; + rate_limit = { search_requests = 10; search_interval = 60; }; + }; + }; +} +``` + +The module manages a system user, systemd service, persistent cache/state +directories and the asset links needed by OmniSearch. `settings` uses INI +sections and keys matching `example-config.ini`; `extraConfig` appends raw INI +text after those generated settings. Both are embedded in the world-readable +Nix store, so do not put credentials or other secrets in them. The firewall +remains closed unless `openFirewall` is enabled. + ## Deploy with Docker Compose You need Docker or Podman and Docker Compose installed on your system. diff --git a/flake.nix b/flake.nix index 0e629dc..daec969 100644 --- a/flake.nix +++ b/flake.nix @@ -81,5 +81,7 @@ ''; }; }); + + nixosModules.default = import ./nixos-module.nix { inherit self; }; }; } diff --git a/nixos-module.nix b/nixos-module.nix new file mode 100644 index 0000000..2bc5b41 --- /dev/null +++ b/nixos-module.nix @@ -0,0 +1,121 @@ +{ self }: +{ config, lib, pkgs, ... }: +let + inherit (lib) mkEnableOption mkIf mkOption types; + cfg = config.services.omnisearch; + generatedConfig = pkgs.writeText "omnisearch-config.ini" '' + ${lib.generators.toINI {} cfg.settings} + ${cfg.extraConfig} + ''; + assetDir = "${cfg.package}/share/omnisearch"; +in +{ + options.services.omnisearch = { + enable = mkEnableOption "OmniSearch metasearch server"; + + package = mkOption { + type = types.package; + default = self.packages.${pkgs.stdenv.hostPlatform.system}.default; + defaultText = lib.literalExpression "packages.${pkgs.stdenv.hostPlatform.system}.default"; + description = "OmniSearch package to run."; + }; + + user = mkOption { + type = types.str; + default = "omnisearch"; + description = "User account under which OmniSearch runs."; + }; + + group = mkOption { + type = types.str; + default = "omnisearch"; + description = "Group under which OmniSearch runs."; + }; + + settings = mkOption { + type = types.attrsOf (types.attrsOf (types.oneOf [ types.str types.int types.bool ])); + default = { + server = { + host = "0.0.0.0"; + port = 8087; + }; + cache.dir = "/var/cache/omnisearch"; + engines.engines = "*"; + }; + example = { + server = { host = "127.0.0.1"; port = 8087; }; + engines.engines = "ddg,startpage"; + rate_limit = { search_requests = 10; search_interval = 60; }; + }; + description = '' + INI settings grouped by section. Section and key names follow + `example-config.ini`; values are written as strings, integers or + booleans. The generated INI is stored in the world-readable Nix store, + so do not put secrets here. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + [proxy] + proxy = "socks5://127.0.0.1:9050" + ''; + description = '' + Additional raw INI text appended after the generated `settings`. + This text is also embedded in the world-readable Nix store; avoid + including secrets. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Whether to open the configured TCP port in the firewall."; + }; + }; + + config = mkIf cfg.enable { + users.groups.${cfg.group} = {}; + users.users.${cfg.user} = { + isSystemUser = true; + group = cfg.group; + description = "OmniSearch service user"; + }; + + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ + (cfg.settings.server.port or 8087) + ]; + + systemd.services.omnisearch = { + description = "OmniSearch metasearch server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + wants = [ "network-online.target" ]; + serviceConfig = { + Type = "simple"; + User = cfg.user; + Group = cfg.group; + StateDirectory = "omnisearch"; + CacheDirectory = "omnisearch"; + WorkingDirectory = "/var/lib/omnisearch"; + ExecStartPre = pkgs.writeShellScript "omnisearch-prepare" '' + ln -sfn ${lib.escapeShellArg "${assetDir}/templates"} templates + ln -sfn ${lib.escapeShellArg "${assetDir}/static"} static + ln -sfn ${lib.escapeShellArg "${assetDir}/locales"} locales + ln -sfn ${lib.escapeShellArg (toString generatedConfig)} config.ini + ''; + ExecStart = "${cfg.package}/bin/omnisearch"; + Restart = "on-failure"; + RestartSec = "5s"; + PrivateTmp = true; + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + ReadWritePaths = [ "/var/lib/omnisearch" "/var/cache/omnisearch" ]; + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; + }; + }; + }; +}