{ config, lib, pkgs, ... }: let cfg = config.homelab.services.attic; in { options.homelab.services.attic = { enable = lib.mkEnableOption "Attic binary cache server"; domain = lib.mkOption { type = lib.types.str; default = "nix-cache.depeuter.dev"; description = "The domain name for the Attic server."; }; port = lib.mkOption { type = lib.types.port; default = 8080; description = "The port Attic server listens on."; }; databaseName = lib.mkOption { type = lib.types.str; default = "attic"; description = "The name of the PostgreSQL database."; }; dbContainerName = lib.mkOption { type = lib.types.str; default = "attic-db"; description = "The name of the PostgreSQL container."; }; storagePath = lib.mkOption { type = lib.types.str; default = "/var/lib/atticd/storage"; description = "The path where Attic store's its blobs."; }; openFirewall = lib.mkOption { type = lib.types.bool; default = false; description = "Whether to open the firewall port for Attic."; }; enableRemoteBuilder = lib.mkOption { type = lib.types.bool; default = false; description = "Whether to enable remote build capabilities on this host."; }; }; config = lib.mkIf cfg.enable { sops.secrets = { "attic/db-password" = { }; "attic/server-token-secret" = { }; }; services.atticd = { enable = true; environmentFile = config.sops.secrets."attic/server-token-secret".path; settings = { listen = "[::]:${toString cfg.port}"; allowed-hosts = [ cfg.domain ]; api-endpoint = "https://${cfg.domain}/"; database.url = "postgresql://${cfg.databaseName}@${cfg.dbContainerName}:5432/${cfg.databaseName}"; storage = { type = "local"; path = cfg.storagePath; }; chunking = { min-size = 16384; # 16 KiB avg-size = 65536; # 64 KiB max-size = 262144; # 256 KiB }; }; }; homelab.virtualisation.containers.enable = true; virtualisation.oci-containers.containers."${cfg.dbContainerName}" = { image = "postgres:15-alpine"; autoStart = true; # We still map it to host for Attic (running on host) to connect to it via bridge IP or name # if we set up networking/DNS correctly. ports = [ "5432:5432/tcp" ]; environment = { POSTGRES_USER = cfg.databaseName; POSTGRES_PASSWORD_FILE = config.sops.secrets."attic/db-password".path; POSTGRES_DB = cfg.databaseName; }; volumes = [ "attic-db:/var/lib/postgresql/data" ]; }; # Map the container name to localhost if Attic is on the host networking.extraHosts = '' 127.0.0.1 ${cfg.dbContainerName} ''; networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ]; # Remote build host configuration nix.settings.trusted-users = lib.mkIf cfg.enableRemoteBuilder [ "root" "@wheel" "builder" ]; users.users.builder = lib.mkIf cfg.enableRemoteBuilder { isNormalUser = true; group = "builder"; openssh.authorizedKeys.keys = [ # Placeholders - user should provide actual keys "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFrp6aM62Bf7bj1YM5AlAWuNrANU3N5e8+LtbbpmZPKS" ]; }; users.groups.builder = lib.mkIf cfg.enableRemoteBuilder {}; # Only open SSH if remote builder is enabled services.openssh.ports = lib.mkIf cfg.enableRemoteBuilder [ 22 ]; networking.firewall.allowedTCPPorts = lib.mkIf cfg.enableRemoteBuilder [ 22 ]; }; }