# Helper module to configure multiple services with onion and clearnet access, as well as optional backups. # Each service can specify its own URL, onion settings, data directory, and backup options. { config, lib, pkgs, ... }: let cfg = config.distrust.services; backup_cfg = config.distrust.backups; in { options = { distrust = { services = lib.mkOption { description = "Services configuration map"; type = lib.types.attrsOf ( lib.types.submodule { options = { url = lib.mkOption { description = "Clearnet URL"; type = lib.types.str; }; dataDir = lib.mkOption { description = "Path to stateful storage for service"; type = lib.types.nullOr lib.types.path; }; onion = lib.mkOption { description = "Onion service settings"; type = lib.types.submodule { options = { url = lib.mkOption { description = ".onion URL"; type = lib.types.str; }; secretKey = lib.mkOption { description = "Path to onion secret key file"; type = lib.types.path; }; }; }; default = {}; }; backup = lib.mkOption { description = "Backup configuration"; type = lib.types.submodule { options = { enable = lib.mkOption { description = "Enable backups for this service"; type = lib.types.bool; default = false; }; paths = lib.mkOption { description = "Paths to back up"; type = lib.types.listOf lib.types.str; default = []; }; database = lib.mkOption { description = "Database to back up"; type = lib.types.nullOr lib.types.str; default = null; }; }; }; default = {}; }; virtualHostConfig = lib.mkOption { description = "Caddy virtual host config"; type = lib.types.str; }; }; } ); default = {}; }; backups = lib.mkOption { description = "Global backup configuration"; type = lib.types.submodule { options = { borgRepository = lib.mkOption { description = "Borg backup repository URL"; type = lib.types.str; }; borgSSHKey = lib.mkOption { description = "Path to SSH key for Borg backup repository"; type = lib.types.path; }; borgPassCommand = lib.mkOption { description = "Command to retrieve Borg repository passphrase"; type = lib.types.str; }; }; }; }; }; }; config = { services.tor.relay.onionServices = builtins.foldl' (acc: key: acc // { "${key}" = { map = [80]; inherit (cfg.${key}.onion) secretKey; }; }) {} (builtins.attrNames cfg); services.caddy = { enable = true; virtualHosts = builtins.foldl' (acc: key: let site = cfg.${key}; vhostKey = "${site.url} ${site.onion.url}"; extraCfg = '' ${site.virtualHostConfig or ""} header Onion-Location ${site.onion.url} ''; in acc // { "${vhostKey}" = { extraConfig = extraCfg; }; }) {} (builtins.attrNames cfg); services.borgbackup.jobs = builtins.foldl' (acc: key: let site = cfg.${key}; dump = site.backup.database; paths = builtins.concatLists [ site.backup.paths (if dump != null then [ "/var/backup/${key}.sql" ] else []) ]; preHook = lib.mkIf (dump != null) '' mkdir -p /var/backup ${pkgs.sudo}/bin/sudo -u postgres pg_dump ${dump} > /var/backup/postgres/${key}.sql ''; postHook = lib.mkIf (dump != null) '' rm -f /var/backup/postgres/${key}.sql ''; in acc // lib.mkIf site.backup.enable { "${key}" = { repo = backup_cfg.borgRepository + "/./${key}"; environment = { BORG_RSH = "ssh -i ${backup_cfg.borgSSHKey} -o 'StrictHostKeyChecking=no'"; }; inherit paths; encryption = { mode = "keyfile"; passCommand = backup_cfg.borgPassCommand; }; compression = "auto,lzma"; startAt = "daily"; prune.keep = { daily = 7; weekly = 4; monthly = -1; }; inherit preHook; inherit postHook; }; }) {} (builtins.attrNames cfg); systemd.tmpfiles.settings = { "99-borgdatabasebackups"."/var/backup/postgres".d = { user = "root"; group = "root"; mode = "0755"; }; }; }; }; }