From 34e9e9e4067ef616555ad6247eb3539fa168bc3b Mon Sep 17 00:00:00 2001 From: Seudonym Date: Thu, 11 Jun 2026 16:41:29 +0530 Subject: [PATCH 1/6] nixos/syncthing: fix defaults config --- nixos/modules/services/networking/syncthing.nix | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index dc338f783858..6925950a048f 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -301,7 +301,7 @@ let + /* Now we update the other settings defined in cleanedConfig which are not - "folders", "devices", or "guiPasswordFile". + "folders", "devices", "guiPasswordFile", or "defaults". */ (lib.pipe cleanedConfig [ builtins.attrNames @@ -309,6 +309,7 @@ let "folders" "devices" "guiPasswordFile" + "defaults" ]) (map (subOption: '' curl -X PUT -d ${ @@ -317,6 +318,19 @@ let '')) (lib.concatStringsSep "\n") ]) + + + # Handle the "defaults" option separately, as it has multiple sub-endpoints. + (lib.optionalString (cleanedConfig ? defaults) ( + lib.pipe cleanedConfig.defaults [ + builtins.attrNames + (map (subOption: '' + curl -X PUT -d ${ + lib.escapeShellArg (builtins.toJSON cleanedConfig.defaults.${subOption}) + } ${curlAddressArgs "/rest/config/defaults/${subOption}"} + '')) + (lib.concatStringsSep "\n") + ] + )) + # Now we hash the contents of guiPasswordFile and use the result to update the gui password (lib.optionalString (cfg.guiPasswordFile != null) '' From 511068bba9b41efecf67fa3557119e99ebf0bb1e Mon Sep 17 00:00:00 2001 From: Seudonym Date: Thu, 11 Jun 2026 16:42:15 +0530 Subject: [PATCH 2/6] nixos/tests/syncthing: add defaults test --- nixos/tests/all-tests.nix | 1 + nixos/tests/syncthing/defaults.nix | 42 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 nixos/tests/syncthing/defaults.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 5b17b838e343..96b117c3f9e5 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1585,6 +1585,7 @@ in sx = runTest ./sx.nix; sympa = runTest ./sympa.nix; syncthing = runTest ./syncthing/main.nix; + syncthing-defaults = runTest ./syncthing/defaults.nix; syncthing-folders = runTest ./syncthing/folders.nix; syncthing-guiPassword = runTest ./syncthing/guiPassword.nix; syncthing-guiPasswordFile = runTest ./syncthing/guiPasswordFile.nix; diff --git a/nixos/tests/syncthing/defaults.nix b/nixos/tests/syncthing/defaults.nix new file mode 100644 index 000000000000..cd90d40770e5 --- /dev/null +++ b/nixos/tests/syncthing/defaults.nix @@ -0,0 +1,42 @@ +{ lib, pkgs, ... }: +let + expectedPath = "/tmp/syncthing-default"; +in +{ + name = "syncthing-defaults"; + meta.maintainers = with pkgs.lib.maintainers; [ seudonym ]; + + nodes.machine = + { pkgs, ... }: + { + environment.systemPackages = [ + pkgs.libxml2 + pkgs.curl + ]; + services.syncthing = { + enable = true; + settings.defaults.folder.path = expectedPath; + }; + }; + + testScript = '' + import json + + machine.wait_for_unit("syncthing.service") + machine.wait_for_unit("syncthing-init.service") + + # Get the API key by parsing the config.xml + api_key = machine.succeed( + "xmllint --xpath 'string(configuration/gui/apikey)' /var/lib/syncthing/.config/syncthing/config.xml" + ).strip() + + # Query the defaults/folder endpoint via Syncthing's REST API + config = json.loads(machine.succeed( + f"curl -Ssf -H 'X-API-Key: {api_key}' http://127.0.0.1:8384/rest/config/defaults/folder" + )) + + actual_path = config.get('path') + assert actual_path == "${expectedPath}", f"Default folder path is '{actual_path}', but expected '${expectedPath}'" + machine.log(f"Success: Default folder path is correctly set to '{actual_path}'") + ''; +} From f7d047601bfdffd1317d61f75a282dd01fc1878f Mon Sep 17 00:00:00 2001 From: Seudonym Date: Thu, 11 Jun 2026 19:07:23 +0530 Subject: [PATCH 3/6] nixos/syncthing: use PATCH for config updates --- .../manual/release-notes/rl-2611.section.md | 2 ++ .../modules/services/networking/syncthing.nix | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2611.section.md b/nixos/doc/manual/release-notes/rl-2611.section.md index 3697b3bb2e3e..64e2c427da94 100644 --- a/nixos/doc/manual/release-notes/rl-2611.section.md +++ b/nixos/doc/manual/release-notes/rl-2611.section.md @@ -34,6 +34,8 @@ - `services.firezone.server.provision` has been removed due to it being unmaintanable. Remove all uses of provisioning and use the WebUI to configure firezone. +- The `services.syncthing` module now updates the Syncthing REST API using partial updates (`PATCH`) instead of full replacements (`PUT`) for general settings. Updating these settings was broken and prone to errors after updates, see [#428808](https://github.com/NixOS/nixpkgs/issues/428808) and [#528889](https://github.com/NixOS/nixpkgs/issues/528889). As a result, settings modified manually through the Syncthing Web UI that are not explicitly defined in your Nix configuration will now persist across rebuilds. + ## Other Notable Changes {#sec-release-26.11-notable-changes} diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index 6925950a048f..17594e8db9f9 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -312,7 +312,7 @@ let "defaults" ]) (map (subOption: '' - curl -X PUT -d ${ + curl -X PATCH -d ${ lib.escapeShellArg (builtins.toJSON cleanedConfig.${subOption}) } ${curlAddressArgs "/rest/config/${subOption}"} '')) @@ -323,11 +323,18 @@ let (lib.optionalString (cleanedConfig ? defaults) ( lib.pipe cleanedConfig.defaults [ builtins.attrNames - (map (subOption: '' - curl -X PUT -d ${ - lib.escapeShellArg (builtins.toJSON cleanedConfig.defaults.${subOption}) - } ${curlAddressArgs "/rest/config/defaults/${subOption}"} - '')) + (map ( + subOption: + let + # /rest/config/defaults/ignores only supports PUT + method = if subOption == "ignores" then "PUT" else "PATCH"; + in + '' + curl -X ${method} -d ${ + lib.escapeShellArg (builtins.toJSON cleanedConfig.defaults.${subOption}) + } ${curlAddressArgs "/rest/config/defaults/${subOption}"} + '' + )) (lib.concatStringsSep "\n") ] )) From 893048f24cba47d8f833a2bf6ba537b8bd351f1c Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Fri, 12 Jun 2026 14:46:28 +0300 Subject: [PATCH 4/6] nixos/syncthing: remove `with lib;` --- .../modules/services/networking/syncthing.nix | 246 +++++++++--------- 1 file changed, 123 insertions(+), 123 deletions(-) diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index 17594e8db9f9..69d119d88a1d 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -6,15 +6,13 @@ ... }: -with lib; - let cfg = config.services.syncthing; opt = options.services.syncthing; defaultUser = "syncthing"; defaultGroup = defaultUser; settingsFormat = pkgs.formats.json { }; - cleanedConfig = converge (filterAttrsRecursive (_: v: v != null && v != { })) cfg.settings; + cleanedConfig = lib.converge (lib.filterAttrsRecursive (_: v: v != null && v != { })) cfg.settings; isUnixGui = lib.strings.hasPrefix "unix://" cfg.guiAddress; @@ -35,7 +33,7 @@ let else "${cfg.guiAddress}${path}"; - devices = mapAttrsToList ( + devices = lib.mapAttrsToList ( _: device: device // { @@ -45,11 +43,11 @@ let anyAutoAccept = builtins.any (dev: dev.autoAcceptFolders) devices; - folders = mapAttrsToList ( + folders = lib.mapAttrsToList ( _: folder: folder // - throwIf (folder ? rescanInterval || folder ? watch || folder ? watchDelay) + lib.throwIf (folder ? rescanInterval || folder ? watch || folder ? watchDelay) '' The options services.syncthing.settings.folders..{rescanInterval,watch,watchDelay} were removed. Please use, respectively, {rescanIntervalS,fsWatcherEnabled,fsWatcherDelayS} instead. @@ -69,7 +67,7 @@ let throw "Invalid type for devices in folder '${folderName}'; expected list or attrset." ) folderDevices; } - ) (filterAttrs (_: folder: folder.enable) cfg.settings.folders); + ) (lib.filterAttrs (_: folder: folder.enable) cfg.settings.folders); jq = "${pkgs.jq}/bin/jq"; grep = lib.getExe pkgs.gnugrep; @@ -179,7 +177,7 @@ let [ # Now for each of these attributes, write the curl commands that are # identical to both folders and devices. - (mapAttrs ( + (lib.mapAttrs ( conf_type: s: # We iterate the `conf` list now, and run a curl -X POST command for each, that # should update that device/folder only. @@ -358,10 +356,10 @@ in options = { services.syncthing = { - enable = mkEnableOption "Syncthing, a self-hosted open-source alternative to Dropbox and Bittorrent Sync"; + enable = lib.mkEnableOption "Syncthing, a self-hosted open-source alternative to Dropbox and Bittorrent Sync"; - cert = mkOption { - type = types.nullOr types.str; + cert = lib.mkOption { + type = lib.types.nullOr lib.types.str; default = null; description = '' Path to the `cert.pem` file, which will be copied into Syncthing's @@ -369,8 +367,8 @@ in ''; }; - key = mkOption { - type = types.nullOr types.str; + key = lib.mkOption { + type = lib.types.nullOr lib.types.str; default = null; description = '' Path to the `key.pem` file, which will be copied into Syncthing's @@ -378,16 +376,16 @@ in ''; }; - guiPasswordFile = mkOption { - type = types.nullOr types.str; + guiPasswordFile = lib.mkOption { + type = lib.types.nullOr lib.types.str; default = null; description = '' Path to file containing the plaintext password for Syncthing's GUI. ''; }; - overrideDevices = mkOption { - type = types.bool; + overrideDevices = lib.mkOption { + type = lib.types.bool; default = true; description = '' Whether to delete the devices which are not configured via the @@ -397,10 +395,10 @@ in ''; }; - overrideFolders = mkOption { - type = types.bool; + overrideFolders = lib.mkOption { + type = lib.types.bool; default = !anyAutoAccept; - defaultText = literalMD '' + defaultText = lib.literalMD '' `true` unless any device has the [autoAcceptFolders](#opt-services.syncthing.settings.devices._name_.autoAcceptFolders) option set to `true`. @@ -413,47 +411,47 @@ in ''; }; - settings = mkOption { - type = types.submodule { + settings = lib.mkOption { + type = lib.types.submodule { freeformType = settingsFormat.type; options = { # global options - options = mkOption { + options = lib.mkOption { default = { }; description = '' The options element contains all other global configuration options ''; - type = types.submodule ( - { name, ... }: + type = lib.types.submodule ( + { ... }: { freeformType = settingsFormat.type; options = { - localAnnounceEnabled = mkOption { - type = types.nullOr types.bool; + localAnnounceEnabled = lib.mkOption { + type = lib.types.nullOr lib.types.bool; default = null; description = '' Whether to send announcements to the local LAN, also use such announcements to find other devices. ''; }; - localAnnouncePort = mkOption { - type = types.nullOr types.port; + localAnnouncePort = lib.mkOption { + type = lib.types.nullOr lib.types.port; default = null; description = '' The port on which to listen and send IPv4 broadcast announcements to. ''; }; - relaysEnabled = mkOption { - type = types.nullOr types.bool; + relaysEnabled = lib.mkOption { + type = lib.types.nullOr lib.types.bool; default = null; description = '' When true, relays will be connected to and potentially used for device to device connections. ''; }; - urAccepted = mkOption { - type = types.nullOr types.int; + urAccepted = lib.mkOption { + type = lib.types.nullOr lib.types.int; default = null; description = '' Whether the user has accepted to submit anonymous usage data. @@ -462,16 +460,16 @@ in ''; }; - limitBandwidthInLan = mkOption { - type = types.nullOr types.bool; + limitBandwidthInLan = lib.mkOption { + type = lib.types.nullOr lib.types.bool; default = null; description = '' Whether to apply bandwidth limits to devices in the same broadcast domain as the local device. ''; }; - maxFolderConcurrency = mkOption { - type = types.nullOr types.int; + maxFolderConcurrency = lib.mkOption { + type = lib.types.nullOr lib.types.int; default = null; description = '' This option controls how many folders may concurrently be in I/O-intensive operations such as syncing or scanning. @@ -484,7 +482,7 @@ in }; # device settings - devices = mkOption { + devices = lib.mkOption { default = { }; description = '' Peers/devices which Syncthing should communicate with. @@ -499,30 +497,30 @@ in addresses = [ "tcp://192.168.0.10:51820" ]; }; }; - type = types.attrsOf ( - types.submodule ( + type = lib.types.attrsOf ( + lib.types.submodule ( { name, ... }: { freeformType = settingsFormat.type; options = { - name = mkOption { - type = types.str; + name = lib.mkOption { + type = lib.types.str; default = name; description = '' The name of the device. ''; }; - id = mkOption { - type = types.str; + id = lib.mkOption { + type = lib.types.str; description = '' The device ID. See . ''; }; - autoAcceptFolders = mkOption { - type = types.bool; + autoAcceptFolders = lib.mkOption { + type = lib.types.bool; default = false; description = '' Automatically create or share folders that this device advertises at the default path. @@ -537,7 +535,7 @@ in }; # folder settings - folders = mkOption { + folders = lib.mkOption { default = { }; description = '' Folders which should be shared by Syncthing. @@ -546,7 +544,7 @@ in will be reverted on restart if [overrideFolders](#opt-services.syncthing.overrideFolders) is enabled. ''; - example = literalExpression '' + example = lib.literalExpression '' { "/home/user/sync" = { id = "syncme"; @@ -554,15 +552,15 @@ in }; } ''; - type = types.attrsOf ( - types.submodule ( + type = lib.types.attrsOf ( + lib.types.submodule ( { name, ... }: { freeformType = settingsFormat.type; options = { - enable = mkOption { - type = types.bool; + enable = lib.mkOption { + type = lib.types.bool; default = true; description = '' Whether to share this folder. @@ -571,12 +569,12 @@ in ''; }; - path = mkOption { + path = lib.mkOption { # TODO for release 23.05: allow relative paths again and set # working directory to cfg.dataDir - type = types.str // { - check = x: types.str.check x && (substring 0 1 x == "/" || substring 0 2 x == "~/"); - description = types.str.description + " starting with / or ~/"; + type = lib.types.str // { + check = x: lib.types.str.check x && (lib.substring 0 1 x == "/" || lib.substring 0 2 x == "~/"); + description = lib.types.str.description + " starting with / or ~/"; }; default = name; description = '' @@ -587,24 +585,24 @@ in ''; }; - id = mkOption { - type = types.str; + id = lib.mkOption { + type = lib.types.str; default = name; description = '' The ID of the folder. Must be the same on all devices. ''; }; - label = mkOption { - type = types.str; + label = lib.mkOption { + type = lib.types.str; default = name; description = '' The label of the folder. ''; }; - type = mkOption { - type = types.enum [ + type = lib.mkOption { + type = lib.types.enum [ "sendreceive" "sendonly" "receiveonly" @@ -617,17 +615,17 @@ in ''; }; - devices = mkOption { - type = types.listOf ( - types.oneOf [ - types.str - (types.submodule ( + devices = lib.mkOption { + type = lib.types.listOf ( + lib.types.oneOf [ + lib.types.str + (lib.types.submodule ( { ... }: { freeformType = settingsFormat.type; options = { - name = mkOption { - type = types.str; + name = lib.mkOption { + type = lib.types.str; default = null; description = '' The name of a device defined in the @@ -635,8 +633,8 @@ in option. ''; }; - encryptionPasswordFile = mkOption { - type = types.nullOr types.externalPath; + encryptionPasswordFile = lib.mkOption { + type = lib.types.nullOr lib.types.externalPath; default = null; description = '' Path to encryption password. If set, the file will be read during @@ -658,14 +656,14 @@ in ''; }; - versioning = mkOption { + versioning = lib.mkOption { default = null; description = '' How to keep changed/deleted files with Syncthing. There are 4 different types of versioning with different parameters. See . ''; - example = literalExpression '' + example = lib.literalExpression '' [ { versioning = { @@ -701,13 +699,12 @@ in } ] ''; - type = - with types; - nullOr (submodule { + type = lib.types.nullOr ( + lib.types.submodule { freeformType = settingsFormat.type; options = { - type = mkOption { - type = enum [ + type = lib.mkOption { + type = lib.types.enum [ "external" "simple" "staggered" @@ -719,11 +716,12 @@ in ''; }; }; - }); + } + ); }; - copyOwnershipFromParent = mkOption { - type = types.bool; + copyOwnershipFromParent = lib.mkOption { + type = lib.types.bool; default = false; description = '' On Unix systems, tries to copy file/folder ownership from the parent directory (the directory it’s located in). @@ -731,8 +729,8 @@ in ''; }; - ignorePatterns = mkOption { - type = types.nullOr (types.listOf types.str); + ignorePatterns = lib.mkOption { + type = lib.types.nullOr (lib.types.listOf lib.types.str); default = null; description = '' Syncthing can be configured to ignore certain files in a folder using ignore patterns. @@ -792,8 +790,8 @@ in }; }; - guiAddress = mkOption { - type = types.str; + guiAddress = lib.mkOption { + type = lib.types.str; default = "127.0.0.1:8384"; apply = x: if lib.strings.hasPrefix "/" x then "unix://${x}" else x; description = '' @@ -801,16 +799,16 @@ in ''; }; - systemService = mkOption { - type = types.bool; + systemService = lib.mkOption { + type = lib.types.bool; default = true; description = '' Whether to auto-launch Syncthing as a system service. ''; }; - user = mkOption { - type = types.str; + user = lib.mkOption { + type = lib.types.str; default = defaultUser; example = "yourUser"; description = '' @@ -820,8 +818,8 @@ in ''; }; - group = mkOption { - type = types.str; + group = lib.mkOption { + type = lib.types.str; default = defaultGroup; example = "yourGroup"; description = '' @@ -830,8 +828,8 @@ in ''; }; - all_proxy = mkOption { - type = with types; nullOr str; + all_proxy = lib.mkOption { + type = lib.types.nullOr lib.types.str; default = null; example = "socks5://address.com:1234"; description = '' @@ -842,8 +840,8 @@ in ''; }; - dataDir = mkOption { - type = types.path; + dataDir = lib.mkOption { + type = lib.types.path; default = "/var/lib/syncthing"; example = "/home/yourUser"; description = '' @@ -853,15 +851,15 @@ in configDir = let - cond = versionAtLeast config.system.stateVersion "19.03"; + cond = lib.versionAtLeast config.system.stateVersion "19.03"; in - mkOption { - type = types.path; + lib.mkOption { + type = lib.types.path; description = '' The path where the settings and keys will exist. ''; - default = cfg.dataDir + optionalString cond "/.config/syncthing"; - defaultText = literalMD '' + default = cfg.dataDir + lib.optionalString cond "/.config/syncthing"; + defaultText = lib.literalMD '' * if `stateVersion >= 19.03`: config.${opt.dataDir} + "/.config/syncthing" @@ -871,17 +869,17 @@ in ''; }; - databaseDir = mkOption { - type = types.path; + databaseDir = lib.mkOption { + type = lib.types.path; description = '' The directory containing the database and logs. ''; default = cfg.configDir; - defaultText = literalExpression "config.${opt.configDir}"; + defaultText = lib.literalExpression "config.${opt.configDir}"; }; - extraFlags = mkOption { - type = types.listOf types.str; + extraFlags = lib.mkOption { + type = lib.types.listOf lib.types.str; default = [ ]; example = [ "--reset-deltas" ]; description = '' @@ -889,8 +887,8 @@ in ''; }; - openDefaultPorts = mkOption { - type = types.bool; + openDefaultPorts = lib.mkOption { + type = lib.types.bool; default = false; example = true; description = '' @@ -904,35 +902,37 @@ in ''; }; - package = mkPackageOption pkgs "syncthing" { }; + package = lib.mkPackageOption pkgs "syncthing" { }; }; }; imports = [ - (mkRemovedOptionModule [ "services" "syncthing" "useInotify" ] '' + (lib.mkRemovedOptionModule [ "services" "syncthing" "useInotify" ] '' This option was removed because Syncthing now has the inotify functionality included under the name "fswatcher". It can be enabled on a per-folder basis through the web interface. '') - (mkRenamedOptionModule + (lib.mkRenamedOptionModule [ "services" "syncthing" "extraOptions" ] [ "services" "syncthing" "settings" ] ) - (mkRenamedOptionModule + (lib.mkRenamedOptionModule [ "services" "syncthing" "folders" ] [ "services" "syncthing" "settings" "folders" ] ) - (mkRenamedOptionModule + (lib.mkRenamedOptionModule [ "services" "syncthing" "devices" ] [ "services" "syncthing" "settings" "devices" ] ) - (mkRenamedOptionModule + (lib.mkRenamedOptionModule [ "services" "syncthing" "options" ] [ "services" "syncthing" "settings" "options" ] ) ] ++ map - (o: mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ]) + ( + o: lib.mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ] + ) [ "cert" "key" @@ -945,7 +945,7 @@ in ###### implementation - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { assertions = [ { assertion = !(cfg.overrideFolders && anyAutoAccept); @@ -962,7 +962,7 @@ in } ]; - networking.firewall = mkIf cfg.openDefaultPorts { + networking.firewall = lib.mkIf cfg.openDefaultPorts { allowedTCPPorts = [ 22000 ]; allowedUDPPorts = [ 21027 @@ -973,7 +973,7 @@ in environment.systemPackages = [ cfg.package ]; systemd.packages = [ cfg.package ]; - users.users = mkIf (cfg.systemService && cfg.user == defaultUser) { + users.users = lib.mkIf (cfg.systemService && cfg.user == defaultUser) { ${defaultUser} = { group = cfg.group; home = cfg.dataDir; @@ -983,14 +983,14 @@ in }; }; - users.groups = mkIf (cfg.systemService && cfg.group == defaultGroup) { + users.groups = lib.mkIf (cfg.systemService && cfg.group == defaultGroup) { ${defaultGroup}.gid = config.ids.gids.syncthing; }; systemd.services = { # upstream reference: # https://github.com/syncthing/syncthing/blob/main/etc/linux-systemd/system/syncthing%40.service - syncthing = mkIf cfg.systemService { + syncthing = lib.mkIf cfg.systemService { description = "Syncthing service"; after = [ "network.target" ]; environment = { @@ -1007,13 +1007,13 @@ in User = cfg.user; Group = cfg.group; ExecStartPre = - mkIf (cfg.cert != null || cfg.key != null) + lib.mkIf (cfg.cert != null || cfg.key != null) "+${pkgs.writers.writeBash "syncthing-copy-keys" '' install -dm700 -o ${cfg.user} -g ${cfg.group} ${cfg.configDir} - ${optionalString (cfg.cert != null) '' + ${lib.optionalString (cfg.cert != null) '' install -Dm644 -o ${cfg.user} -g ${cfg.group} ${toString cfg.cert} ${cfg.configDir}/cert.pem ''} - ${optionalString (cfg.key != null) '' + ${lib.optionalString (cfg.key != null) '' install -Dm600 -o ${cfg.user} -g ${cfg.group} ${toString cfg.key} ${cfg.configDir}/key.pem ''} ''}"; @@ -1055,7 +1055,7 @@ in ]; }; }; - syncthing-init = mkIf (cleanedConfig != { }) { + syncthing-init = lib.mkIf (cleanedConfig != { }) { description = "Syncthing configuration updater"; requisite = [ "syncthing.service" ]; after = [ "syncthing.service" ]; From 95e51031b49ab828a3238cf70b051ae80ec93da4 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Fri, 12 Jun 2026 14:49:16 +0300 Subject: [PATCH 5/6] nixos/syncthing: remove old, not evaluating well deprecation message The `folderType` variable wasn't evaluating even if the message would have been thrown. --- .../modules/services/networking/syncthing.nix | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index 69d119d88a1d..3ee231fcbc3b 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -43,31 +43,10 @@ let anyAutoAccept = builtins.any (dev: dev.autoAcceptFolders) devices; - folders = lib.mapAttrsToList ( - _: folder: - folder - // - lib.throwIf (folder ? rescanInterval || folder ? watch || folder ? watchDelay) - '' - The options services.syncthing.settings.folders..{rescanInterval,watch,watchDelay} - were removed. Please use, respectively, {rescanIntervalS,fsWatcherEnabled,fsWatcherDelayS} instead. - '' - { - devices = - let - folderDevices = folder.devices; - in - map ( - device: - if builtins.isString device then - { deviceId = cfg.settings.devices.${device}.id; } - else if builtins.isAttrs device then - { deviceId = cfg.settings.devices.${device.name}.id; } // device - else - throw "Invalid type for devices in folder '${folderName}'; expected list or attrset." - ) folderDevices; - } - ) (lib.filterAttrs (_: folder: folder.enable) cfg.settings.folders); + folders = lib.pipe cfg.settings.folders [ + (lib.filterAttrs (_: folder: folder.enable)) + builtins.attrValues + ]; jq = "${pkgs.jq}/bin/jq"; grep = lib.getExe pkgs.gnugrep; From c13c604a7a04e9c41524b6330d14a05c266a5197 Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Fri, 12 Jun 2026 14:49:55 +0300 Subject: [PATCH 6/6] nixos/syncthing: add maintainers --- nixos/modules/services/networking/syncthing.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index 3ee231fcbc3b..b96718675e15 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -1050,4 +1050,9 @@ in }; }; }; + + meta.maintainers = with lib.maintainers; [ + doronbehar + seudonym + ]; }