{ lib }: let validSecret = path: path != null && lib.hasPrefix "/" path && !(lib.hasPrefix "/nix/store/" path) && !(lib.hasInfix "\n" path) && !(lib.hasInfix "\r" path); in { inherit validSecret; dataOption = stateDirectory: lib.mkOption { type = lib.types.addCheck (lib.types.strMatching "/[a-zA-Z0-9_./-]+") ( path: !(lib.hasPrefix "/nix/store/" path) && lib.all (part: part != "" && part != "." && part != "..") (lib.tail (lib.splitString "/" path)) ); default = "/var/lib/${stateDirectory}"; description = "Absolute runtime data directory as a string, not a Nix path. Custom directories use a persistent service user."; }; dataConfig = binary: stateDirectory: dataDirectory: lib.mkIf (dataDirectory != "/var/lib/${stateDirectory}") { users.groups."provenance-${binary}" = { }; users.users."provenance-${binary}" = { isSystemUser = true; group = "provenance-${binary}"; }; systemd.tmpfiles.rules = [ "d ${dataDirectory} 0700 provenance-${binary} provenance-${binary} - -" ]; }; logLevelOption = lib.mkOption { type = lib.types.enum [ "debug" "info" "warn" "error" ]; default = "info"; description = "Minimum structured log level. Debug includes requests and committed replay batches."; }; externalWorkersOption = lib.mkOption { type = lib.types.ints.between 1 64; default = 4; description = "Maximum external HTTP workers in this process. Response reads and retry waits occupy a worker."; }; secretOption = description: lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; inherit description; example = "/run/secrets/provenance/service-key"; }; stateOption = name: lib.mkOption { type = lib.types.strMatching "[a-zA-Z0-9_-]+"; default = name; description = "Name of the private systemd state directory under /var/lib."; }; databaseOption = name: lib.mkOption { type = lib.types.strMatching "[a-zA-Z0-9_-][a-zA-Z0-9._-]*"; default = name; description = "SQLite filename within the managed state directory."; }; # Load only credential paths into the unit. Secret contents enter the process at runtime. unit = { package, binary, stateDirectory, environment, credentials, dataDirectory ? "/var/lib/${stateDirectory}", }: { wantedBy = [ "multi-user.target" ]; wants = [ "network-online.target" ]; after = [ "network-online.target" ]; inherit environment; unitConfig.RequiresMountsFor = [ dataDirectory ]; script = lib.concatMapStringsSep "\n" (name: '' ${ if name == "JETSTREAM_API_KEY_FILE" then ''export ${name}="$CREDENTIALS_DIRECTORY/${name}"'' else ''export ${name}="$(< "$CREDENTIALS_DIRECTORY/${name}")"'' } '') (builtins.attrNames credentials) + '' exec ${package}/bin/${binary} ''; serviceConfig = { LoadCredential = lib.mapAttrsToList (name: path: "${name}:${path}") ( lib.filterAttrs (_: path: path != null) credentials ); DynamicUser = true; StateDirectory = stateDirectory; StateDirectoryMode = "0700"; WorkingDirectory = dataDirectory; UMask = "0077"; Restart = "on-failure"; RestartSec = 5; TimeoutStopSec = 15; NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = true; PrivateTmp = true; PrivateDevices = true; ProtectClock = true; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; RestrictSUIDSGID = true; RestrictRealtime = true; LockPersonality = true; MemoryDenyWriteExecute = true; CapabilityBoundingSet = ""; AmbientCapabilities = ""; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK" ]; } // lib.optionalAttrs (dataDirectory != "/var/lib/${stateDirectory}") { DynamicUser = false; User = "provenance-${binary}"; Group = "provenance-${binary}"; ReadWritePaths = [ dataDirectory ]; }; }; }