{ pkgs, lib, config, ... }: let cfg = config.services.syncthing; service = "syncthing"; secretsDir = "/etc/secrets/${service}"; password = { path = "syncthing/${config.frogeye.name}"; selector = "@"; generator = ''(t="$(mktemp -d)" && ${lib.getExe pkgs.syncthing} generate --home="$t" &> /dev/null && cat "$t"/{cert,key}.pem && rm -rf "$t")''; }; capitalizeFirstLetter = str: (lib.strings.toUpper (builtins.substring 0 1 str)) + (builtins.substring 1 (builtins.stringLength str) str); nixosDevices = builtins.map (system: system.config.frogeye) ( builtins.attrValues config.frogeye.toplevel.nixosConfigurations ); allDevices = nixosDevices; syncingDevices = builtins.filter (device: device.syncthing.id != null) allDevices; peerDevices = builtins.filter ( device: device.syncthing.id != config.frogeye.syncthing.id ) syncingDevices; # Can't use the module's folders enable option, as it still requests things somehow allFolders = builtins.attrValues config.frogeye.folders; syncedFolders = builtins.filter (folder: folder.syncthing.enable) allFolders; folderShouldSyncWith = folder: device: (lib.hasAttrByPath [ folder.name ] device.folders) && device.folders.${folder.name}.syncthing.enable; enable = (builtins.length syncedFolders) > 0; in { config = { # Allow to export configuration to other systems system.build.syncthingConfig = { folders = lib.trivial.pipe syncedFolders [ (builtins.map (folder: { name = folder.name; value = folder; })) builtins.listToAttrs (lib.attrsets.mapAttrs ( folderName: folder: (lib.attrsets.filterAttrs ( k: v: builtins.elem k [ "label" "path" "syncthing" "user" ] )) folder )) ]; devices = lib.trivial.pipe syncingDevices [ (builtins.map (device: { name = device.name; value = device; })) builtins.listToAttrs (lib.attrsets.mapAttrs ( deviceName: device: { folders = lib.trivial.pipe device.folders [ (lib.attrsets.filterAttrs (folderName: folder: folder.syncthing.enable)) (lib.attrsets.mapAttrs (folderName: folder: { syncthing.enable = true; })) ]; } // (lib.attrsets.filterAttrs ( k: v: builtins.elem k [ "syncthing" ] )) device )) ]; }; services.${service} = { inherit enable; openDefaultPorts = true; configDir = "/var/lib/${service}"; databaseDir = "/var/cache/${service}"; dataDir = cfg.databaseDir; # Don't really care key = "${secretsDir}/key.pem"; cert = "${secretsDir}/cert.pem"; settings = { devices = builtins.listToAttrs ( builtins.map (device: { inherit (device) name; value = device.syncthing; }) syncingDevices ); folders = builtins.listToAttrs ( builtins.map (folder: { inherit (folder) name; value = { label = "${capitalizeFirstLetter folder.user} ${folder.label}"; path = "${config.users.users.${folder.user}.home}/${folder.path}"; devices = builtins.map (device: device.name) ( builtins.filter (folderShouldSyncWith folder) peerDevices ); versioning = if (config.frogeye.storageSize == "big" && folder.versionsMaxDays != null) then { type = "staggered"; params.maxAge = builtins.toString (folder.versionsMaxDays * 24 * 3600); # TODO Increase cleanupIntervalS to 1 day or so } else null; rescanIntervalS = 10 * 3600; # Using watcher, should be good enough copyRangeMethod = "all"; # Prevents duplication copyOwnershipFromParent = true; } // folder.syncthing; }) syncedFolders ); options = rec { urAccepted = 3; urSeen = urAccepted; }; }; }; systemd.services.${service}.serviceConfig = { ExecStartPre = [ "+${pkgs.writeShellScript "syncthing-create-folders" '' install -Dm700 -o ${cfg.user} -g ${cfg.group} -d ${cfg.configDir} install -Dm700 -o ${cfg.user} -g ${cfg.group} -d ${cfg.databaseDir} ''}" ]; PrivateUsers = lib.mkForce false; AmbientCapabilities = [ "CAP_CHOWN" "CAP_DAC_OVERRIDE" "CAP_FOWNER" ]; }; vivarium.passwordFiles = { ${cfg.key}.password = password // { transform = "${lib.getExe pkgs.openssl} pkey"; }; ${cfg.cert}.password = password // { transform = "${lib.getExe pkgs.openssl} x509"; }; }; }; }