{ self }: { config, lib, pkgs, ... }: let inherit (lib) mkEnableOption mkIf mkOption types ; cfg = config.services.vibe-proxy; statePath = "/var/lib/${cfg.stateDirectory}"; yaml = pkgs.formats.yaml { }; generatedSettings = lib.recursiveUpdate ( { host = cfg.host; port = cfg.port; auth-dir = "${statePath}/auths"; plugins = { enabled = true; dir = "${statePath}/plugins"; }; } // cfg.settings ) { host = cfg.host; port = cfg.port; }; generatedConfig = yaml.generate "vibe-proxy.yaml" generatedSettings; runtimeConfig = if cfg.configFile == null then "${statePath}/config.yaml" else toString cfg.configFile; in { options.services.vibe-proxy = { enable = mkEnableOption "Vibe Proxy"; package = mkOption { type = types.package; default = self.packages.${pkgs.stdenv.hostPlatform.system}.default; defaultText = lib.literalExpression "inputs.vibe-proxy.packages.${pkgs.stdenv.hostPlatform.system}.default"; description = "Vibe Proxy package to run."; }; user = mkOption { type = types.str; default = "vibe-proxy"; description = "User account under which the service runs."; }; group = mkOption { type = types.str; default = "vibe-proxy"; description = "Group under which the service runs."; }; stateDirectory = mkOption { type = types.str; default = "vibe-proxy"; description = "Directory below /var/lib used for writable service state."; }; host = mkOption { type = types.str; default = "127.0.0.1"; description = "Address on which the API listens."; }; port = mkOption { type = types.port; default = 8317; description = "TCP port on which the API listens."; }; openFirewall = mkOption { type = types.bool; default = false; description = "Whether to open the main API port in the firewall."; }; configFile = mkOption { type = types.nullOr types.str; default = null; description = "Writable external configuration file. When null, settings generate the configuration on every service start."; }; settings = mkOption { type = yaml.type; default = { }; description = "Declarative YAML configuration. Secrets in this option are written to the Nix store; use environmentFiles for secrets."; }; environment = mkOption { type = types.attrsOf types.str; default = { }; description = "Non-secret environment variables for the service."; }; environmentFiles = mkOption { type = types.listOf types.path; default = [ ]; description = "Environment files containing secrets such as MANAGEMENT_PASSWORD."; }; localModel = mkOption { type = types.bool; default = false; description = "Disable remote model catalog updates."; }; extraArgs = mkOption { type = types.listOf types.str; default = [ ]; description = "Additional command-line arguments passed to the server."; }; openOAuthCallbackPorts = mkOption { type = types.bool; default = false; description = "Open the built-in OAuth callback ports 1455, 54545, and 51121."; }; liveMediaRelay = { openFirewall = mkOption { type = types.bool; default = false; description = "Open the configured UDP range for the live-media relay."; }; portRange = mkOption { type = types.submodule { options = { from = mkOption { type = types.port; default = 50000; }; to = mkOption { type = types.port; default = 50100; }; }; }; default = { }; description = "UDP firewall range used by the live-media relay."; }; }; }; config = mkIf cfg.enable { assertions = [ { assertion = cfg.liveMediaRelay.portRange.from <= cfg.liveMediaRelay.portRange.to; message = "services.vibe-proxy.liveMediaRelay.portRange.from must not exceed .to"; } ]; users.groups = mkIf (cfg.group == "vibe-proxy") { vibe-proxy = { }; }; users.users = mkIf (cfg.user == "vibe-proxy") { vibe-proxy = { isSystemUser = true; inherit (cfg) group; home = statePath; }; }; networking.firewall.allowedTCPPorts = lib.optional cfg.openFirewall cfg.port ++ lib.optionals cfg.openOAuthCallbackPorts [ 1455 51121 54545 ]; networking.firewall.allowedUDPPortRanges = lib.optional cfg.liveMediaRelay.openFirewall { inherit (cfg.liveMediaRelay.portRange) from to; }; systemd.services.vibe-proxy = { description = "Vibe Proxy API"; wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; environment = { HOME = statePath; WRITABLE_PATH = statePath; } // cfg.environment; preStart = '' ln -sfn ${cfg.package}/share/vibe-proxy/config.example.yaml ${statePath}/config.example.yaml '' + lib.optionalString (cfg.configFile == null) '' install -m 0600 ${generatedConfig} ${runtimeConfig} ''; serviceConfig = { User = cfg.user; Group = cfg.group; StateDirectory = cfg.stateDirectory; StateDirectoryMode = "0700"; WorkingDirectory = statePath; EnvironmentFile = cfg.environmentFiles; ExecStart = lib.concatStringsSep " " ( [ (lib.getExe cfg.package) "--config" (lib.escapeShellArg runtimeConfig) ] ++ lib.optional cfg.localModel "--local-model" ++ map lib.escapeShellArg cfg.extraArgs ); Restart = "on-failure"; RestartSec = "5s"; UMask = "0077"; NoNewPrivileges = true; PrivateTmp = true; ProtectSystem = "strict"; ProtectHome = true; ReadWritePaths = [ statePath ] ++ lib.optional (cfg.configFile != null) (builtins.dirOf cfg.configFile); }; }; }; }