{ config, lib, pkgs, ... }: let cfg = config.services.k8shost; isLocalHttpEndpoint = value: value != null && ( lib.hasPrefix "http://127.0.0.1:" value || lib.hasPrefix "http://localhost:" value || lib.hasPrefix "http://[::1]:" value || lib.hasPrefix "https://127.0.0.1:" value || lib.hasPrefix "https://localhost:" value || lib.hasPrefix "https://[::1]:" value ); isLocalHostPort = value: value != null && ( lib.hasPrefix "127.0.0.1:" value || lib.hasPrefix "localhost:" value || lib.hasPrefix "[::1]:" value ); flaredbUsesExplicitRemote = (cfg.flaredbPdAddr != null && !(isLocalHostPort cfg.flaredbPdAddr)) || (cfg.flaredbDirectAddr != null && !(isLocalHostPort cfg.flaredbDirectAddr)); localServiceDeps = lib.optional ( (config.services.iam.enable or false) && (cfg.iamAddr == null || isLocalHttpEndpoint cfg.iamAddr) ) "iam.service" ++ lib.optional ( (config.services.creditservice.enable or false) && (cfg.creditserviceAddr == null || isLocalHttpEndpoint cfg.creditserviceAddr) ) "creditservice.service" ++ lib.optional ( (config.services.chainfire.enable or false) && cfg.chainfireAddr != null && isLocalHttpEndpoint cfg.chainfireAddr ) "chainfire.service" ++ lib.optional ( (config.services.prismnet.enable or false) && (cfg.prismnetAddr == null || isLocalHttpEndpoint cfg.prismnetAddr) ) "prismnet.service" ++ lib.optional ( (config.services.flashdns.enable or false) && (cfg.flashdnsAddr == null || isLocalHttpEndpoint cfg.flashdnsAddr) ) "flashdns.service" ++ lib.optional ( (config.services.fiberlb.enable or false) && (cfg.fiberlbAddr == null || isLocalHttpEndpoint cfg.fiberlbAddr) ) "fiberlb.service" ++ lib.optional ( (config.services.flaredb.enable or false) && !flaredbUsesExplicitRemote ) "flaredb.service"; tomlFormat = pkgs.formats.toml { }; generatedConfig = { server = { addr = "0.0.0.0:${toString cfg.port}"; http_addr = "127.0.0.1:${toString cfg.httpPort}"; log_level = "info"; }; flaredb = { pd_addr = cfg.flaredbPdAddr; direct_addr = cfg.flaredbDirectAddr; }; chainfire = { endpoint = cfg.chainfireAddr; }; iam = { server_addr = if cfg.iamAddr != null then cfg.iamAddr else "http://127.0.0.1:50080"; }; creditservice = lib.optionalAttrs ( cfg.creditserviceAddr != null || ((config.services ? creditservice) && config.services.creditservice.enable) ) { server_addr = if cfg.creditserviceAddr != null then cfg.creditserviceAddr else "http://127.0.0.1:${toString config.services.creditservice.grpcPort}"; }; fiberlb = { server_addr = if cfg.fiberlbAddr != null then cfg.fiberlbAddr else "http://127.0.0.1:50085"; }; flashdns = { server_addr = if cfg.flashdnsAddr != null then cfg.flashdnsAddr else "http://127.0.0.1:50084"; }; prismnet = { server_addr = if cfg.prismnetAddr != null then cfg.prismnetAddr else "http://127.0.0.1:50081"; }; }; configFile = tomlFormat.generate "k8shost.toml" (lib.recursiveUpdate generatedConfig cfg.settings); configPath = "/etc/k8shost/k8shost.toml"; in { options.services.k8shost = { enable = lib.mkEnableOption "k8shost service (tenant pod and service control plane only; standalone from fleet-scheduler and base OS rollout)"; port = lib.mkOption { type = lib.types.port; default = 50087; description = "Port for k8shost gRPC API server"; }; httpPort = lib.mkOption { type = lib.types.port; default = 8085; description = "Port for k8shost HTTP REST API server"; }; iamAddr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "IAM service endpoint address (http://host:port)"; example = "http://10.0.0.1:50080"; }; creditserviceAddr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "CreditService endpoint address (http://host:port) for pod admission and quota enforcement. Native host-service placement remains the fleet-scheduler path."; example = "http://10.0.0.1:3010"; }; chainfireAddr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "ChainFire endpoint address (http://host:port) for tenant workload coordination; k8shost does not own host-native service placement."; example = "http://10.0.0.1:2379"; }; prismnetAddr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "PrismNET service endpoint address (http://host:port)"; example = "http://10.0.0.1:50081"; }; flaredbPdAddr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "FlareDB Placement Driver address (host:port)"; example = "10.0.0.1:2479"; }; flaredbDirectAddr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "FlareDB direct address (host:port)"; example = "10.0.0.1:50052"; }; fiberlbAddr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "FiberLB service endpoint address (http://host:port)"; example = "http://10.0.0.1:50085"; }; flashdnsAddr = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "FlashDNS service endpoint address (http://host:port)"; example = "http://10.0.0.1:50084"; }; dataDir = lib.mkOption { type = lib.types.path; default = "/var/lib/k8shost"; description = "Data directory for k8shost"; }; settings = lib.mkOption { type = lib.types.attrs; default = {}; description = "Additional configuration settings"; }; package = lib.mkOption { type = lib.types.package; default = pkgs.k8shost-server or (throw "k8shost-server package not found"); description = "Package to use for k8shost"; }; }; config = lib.mkIf cfg.enable { # Create system user users.users.k8shost = { isSystemUser = true; group = "k8shost"; description = "K8shost Kubernetes Hosting Service user"; home = cfg.dataDir; }; users.groups.k8shost = {}; # Create systemd service systemd.services.k8shost = { description = "K8shost Kubernetes Hosting Service (tenant workload control plane only)"; wantedBy = [ "multi-user.target" ]; wants = [ "network-online.target" ] ++ localServiceDeps; after = [ "network-online.target" ] ++ localServiceDeps; requires = localServiceDeps; serviceConfig = { Type = "simple"; User = "k8shost"; Group = "k8shost"; Restart = "on-failure"; RestartSec = "10s"; # State directory management StateDirectory = "k8shost"; StateDirectoryMode = "0750"; # Security hardening NoNewPrivileges = true; PrivateTmp = true; ProtectSystem = "strict"; ProtectHome = true; ReadWritePaths = [ cfg.dataDir ]; ExecStart = "${cfg.package}/bin/k8shost-server --config ${configPath}"; }; }; environment.etc."k8shost/k8shost.toml".source = configFile; }; }