{ self, inputs, ... }: { flake.modules.nixos.restic-server = { config, pkgs, lib, ... }: let cfg = config.resticServer; port = builtins.fromJSON (lib.last (lib.splitString ":" cfg.listenAddress)); in { options.resticServer = { enable = lib.mkEnableOption "Enable the RESTic REST server"; dataDir = lib.mkOption { description = "Storage directory for RESTic repositories served by this host."; type = lib.types.str; default = "/mnt/restic"; }; listenAddress = lib.mkOption { description = "Listen address for the RESTic REST server."; type = lib.types.str; default = "0.0.0.0:8080"; }; privateRepos = lib.mkOption { description = "Whether the RESTic server should use private repository mode."; type = lib.types.bool; default = true; }; extraFlags = lib.mkOption { description = "Additional flags to pass to the RESTic REST server before TLS flags are appended."; type = lib.types.listOf lib.types.str; default = [ "--no-auth" ]; }; tls = { certFile = lib.mkOption { description = "Path to the TLS certificate file for the RESTic REST server, or null to disable TLS."; type = lib.types.nullOr lib.types.str; default = null; }; keyFile = lib.mkOption { description = "Path to the TLS private key file for the RESTic REST server, or null to disable TLS."; type = lib.types.nullOr lib.types.str; default = null; }; }; }; config = lib.mkIf cfg.enable { networking.firewall.allowedTCPPorts = [ port ]; services.restic.server = { enable = true; inherit (cfg) dataDir listenAddress privateRepos; extraFlags = cfg.extraFlags ++ lib.optionals (cfg.tls.certFile != null && cfg.tls.keyFile != null) [ "--tls" "--tls-cert=${cfg.tls.certFile}" "--tls-key=${cfg.tls.keyFile}" ]; }; }; }; flake.modules.homeManager.restic = { config, pkgs, lib, ... }: let cfg = config.restic; in { options.restic = { repoName = lib.mkOption { description = "Name of the restic repo to use"; type = lib.types.str; default = "john-ubuntu"; }; repoUrl = lib.mkOption { description = "URL to the REST endpoint"; type = lib.types.str; default = "rest:https://soteria.john-stream.com/${cfg.repoName}"; }; passwordFile = lib.mkOption { description = "String path to the restic password file"; type = lib.types.str; }; paths = lib.mkOption { description = "List of string paths to include in the backup"; type = lib.types.listOf lib.types.str; default = [ ]; }; exclude = lib.mkOption { description = "List of string paths to include in the backup. There are already some common ones included by default."; type = lib.types.listOf lib.types.str; default = [ ]; }; OnCalendar = lib.mkOption { description = ""; type = lib.types.str; }; RandomizedDelaySec = lib.mkOption { description = ""; type = lib.types.str; default = "1m"; }; }; config = { home.sessionVariables = { RESTIC_REPOSITORY = cfg.repoUrl; RESTIC_PASSWORD_FILE = cfg.passwordFile; RESTIC_CACERT = config.mtls.caFile; RESTIC_TLS_CLIENT_CERT = config.mtls.bundleFile; }; # This is necessary because the restic service in home manager doesn't otherwise expose these options. systemd.user.services."restic-backups-${cfg.repoName}".Service.Environment = [ "RESTIC_CACERT=${config.mtls.caFile}" "RESTIC_TLS_CLIENT_CERT=${config.mtls.bundleFile}" ]; services.restic = { enable = true; backups.${cfg.repoName} = { repository = cfg.repoUrl; passwordFile = cfg.passwordFile; paths = cfg.paths; timerConfig = { OnCalendar = cfg.OnCalendar; RandomizedDelaySec = cfg.RandomizedDelaySec; Persistent = true; }; runCheck = true; pruneOpts = [ "--keep-last 10" "--keep-hourly 8" "--keep-daily 14" "--keep-weekly 8" "--keep-monthly 12" ]; exclude = cfg.exclude ++ [ ".cache" ".devenv" ".rustup" ".cargo" ".venv" ".pyenv" ".vscode*" "data/postgres" "build" "dist" "__pycache__" "*.log" "*.egg-info" "*.csv" "*.m4a" ".local/share/Steam" ".local/share/Trash" ]; }; }; }; }; }