{ pkgs, lib, config, ... }: let cfg = config.services.tvix-binary-cache; settingsFormat = pkgs.formats.toml { }; systemdHardening = { PrivateDevices = true; PrivateTmp = true; ProtectControlGroups = true; ProtectKernelTunables = true; RestrictSUIDSGID = true; ProtectSystem = "strict"; ProtectKernelLogs = true; ProtectProc = "invisible"; PrivateUsers = true; ProtectHome = true; UMask = "0077"; RuntimeDirectoryMode = "0750"; StateDirectoryMode = "0750"; }; in { imports = [ ./nginx.nix ]; options = { services.tvix-binary-cache = { enable = lib.mkEnableOption "BinaryCache using tvix ca-store"; castoreDir = lib.mkOption { type = lib.types.nullOr lib.types.str; default = "/var/lib/castore"; }; caches = lib.mkOption { type = lib.types.attrsOf ( lib.types.submodule ( { name, ... }@cacheAttrs: { options = { grpcListenAddress = lib.mkOption { type = lib.types.str; }; narBridgeListenAddress = lib.mkOption { type = lib.types.str; }; common-composition = lib.mkOption { inherit (settingsFormat) type; }; nar-bridge-composition = lib.mkOption { inherit (settingsFormat) type; }; tvix-daemon-composition = lib.mkOption { inherit (settingsFormat) type; }; name = lib.mkOption { type = lib.types.str; description = "Name of the cache"; default = name; defaultText = lib.literalMD "Defaults to attribute name in services.tvix-binary-cache.caches"; }; remote-path-info-service-addr = lib.mkOption { type = with lib.types; nullOr str; description = "Upstream cache to substitute from if nothing in "; example = "nix+https://cache.nixos.org?trusted-public-keys=cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="; default = null; }; }; config = { common-composition = { blobservices.default = { type = "objectstore"; object_store_url = "file://${cfg.castoreDir}/blobs.object-store"; object_store_options = { }; }; directoryservices = { objectstore = { type = "objectstore"; object_store_url = "file://${cfg.castoreDir}/directories.object-store"; object_store_options = { }; }; memory = { type = "memory"; }; cache = { type = "cache"; near = "memory"; far = "objectstore"; }; default = { type = "router"; writes = "objectstore"; reads = "cache"; }; }; }; nar-bridge-composition = lib.mkMerge [ cacheAttrs.config.common-composition { pathinfoservices.default = { type = "grpc"; url = "grpc+http://${ lib.replaceStrings [ "[::]" "0.0.0.0" ] [ "[::1]" "127.0.0.1" ] cacheAttrs.config.grpcListenAddress }"; }; } ]; tvix-daemon-composition = lib.mkMerge [ cacheAttrs.config.common-composition { pathinfoservices.default = { type = "redb"; is_temporary = false; path = "/var/lib/tvix-daemon-${name}/pathinfos.redb"; }; } ]; }; } ) ); }; }; }; config = lib.mkIf cfg.enable { environment.systemPackages = [ pkgs.tvix ]; users.users.tvix-castore = { isSystemUser = true; group = "tvix-castore"; }; users.groups.tvix-castore = { }; systemd.tmpfiles.rules = [ "d ${cfg.castoreDir} 770 root tvix-castore -" ]; systemd.services = lib.mkMerge ( (lib.singleton { }) ++ (lib.mapAttrsToList (name: cache: { "tvix-daemon-${cache.name}" = { environment = { EXPERIMENTAL_STORE_COMPOSITION = settingsFormat.generate "Config.toml" cache.tvix-daemon-composition; }; serviceConfig = { UMask = "007"; ExecStart = "${pkgs.tvix}/bin/tvix-store --otlp=false daemon --listen-address=\"${cache.grpcListenAddress}\""; StateDirectory = "tvix-daemon-${cache.name}"; RuntimeDirectory = "tvix-daemon-${cache.name}"; User = "tvix-castore"; Group = "tvix-castore"; ReadWritePaths = cfg.castoreDir; } // systemdHardening; }; "narbridge-${cache.name}" = { wantedBy = [ "multi-user.target" ]; wants = [ "tvix-daemon-${cache.name}.service" ]; after = [ "tvix-daemon-${cache.name}.service" ]; environment = { EXPERIMENTAL_STORE_COMPOSITION = settingsFormat.generate "Config.toml" cache.nar-bridge-composition; }; serviceConfig = { UMask = "007"; ExecStart = "${pkgs.tvix}/bin/nar-bridge --otlp=false --listen-address=\"${cache.narBridgeListenAddress}\""; User = "tvix-castore"; Group = "tvix-castore"; RuntimeDirectory = "narbridge-${cache.name}"; ReadWritePaths = cfg.castoreDir; } // systemdHardening; }; }) cfg.caches) ); }; }