2025-04-01 20:10:43 +02:00
|
|
|
|
{
|
|
|
|
|
config,
|
|
|
|
|
lib,
|
|
|
|
|
options,
|
|
|
|
|
pkgs,
|
|
|
|
|
...
|
|
|
|
|
}:
|
2014-04-03 18:54:10 +02:00
|
|
|
|
|
2014-04-14 16:26:48 +02:00
|
|
|
|
with lib;
|
2014-04-03 18:54:10 +02:00
|
|
|
|
|
|
|
|
|
let
|
|
|
|
|
cfg = config.services.syncthing;
|
2021-12-05 22:06:49 +01:00
|
|
|
|
opt = options.services.syncthing;
|
2016-04-01 01:26:52 +02:00
|
|
|
|
defaultUser = "syncthing";
|
2021-07-29 15:20:30 +02:00
|
|
|
|
defaultGroup = defaultUser;
|
2023-05-22 16:56:23 +02:00
|
|
|
|
settingsFormat = pkgs.formats.json { };
|
2025-04-01 20:10:43 +02:00
|
|
|
|
cleanedConfig = converge (filterAttrsRecursive (_: v: v != null && v != { })) cfg.settings;
|
2019-04-21 23:05:07 +02:00
|
|
|
|
|
2023-10-25 09:18:42 +02:00
|
|
|
|
isUnixGui = (builtins.substring 0 1 cfg.guiAddress) == "/";
|
|
|
|
|
|
|
|
|
|
# Syncthing supports serving the GUI over Unix sockets. If that happens, the
|
|
|
|
|
# API is served over the Unix socket as well. This function returns the correct
|
|
|
|
|
# curl arguments for the address portion of the curl command for both network
|
|
|
|
|
# and Unix socket addresses.
|
2025-04-01 20:10:43 +02:00
|
|
|
|
curlAddressArgs =
|
|
|
|
|
path:
|
|
|
|
|
if
|
|
|
|
|
isUnixGui
|
2023-10-25 09:18:42 +02:00
|
|
|
|
# if cfg.guiAddress is a unix socket, tell curl explicitly about it
|
|
|
|
|
# note that the dot in front of `${path}` is the hostname, which is
|
|
|
|
|
# required.
|
2025-04-01 20:10:43 +02:00
|
|
|
|
then
|
|
|
|
|
"--unix-socket ${cfg.guiAddress} http://.${path}"
|
2025-02-19 15:44:05 +07:00
|
|
|
|
# no adjustments are needed if cfg.guiAddress is a network address
|
2025-04-01 20:10:43 +02:00
|
|
|
|
else
|
|
|
|
|
"${cfg.guiAddress}${path}";
|
|
|
|
|
|
|
|
|
|
devices = mapAttrsToList (
|
|
|
|
|
_: device:
|
|
|
|
|
device
|
|
|
|
|
// {
|
|
|
|
|
deviceID = device.id;
|
|
|
|
|
}
|
|
|
|
|
) cfg.settings.devices;
|
2023-05-22 16:56:23 +02:00
|
|
|
|
|
2025-03-01 08:36:50 -05:00
|
|
|
|
anyAutoAccept = builtins.any (dev: dev.autoAcceptFolders) devices;
|
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
folders = mapAttrsToList (
|
|
|
|
|
_: folder:
|
|
|
|
|
folder
|
|
|
|
|
//
|
|
|
|
|
throwIf (folder ? rescanInterval || folder ? watch || folder ? watchDelay)
|
|
|
|
|
''
|
|
|
|
|
The options services.syncthing.settings.folders.<name>.{rescanInterval,watch,watchDelay}
|
|
|
|
|
were removed. Please use, respectively, {rescanIntervalS,fsWatcherEnabled,fsWatcherDelayS} instead.
|
|
|
|
|
''
|
|
|
|
|
{
|
2024-09-15 21:10:12 +01:00
|
|
|
|
devices =
|
|
|
|
|
let
|
|
|
|
|
folderDevices = folder.devices;
|
|
|
|
|
in
|
2025-01-12 19:17:54 -08:00
|
|
|
|
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;
|
2025-04-01 20:10:43 +02:00
|
|
|
|
}
|
|
|
|
|
) (filterAttrs (_: folder: folder.enable) cfg.settings.folders);
|
2019-04-21 23:05:07 +02:00
|
|
|
|
|
2023-07-22 17:36:25 +03:00
|
|
|
|
jq = "${pkgs.jq}/bin/jq";
|
2025-04-01 20:10:43 +02:00
|
|
|
|
updateConfig = pkgs.writers.writeBash "merge-syncthing-config" (
|
|
|
|
|
''
|
|
|
|
|
set -efu
|
|
|
|
|
|
|
|
|
|
# be careful not to leak secrets in the filesystem or in process listings
|
|
|
|
|
umask 0077
|
|
|
|
|
|
|
|
|
|
curl() {
|
|
|
|
|
# get the api key by parsing the config.xml
|
|
|
|
|
while
|
|
|
|
|
! ${pkgs.libxml2}/bin/xmllint \
|
|
|
|
|
--xpath 'string(configuration/gui/apikey)' \
|
|
|
|
|
${cfg.configDir}/config.xml \
|
|
|
|
|
>"$RUNTIME_DIRECTORY/api_key"
|
|
|
|
|
do sleep 1; done
|
|
|
|
|
(printf "X-API-Key: "; cat "$RUNTIME_DIRECTORY/api_key") >"$RUNTIME_DIRECTORY/headers"
|
|
|
|
|
${pkgs.curl}/bin/curl -sSLk -H "@$RUNTIME_DIRECTORY/headers" \
|
|
|
|
|
--retry 1000 --retry-delay 1 --retry-all-errors \
|
|
|
|
|
"$@"
|
|
|
|
|
}
|
|
|
|
|
''
|
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Syncthing's rest API for the folders and devices is almost identical.
|
|
|
|
|
Hence we iterate them using lib.pipe and generate shell commands for both at
|
|
|
|
|
the same time.
|
|
|
|
|
*/
|
|
|
|
|
(lib.pipe
|
|
|
|
|
{
|
|
|
|
|
# The attributes below are the only ones that are different for devices /
|
|
|
|
|
# folders.
|
|
|
|
|
devs = {
|
|
|
|
|
new_conf_IDs = map (v: v.id) devices;
|
|
|
|
|
GET_IdAttrName = "deviceID";
|
|
|
|
|
override = cfg.overrideDevices;
|
|
|
|
|
conf = devices;
|
|
|
|
|
baseAddress = curlAddressArgs "/rest/config/devices";
|
|
|
|
|
};
|
|
|
|
|
dirs = {
|
|
|
|
|
new_conf_IDs = map (v: v.id) folders;
|
|
|
|
|
GET_IdAttrName = "id";
|
|
|
|
|
override = cfg.overrideFolders;
|
|
|
|
|
conf = folders;
|
|
|
|
|
baseAddress = curlAddressArgs "/rest/config/folders";
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
[
|
|
|
|
|
# Now for each of these attributes, write the curl commands that are
|
|
|
|
|
# identical to both folders and devices.
|
|
|
|
|
(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.
|
|
|
|
|
lib.pipe s.conf [
|
|
|
|
|
# Quoting https://docs.syncthing.net/rest/config.html:
|
|
|
|
|
#
|
|
|
|
|
# > PUT takes an array and POST a single object. In both cases if a
|
|
|
|
|
# given folder/device already exists, it’s replaced, otherwise a new
|
|
|
|
|
# one is added.
|
|
|
|
|
#
|
|
|
|
|
# What's not documented, is that using PUT will remove objects that
|
|
|
|
|
# don't exist in the array given. That's why we use here `POST`, and
|
|
|
|
|
# only if s.override == true then we DELETE the relevant folders
|
|
|
|
|
# afterwards.
|
2024-09-15 21:10:12 +01:00
|
|
|
|
(map (
|
|
|
|
|
new_cfg:
|
|
|
|
|
let
|
2025-01-12 19:17:54 -08:00
|
|
|
|
jsonPreSecretsFile = pkgs.writeTextFile {
|
|
|
|
|
name = "${conf_type}-${new_cfg.id}-conf-pre-secrets.json";
|
|
|
|
|
text = builtins.toJSON new_cfg;
|
|
|
|
|
};
|
|
|
|
|
injectSecretsJqCmd =
|
|
|
|
|
{
|
|
|
|
|
# There are no secrets in `devs`, so no massaging needed.
|
|
|
|
|
"devs" = "${jq} .";
|
|
|
|
|
"dirs" =
|
2024-09-15 21:10:12 +01:00
|
|
|
|
let
|
2025-01-12 19:17:54 -08:00
|
|
|
|
folder = new_cfg;
|
|
|
|
|
devicesWithSecrets = lib.pipe folder.devices [
|
|
|
|
|
(lib.filter (device: (builtins.isAttrs device) && device ? encryptionPasswordFile))
|
|
|
|
|
(map (device: {
|
|
|
|
|
deviceId = device.deviceId;
|
|
|
|
|
variableName = "secret_${builtins.hashString "sha256" device.encryptionPasswordFile}";
|
|
|
|
|
secretPath = device.encryptionPasswordFile;
|
|
|
|
|
}))
|
|
|
|
|
];
|
|
|
|
|
# At this point, `jsonPreSecretsFile` looks something like this:
|
|
|
|
|
#
|
|
|
|
|
# {
|
|
|
|
|
# ...,
|
|
|
|
|
# "devices": [
|
|
|
|
|
# {
|
|
|
|
|
# "deviceId": "id1",
|
|
|
|
|
# "encryptionPasswordFile": "/etc/bar-encryption-password",
|
|
|
|
|
# "name": "..."
|
|
|
|
|
# }
|
|
|
|
|
# ],
|
|
|
|
|
# }
|
|
|
|
|
#
|
|
|
|
|
# We now generate a `jq` command that can replace those
|
|
|
|
|
# `encryptionPasswordFile`s with `encryptionPassword`.
|
|
|
|
|
# The `jq` command ends up looking like this:
|
|
|
|
|
#
|
|
|
|
|
# jq --rawfile secret_DEADBEEF /etc/bar-encryption-password '
|
|
|
|
|
# .devices[] |= (
|
|
|
|
|
# if .deviceId == "id1" then
|
|
|
|
|
# del(.encryptionPasswordFile) |
|
|
|
|
|
# .encryptionPassword = $secret_DEADBEEF
|
|
|
|
|
# else
|
|
|
|
|
# .
|
|
|
|
|
# end
|
|
|
|
|
# )
|
|
|
|
|
# '
|
|
|
|
|
jqUpdates = map (device: ''
|
|
|
|
|
.devices[] |= (
|
|
|
|
|
if .deviceId == "${device.deviceId}" then
|
|
|
|
|
del(.encryptionPasswordFile) |
|
|
|
|
|
.encryptionPassword = ''$${device.variableName}
|
|
|
|
|
else
|
|
|
|
|
.
|
|
|
|
|
end
|
|
|
|
|
)
|
|
|
|
|
'') devicesWithSecrets;
|
|
|
|
|
jqRawFiles = map (
|
|
|
|
|
device: "--rawfile ${device.variableName} ${lib.escapeShellArg device.secretPath}"
|
|
|
|
|
) devicesWithSecrets;
|
2024-09-15 21:10:12 +01:00
|
|
|
|
in
|
2025-01-12 19:17:54 -08:00
|
|
|
|
"${jq} ${lib.concatStringsSep " " jqRawFiles} ${
|
|
|
|
|
lib.escapeShellArg (lib.concatStringsSep "|" ([ "." ] ++ jqUpdates))
|
|
|
|
|
}";
|
|
|
|
|
}
|
|
|
|
|
.${conf_type};
|
2024-09-15 21:10:12 +01:00
|
|
|
|
in
|
|
|
|
|
''
|
2025-01-12 19:17:54 -08:00
|
|
|
|
${injectSecretsJqCmd} ${jsonPreSecretsFile} | curl --json @- -X POST ${s.baseAddress}
|
2024-09-15 21:10:12 +01:00
|
|
|
|
''
|
|
|
|
|
))
|
2025-04-01 20:10:43 +02:00
|
|
|
|
(lib.concatStringsSep "\n")
|
|
|
|
|
]
|
|
|
|
|
/*
|
|
|
|
|
If we need to override devices/folders, we iterate all currently configured
|
|
|
|
|
IDs, via another `curl -X GET`, and we delete all IDs that are not part of
|
|
|
|
|
the Nix configured list of IDs
|
|
|
|
|
*/
|
|
|
|
|
+ lib.optionalString s.override ''
|
|
|
|
|
stale_${conf_type}_ids="$(curl -X GET ${s.baseAddress} | ${jq} \
|
|
|
|
|
--argjson new_ids ${lib.escapeShellArg (builtins.toJSON s.new_conf_IDs)} \
|
|
|
|
|
--raw-output \
|
|
|
|
|
'[.[].${s.GET_IdAttrName}] - $new_ids | .[]'
|
|
|
|
|
)"
|
|
|
|
|
for id in ''${stale_${conf_type}_ids}; do
|
|
|
|
|
>&2 echo "Deleting stale device: $id"
|
|
|
|
|
curl -X DELETE ${s.baseAddress}/$id
|
|
|
|
|
done
|
|
|
|
|
''
|
|
|
|
|
))
|
|
|
|
|
builtins.attrValues
|
|
|
|
|
(lib.concatStringsSep "\n")
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
+
|
|
|
|
|
/*
|
|
|
|
|
Now we update the other settings defined in cleanedConfig which are not
|
|
|
|
|
"folders" or "devices".
|
|
|
|
|
*/
|
|
|
|
|
(lib.pipe cleanedConfig [
|
|
|
|
|
builtins.attrNames
|
|
|
|
|
(lib.subtractLists [
|
|
|
|
|
"folders"
|
|
|
|
|
"devices"
|
|
|
|
|
])
|
|
|
|
|
(map (subOption: ''
|
|
|
|
|
curl -X PUT -d ${
|
|
|
|
|
lib.escapeShellArg (builtins.toJSON cleanedConfig.${subOption})
|
|
|
|
|
} ${curlAddressArgs "/rest/config/${subOption}"}
|
2023-07-22 17:36:25 +03:00
|
|
|
|
''))
|
|
|
|
|
(lib.concatStringsSep "\n")
|
2025-04-01 20:10:43 +02:00
|
|
|
|
])
|
|
|
|
|
+ ''
|
|
|
|
|
# restart Syncthing if required
|
|
|
|
|
if curl ${curlAddressArgs "/rest/config/restart-required"} |
|
|
|
|
|
${jq} -e .requiresRestart > /dev/null; then
|
|
|
|
|
curl -X POST ${curlAddressArgs "/rest/system/restart"}
|
|
|
|
|
fi
|
|
|
|
|
''
|
|
|
|
|
);
|
|
|
|
|
in
|
|
|
|
|
{
|
2014-04-03 18:54:10 +02:00
|
|
|
|
###### interface
|
|
|
|
|
options = {
|
|
|
|
|
services.syncthing = {
|
|
|
|
|
|
2024-04-13 14:54:15 +02:00
|
|
|
|
enable = mkEnableOption "Syncthing, a self-hosted open-source alternative to Dropbox and Bittorrent Sync";
|
2014-04-03 18:54:10 +02:00
|
|
|
|
|
2021-07-28 11:14:09 +02:00
|
|
|
|
cert = mkOption {
|
|
|
|
|
type = types.nullOr types.str;
|
|
|
|
|
default = null;
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2022-06-03 21:47:57 +02:00
|
|
|
|
Path to the `cert.pem` file, which will be copied into Syncthing's
|
|
|
|
|
[configDir](#opt-services.syncthing.configDir).
|
2021-07-28 11:14:09 +02:00
|
|
|
|
'';
|
|
|
|
|
};
|
2019-04-21 23:05:07 +02:00
|
|
|
|
|
2021-07-28 11:14:09 +02:00
|
|
|
|
key = mkOption {
|
|
|
|
|
type = types.nullOr types.str;
|
|
|
|
|
default = null;
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2022-06-03 21:47:57 +02:00
|
|
|
|
Path to the `key.pem` file, which will be copied into Syncthing's
|
|
|
|
|
[configDir](#opt-services.syncthing.configDir).
|
2021-07-28 11:14:09 +02:00
|
|
|
|
'';
|
|
|
|
|
};
|
2019-04-21 23:05:07 +02:00
|
|
|
|
|
2021-07-28 11:14:09 +02:00
|
|
|
|
overrideDevices = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = true;
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-07-28 11:14:09 +02:00
|
|
|
|
Whether to delete the devices which are not configured via the
|
2023-05-22 16:56:23 +02:00
|
|
|
|
[devices](#opt-services.syncthing.settings.devices) option.
|
2022-06-03 21:47:57 +02:00
|
|
|
|
If set to `false`, devices added via the web
|
2021-07-29 15:20:30 +02:00
|
|
|
|
interface will persist and will have to be deleted manually.
|
2021-07-28 11:14:09 +02:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
overrideFolders = mkOption {
|
|
|
|
|
type = types.bool;
|
2025-03-01 08:36:50 -05:00
|
|
|
|
default = !anyAutoAccept;
|
|
|
|
|
defaultText = literalMD ''
|
|
|
|
|
`true` unless any device has the
|
|
|
|
|
[autoAcceptFolders](#opt-services.syncthing.settings.devices._name_.autoAcceptFolders)
|
|
|
|
|
option set to `true`.
|
|
|
|
|
'';
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-07-28 11:14:09 +02:00
|
|
|
|
Whether to delete the folders which are not configured via the
|
2023-05-22 16:56:23 +02:00
|
|
|
|
[folders](#opt-services.syncthing.settings.folders) option.
|
2022-06-03 21:47:57 +02:00
|
|
|
|
If set to `false`, folders added via the web
|
2021-07-29 15:20:30 +02:00
|
|
|
|
interface will persist and will have to be deleted manually.
|
2021-07-28 11:14:09 +02:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-22 16:56:23 +02:00
|
|
|
|
settings = mkOption {
|
|
|
|
|
type = types.submodule {
|
|
|
|
|
freeformType = settingsFormat.type;
|
2023-05-22 10:28:27 +02:00
|
|
|
|
options = {
|
2023-05-22 16:56:23 +02:00
|
|
|
|
# global options
|
|
|
|
|
options = mkOption {
|
2025-04-01 20:10:43 +02:00
|
|
|
|
default = { };
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2023-05-22 16:56:23 +02:00
|
|
|
|
The options element contains all other global configuration options
|
2023-05-22 10:28:27 +02:00
|
|
|
|
'';
|
2025-04-01 20:10:43 +02:00
|
|
|
|
type = types.submodule (
|
|
|
|
|
{ name, ... }:
|
|
|
|
|
{
|
|
|
|
|
freeformType = settingsFormat.type;
|
|
|
|
|
options = {
|
|
|
|
|
localAnnounceEnabled = mkOption {
|
|
|
|
|
type = types.nullOr types.bool;
|
|
|
|
|
default = null;
|
|
|
|
|
description = ''
|
|
|
|
|
Whether to send announcements to the local LAN, also use such announcements to find other devices.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-04-14 00:20:18 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
localAnnouncePort = mkOption {
|
|
|
|
|
type = types.nullOr types.int;
|
|
|
|
|
default = null;
|
|
|
|
|
description = ''
|
|
|
|
|
The port on which to listen and send IPv4 broadcast announcements to.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-04-14 00:20:18 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
relaysEnabled = mkOption {
|
|
|
|
|
type = types.nullOr types.bool;
|
|
|
|
|
default = null;
|
|
|
|
|
description = ''
|
|
|
|
|
When true, relays will be connected to and potentially used for device to device connections.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-04-14 00:20:18 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
urAccepted = mkOption {
|
|
|
|
|
type = types.nullOr types.int;
|
|
|
|
|
default = null;
|
|
|
|
|
description = ''
|
|
|
|
|
Whether the user has accepted to submit anonymous usage data.
|
|
|
|
|
The default, 0, mean the user has not made a choice, and Syncthing will ask at some point in the future.
|
|
|
|
|
"-1" means no, a number above zero means that that version of usage reporting has been accepted.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
limitBandwidthInLan = mkOption {
|
|
|
|
|
type = types.nullOr types.bool;
|
|
|
|
|
default = null;
|
|
|
|
|
description = ''
|
|
|
|
|
Whether to apply bandwidth limits to devices in the same broadcast domain as the local device.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
maxFolderConcurrency = mkOption {
|
|
|
|
|
type = types.nullOr types.int;
|
|
|
|
|
default = null;
|
|
|
|
|
description = ''
|
|
|
|
|
This option controls how many folders may concurrently be in I/O-intensive operations such as syncing or scanning.
|
|
|
|
|
The mechanism is described in detail in a [separate chapter](https://docs.syncthing.net/advanced/option-max-concurrency.html).
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
};
|
2025-04-01 20:10:43 +02:00
|
|
|
|
}
|
|
|
|
|
);
|
2021-07-28 11:14:09 +02:00
|
|
|
|
};
|
|
|
|
|
|
2023-05-22 16:56:23 +02:00
|
|
|
|
# device settings
|
2021-07-28 11:14:09 +02:00
|
|
|
|
devices = mkOption {
|
2025-04-01 20:10:43 +02:00
|
|
|
|
default = { };
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2023-05-22 16:56:23 +02:00
|
|
|
|
Peers/devices which Syncthing should communicate with.
|
2021-07-27 18:15:41 +02:00
|
|
|
|
|
2023-05-22 16:56:23 +02:00
|
|
|
|
Note that you can still add devices manually, but those changes
|
|
|
|
|
will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices)
|
|
|
|
|
is enabled.
|
2021-07-28 11:14:09 +02:00
|
|
|
|
'';
|
2023-05-22 16:56:23 +02:00
|
|
|
|
example = {
|
|
|
|
|
bigbox = {
|
|
|
|
|
id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU";
|
|
|
|
|
addresses = [ "tcp://192.168.0.10:51820" ];
|
|
|
|
|
};
|
|
|
|
|
};
|
2025-04-01 20:10:43 +02:00
|
|
|
|
type = types.attrsOf (
|
|
|
|
|
types.submodule (
|
|
|
|
|
{ name, ... }:
|
|
|
|
|
{
|
|
|
|
|
freeformType = settingsFormat.type;
|
|
|
|
|
options = {
|
|
|
|
|
|
|
|
|
|
name = mkOption {
|
|
|
|
|
type = types.str;
|
|
|
|
|
default = name;
|
|
|
|
|
description = ''
|
|
|
|
|
The name of the device.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
id = mkOption {
|
|
|
|
|
type = types.str;
|
|
|
|
|
description = ''
|
|
|
|
|
The device ID. See <https://docs.syncthing.net/dev/device-ids.html>.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
autoAcceptFolders = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
|
|
|
|
description = ''
|
|
|
|
|
Automatically create or share folders that this device advertises at the default path.
|
|
|
|
|
See <https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format>.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
2023-05-22 10:28:27 +02:00
|
|
|
|
};
|
|
|
|
|
|
2023-05-22 16:56:23 +02:00
|
|
|
|
# folder settings
|
|
|
|
|
folders = mkOption {
|
2025-04-01 20:10:43 +02:00
|
|
|
|
default = { };
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2023-05-22 16:56:23 +02:00
|
|
|
|
Folders which should be shared by Syncthing.
|
2023-05-22 10:28:27 +02:00
|
|
|
|
|
2023-05-22 16:56:23 +02:00
|
|
|
|
Note that you can still add folders manually, but those changes
|
|
|
|
|
will be reverted on restart if [overrideFolders](#opt-services.syncthing.overrideFolders)
|
|
|
|
|
is enabled.
|
2023-05-22 10:28:27 +02:00
|
|
|
|
'';
|
2023-05-22 16:56:23 +02:00
|
|
|
|
example = literalExpression ''
|
|
|
|
|
{
|
|
|
|
|
"/home/user/sync" = {
|
|
|
|
|
id = "syncme";
|
|
|
|
|
devices = [ "bigbox" ];
|
|
|
|
|
};
|
|
|
|
|
}
|
2023-05-22 10:28:27 +02:00
|
|
|
|
'';
|
2025-04-01 20:10:43 +02:00
|
|
|
|
type = types.attrsOf (
|
|
|
|
|
types.submodule (
|
|
|
|
|
{ name, ... }:
|
|
|
|
|
{
|
|
|
|
|
freeformType = settingsFormat.type;
|
|
|
|
|
options = {
|
|
|
|
|
|
|
|
|
|
enable = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = true;
|
|
|
|
|
description = ''
|
|
|
|
|
Whether to share this folder.
|
|
|
|
|
This option is useful when you want to define all folders
|
|
|
|
|
in one place, but not every machine should share all folders.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 10:28:27 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
path = 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 ~/";
|
|
|
|
|
};
|
|
|
|
|
default = name;
|
|
|
|
|
description = ''
|
|
|
|
|
The path to the folder which should be shared.
|
|
|
|
|
Only absolute paths (starting with `/`) and paths relative to
|
|
|
|
|
the [user](#opt-services.syncthing.user)'s home directory
|
|
|
|
|
(starting with `~/`) are allowed.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2021-07-28 11:14:09 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
id = mkOption {
|
|
|
|
|
type = types.str;
|
|
|
|
|
default = name;
|
|
|
|
|
description = ''
|
|
|
|
|
The ID of the folder. Must be the same on all devices.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
label = mkOption {
|
|
|
|
|
type = types.str;
|
|
|
|
|
default = name;
|
|
|
|
|
description = ''
|
|
|
|
|
The label of the folder.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
type = mkOption {
|
|
|
|
|
type = types.enum [
|
|
|
|
|
"sendreceive"
|
|
|
|
|
"sendonly"
|
|
|
|
|
"receiveonly"
|
|
|
|
|
"receiveencrypted"
|
|
|
|
|
];
|
|
|
|
|
default = "sendreceive";
|
|
|
|
|
description = ''
|
|
|
|
|
Controls how the folder is handled by Syncthing.
|
|
|
|
|
See <https://docs.syncthing.net/users/config.html#config-option-folder.type>.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2024-07-02 13:49:03 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
devices = mkOption {
|
2025-01-12 19:17:54 -08:00
|
|
|
|
type = types.listOf (
|
|
|
|
|
types.oneOf [
|
|
|
|
|
types.str
|
|
|
|
|
(types.submodule (
|
|
|
|
|
{ ... }:
|
2024-09-15 21:10:12 +01:00
|
|
|
|
{
|
|
|
|
|
freeformType = settingsFormat.type;
|
|
|
|
|
options = {
|
2025-01-12 19:17:54 -08:00
|
|
|
|
name = mkOption {
|
|
|
|
|
type = types.str;
|
|
|
|
|
default = null;
|
|
|
|
|
description = ''
|
|
|
|
|
The name of a device defined in the
|
|
|
|
|
[devices](#opt-services.syncthing.settings.devices)
|
|
|
|
|
option.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
encryptionPasswordFile = mkOption {
|
|
|
|
|
type = types.nullOr (
|
|
|
|
|
types.pathWith {
|
|
|
|
|
inStore = false;
|
|
|
|
|
absolute = true;
|
|
|
|
|
}
|
|
|
|
|
);
|
2024-09-15 21:10:12 +01:00
|
|
|
|
default = null;
|
|
|
|
|
description = ''
|
|
|
|
|
Path to encryption password. If set, the file will be read during
|
|
|
|
|
service activation, without being embedded in derivation.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-01-12 19:17:54 -08:00
|
|
|
|
))
|
|
|
|
|
]
|
|
|
|
|
);
|
2025-04-01 20:10:43 +02:00
|
|
|
|
default = [ ];
|
|
|
|
|
description = ''
|
|
|
|
|
The devices this folder should be shared with. Each device must
|
|
|
|
|
be defined in the [devices](#opt-services.syncthing.settings.devices) option.
|
2024-09-15 21:10:12 +01:00
|
|
|
|
|
2025-01-12 19:17:54 -08:00
|
|
|
|
A list of either strings or attribute sets, where values
|
|
|
|
|
are device names or device configurations.
|
2025-04-01 20:10:43 +02:00
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
versioning = mkOption {
|
|
|
|
|
default = null;
|
|
|
|
|
description = ''
|
|
|
|
|
How to keep changed/deleted files with Syncthing.
|
|
|
|
|
There are 4 different types of versioning with different parameters.
|
|
|
|
|
See <https://docs.syncthing.net/users/versioning.html>.
|
|
|
|
|
'';
|
|
|
|
|
example = literalExpression ''
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
versioning = {
|
|
|
|
|
type = "simple";
|
|
|
|
|
params.keep = "10";
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
versioning = {
|
|
|
|
|
type = "trashcan";
|
|
|
|
|
params.cleanoutDays = "1000";
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
versioning = {
|
|
|
|
|
type = "staggered";
|
|
|
|
|
fsPath = "/syncthing/backup";
|
|
|
|
|
params = {
|
|
|
|
|
cleanInterval = "3600";
|
|
|
|
|
maxAge = "31536000";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
versioning = {
|
|
|
|
|
type = "external";
|
|
|
|
|
params.versionsPath = pkgs.writers.writeBash "backup" '''
|
|
|
|
|
folderpath="$1"
|
|
|
|
|
filepath="$2"
|
|
|
|
|
rm -rf "$folderpath/$filepath"
|
|
|
|
|
''';
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
'';
|
|
|
|
|
type =
|
|
|
|
|
with types;
|
|
|
|
|
nullOr (submodule {
|
|
|
|
|
freeformType = settingsFormat.type;
|
|
|
|
|
options = {
|
|
|
|
|
type = mkOption {
|
|
|
|
|
type = enum [
|
|
|
|
|
"external"
|
|
|
|
|
"simple"
|
|
|
|
|
"staggered"
|
|
|
|
|
"trashcan"
|
|
|
|
|
];
|
|
|
|
|
description = ''
|
|
|
|
|
The type of versioning.
|
|
|
|
|
See <https://docs.syncthing.net/users/versioning.html>.
|
|
|
|
|
'';
|
|
|
|
|
};
|
2023-05-22 16:56:23 +02:00
|
|
|
|
};
|
2025-04-01 20:10:43 +02:00
|
|
|
|
});
|
2023-05-22 16:56:23 +02:00
|
|
|
|
};
|
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
copyOwnershipFromParent = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
|
|
|
|
description = ''
|
|
|
|
|
On Unix systems, tries to copy file/folder ownership from the parent directory (the directory it’s located in).
|
|
|
|
|
Requires running Syncthing as a privileged user, or granting it additional capabilities (e.g. CAP_CHOWN on Linux).
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
2023-05-22 10:28:27 +02:00
|
|
|
|
};
|
|
|
|
|
|
2023-05-22 16:56:23 +02:00
|
|
|
|
};
|
|
|
|
|
};
|
2025-04-01 20:10:43 +02:00
|
|
|
|
default = { };
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-07-28 11:14:09 +02:00
|
|
|
|
Extra configuration options for Syncthing.
|
2022-06-03 21:47:57 +02:00
|
|
|
|
See <https://docs.syncthing.net/users/config.html>.
|
2023-01-16 13:12:12 +01:00
|
|
|
|
Note that this attribute set does not exactly match the documented
|
|
|
|
|
xml format. Instead, this is the format of the json rest api. There
|
|
|
|
|
are slight differences. For example, this xml:
|
|
|
|
|
```xml
|
|
|
|
|
<options>
|
|
|
|
|
<listenAddress>default</listenAddress>
|
|
|
|
|
<minHomeDiskFree unit="%">1</minHomeDiskFree>
|
|
|
|
|
</options>
|
|
|
|
|
```
|
|
|
|
|
corresponds to the json:
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
options: {
|
|
|
|
|
listenAddresses = [
|
|
|
|
|
"default"
|
|
|
|
|
];
|
|
|
|
|
minHomeDiskFree = {
|
|
|
|
|
unit = "%";
|
|
|
|
|
value = 1;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
2021-07-28 11:14:09 +02:00
|
|
|
|
'';
|
|
|
|
|
example = {
|
|
|
|
|
options.localAnnounceEnabled = false;
|
|
|
|
|
gui.theme = "black";
|
2021-07-27 18:15:41 +02:00
|
|
|
|
};
|
2019-04-21 23:05:07 +02:00
|
|
|
|
};
|
|
|
|
|
|
2018-10-09 22:13:58 +02:00
|
|
|
|
guiAddress = mkOption {
|
2018-10-14 06:46:36 +02:00
|
|
|
|
type = types.str;
|
2018-10-09 22:13:58 +02:00
|
|
|
|
default = "127.0.0.1:8384";
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-07-29 15:20:30 +02:00
|
|
|
|
The address to serve the web interface at.
|
2018-10-09 22:13:58 +02:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2016-04-14 16:42:04 +08:00
|
|
|
|
systemService = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = true;
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-07-29 15:20:30 +02:00
|
|
|
|
Whether to auto-launch Syncthing as a system service.
|
|
|
|
|
'';
|
2016-04-14 16:42:04 +08:00
|
|
|
|
};
|
|
|
|
|
|
2014-04-03 18:54:10 +02:00
|
|
|
|
user = mkOption {
|
2018-10-14 06:46:36 +02:00
|
|
|
|
type = types.str;
|
2016-04-01 01:26:52 +02:00
|
|
|
|
default = defaultUser;
|
2021-07-29 15:20:30 +02:00
|
|
|
|
example = "yourUser";
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-07-29 15:20:30 +02:00
|
|
|
|
The user to run Syncthing as.
|
2022-11-01 17:33:35 -07:00
|
|
|
|
By default, a user named `${defaultUser}` will be created whose home
|
|
|
|
|
directory is [dataDir](#opt-services.syncthing.dataDir).
|
2016-04-14 16:42:04 +08:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
group = mkOption {
|
2018-10-14 06:46:36 +02:00
|
|
|
|
type = types.str;
|
2021-07-29 15:20:30 +02:00
|
|
|
|
default = defaultGroup;
|
|
|
|
|
example = "yourGroup";
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-07-29 15:20:30 +02:00
|
|
|
|
The group to run Syncthing under.
|
2022-06-03 21:47:57 +02:00
|
|
|
|
By default, a group named `${defaultGroup}` will be created.
|
2014-04-03 18:54:10 +02:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2015-12-15 15:07:40 +03:00
|
|
|
|
all_proxy = mkOption {
|
2018-10-14 06:46:36 +02:00
|
|
|
|
type = with types; nullOr str;
|
2016-04-01 01:26:52 +02:00
|
|
|
|
default = null;
|
2015-12-15 15:07:40 +03:00
|
|
|
|
example = "socks5://address.com:1234";
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-07-29 15:20:30 +02:00
|
|
|
|
Overwrites the all_proxy environment variable for the Syncthing process to
|
|
|
|
|
the given value. This is normally used to let Syncthing connect
|
|
|
|
|
through a SOCKS5 proxy server.
|
2022-06-03 21:47:57 +02:00
|
|
|
|
See <https://docs.syncthing.net/users/proxying.html>.
|
2015-12-15 15:07:40 +03:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2014-04-03 18:54:10 +02:00
|
|
|
|
dataDir = mkOption {
|
2016-04-01 01:26:52 +02:00
|
|
|
|
type = types.path;
|
2014-04-03 18:54:10 +02:00
|
|
|
|
default = "/var/lib/syncthing";
|
2021-07-29 15:20:30 +02:00
|
|
|
|
example = "/home/yourUser";
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-07-29 15:20:30 +02:00
|
|
|
|
The path where synchronised directories will exist.
|
2018-10-08 23:24:36 +02:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
configDir =
|
|
|
|
|
let
|
|
|
|
|
cond = versionAtLeast config.system.stateVersion "19.03";
|
|
|
|
|
in
|
|
|
|
|
mkOption {
|
|
|
|
|
type = types.path;
|
|
|
|
|
description = ''
|
|
|
|
|
The path where the settings and keys will exist.
|
|
|
|
|
'';
|
|
|
|
|
default = cfg.dataDir + optionalString cond "/.config/syncthing";
|
|
|
|
|
defaultText = literalMD ''
|
|
|
|
|
* if `stateVersion >= 19.03`:
|
2022-06-03 21:47:57 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
config.${opt.dataDir} + "/.config/syncthing"
|
|
|
|
|
* otherwise:
|
2022-06-03 21:47:57 +02:00
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
config.${opt.dataDir}
|
|
|
|
|
'';
|
|
|
|
|
};
|
2014-04-03 18:54:10 +02:00
|
|
|
|
|
2023-12-11 20:41:16 -05:00
|
|
|
|
databaseDir = mkOption {
|
|
|
|
|
type = types.path;
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2023-12-11 20:41:16 -05:00
|
|
|
|
The directory containing the database and logs.
|
|
|
|
|
'';
|
|
|
|
|
default = cfg.configDir;
|
|
|
|
|
defaultText = literalExpression "config.${opt.configDir}";
|
|
|
|
|
};
|
|
|
|
|
|
2021-08-29 11:20:02 +02:00
|
|
|
|
extraFlags = mkOption {
|
|
|
|
|
type = types.listOf types.str;
|
2025-04-01 20:10:43 +02:00
|
|
|
|
default = [ ];
|
2021-08-29 11:20:02 +02:00
|
|
|
|
example = [ "--reset-deltas" ];
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2021-08-29 11:20:02 +02:00
|
|
|
|
Extra flags passed to the syncthing command in the service definition.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2016-09-26 15:40:21 +08:00
|
|
|
|
openDefaultPorts = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
2021-07-29 15:20:30 +02:00
|
|
|
|
example = true;
|
2024-04-13 14:54:15 +02:00
|
|
|
|
description = ''
|
2022-01-18 11:40:06 +10:00
|
|
|
|
Whether to open the default ports in the firewall: TCP/UDP 22000 for transfers
|
2021-07-29 15:20:30 +02:00
|
|
|
|
and UDP 21027 for discovery.
|
|
|
|
|
|
|
|
|
|
If multiple users are running Syncthing on this machine, you will need
|
|
|
|
|
to manually open a set of ports for each instance and leave this disabled.
|
|
|
|
|
Alternatively, if you are running only a single instance on this machine
|
|
|
|
|
using the default ports, enable this.
|
2016-09-26 15:40:21 +08:00
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2023-11-27 01:19:27 +01:00
|
|
|
|
package = mkPackageOption pkgs "syncthing" { };
|
2014-04-03 18:54:10 +02:00
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-01 20:10:43 +02:00
|
|
|
|
imports =
|
|
|
|
|
[
|
|
|
|
|
(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
|
|
|
|
|
[ "services" "syncthing" "extraOptions" ]
|
|
|
|
|
[ "services" "syncthing" "settings" ]
|
|
|
|
|
)
|
|
|
|
|
(mkRenamedOptionModule
|
|
|
|
|
[ "services" "syncthing" "folders" ]
|
|
|
|
|
[ "services" "syncthing" "settings" "folders" ]
|
|
|
|
|
)
|
|
|
|
|
(mkRenamedOptionModule
|
|
|
|
|
[ "services" "syncthing" "devices" ]
|
|
|
|
|
[ "services" "syncthing" "settings" "devices" ]
|
|
|
|
|
)
|
|
|
|
|
(mkRenamedOptionModule
|
|
|
|
|
[ "services" "syncthing" "options" ]
|
|
|
|
|
[ "services" "syncthing" "settings" "options" ]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
++ map
|
|
|
|
|
(o: mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ])
|
|
|
|
|
[
|
|
|
|
|
"cert"
|
|
|
|
|
"key"
|
|
|
|
|
"devices"
|
|
|
|
|
"folders"
|
|
|
|
|
"overrideDevices"
|
|
|
|
|
"overrideFolders"
|
|
|
|
|
"extraOptions"
|
|
|
|
|
];
|
2014-04-03 18:54:10 +02:00
|
|
|
|
|
|
|
|
|
###### implementation
|
|
|
|
|
|
2014-04-04 10:46:19 +02:00
|
|
|
|
config = mkIf cfg.enable {
|
2025-03-01 08:36:50 -05:00
|
|
|
|
assertions = [
|
|
|
|
|
{
|
|
|
|
|
assertion = !(cfg.overrideFolders && anyAutoAccept);
|
|
|
|
|
message = ''
|
|
|
|
|
services.syncthing.overrideFolders will delete auto-accepted folders
|
|
|
|
|
from the configuration, creating path conflicts.
|
|
|
|
|
'';
|
|
|
|
|
}
|
|
|
|
|
];
|
2014-04-03 18:54:10 +02:00
|
|
|
|
|
2016-09-26 15:40:21 +08:00
|
|
|
|
networking.firewall = mkIf cfg.openDefaultPorts {
|
|
|
|
|
allowedTCPPorts = [ 22000 ];
|
2025-04-01 20:10:43 +02:00
|
|
|
|
allowedUDPPorts = [
|
|
|
|
|
21027
|
|
|
|
|
22000
|
|
|
|
|
];
|
2016-09-26 15:40:21 +08:00
|
|
|
|
};
|
|
|
|
|
|
2018-02-23 19:59:06 +01:00
|
|
|
|
systemd.packages = [ pkgs.syncthing ];
|
2016-09-26 15:40:21 +08:00
|
|
|
|
|
2019-07-29 21:56:12 +03:00
|
|
|
|
users.users = mkIf (cfg.systemService && cfg.user == defaultUser) {
|
2025-04-01 20:10:43 +02:00
|
|
|
|
${defaultUser} = {
|
|
|
|
|
group = cfg.group;
|
|
|
|
|
home = cfg.dataDir;
|
|
|
|
|
createHome = true;
|
|
|
|
|
uid = config.ids.uids.syncthing;
|
|
|
|
|
description = "Syncthing daemon user";
|
|
|
|
|
};
|
2019-07-29 21:56:12 +03:00
|
|
|
|
};
|
2016-04-01 01:26:52 +02:00
|
|
|
|
|
2021-07-29 15:20:30 +02:00
|
|
|
|
users.groups = mkIf (cfg.systemService && cfg.group == defaultGroup) {
|
2025-04-01 20:10:43 +02:00
|
|
|
|
${defaultGroup}.gid = config.ids.gids.syncthing;
|
2016-04-01 01:26:52 +02:00
|
|
|
|
};
|
|
|
|
|
|
2016-08-06 18:20:18 +03:00
|
|
|
|
systemd.services = {
|
2022-09-11 13:24:51 +02:00
|
|
|
|
# upstream reference:
|
|
|
|
|
# https://github.com/syncthing/syncthing/blob/main/etc/linux-systemd/system/syncthing%40.service
|
2016-09-26 15:40:21 +08:00
|
|
|
|
syncthing = mkIf cfg.systemService {
|
|
|
|
|
description = "Syncthing service";
|
|
|
|
|
after = [ "network.target" ];
|
|
|
|
|
environment = {
|
|
|
|
|
STNORESTART = "yes";
|
|
|
|
|
STNOUPGRADE = "yes";
|
|
|
|
|
inherit (cfg) all_proxy;
|
|
|
|
|
} // config.networking.proxy.envVars;
|
2016-04-14 16:42:04 +08:00
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
2016-09-26 15:40:21 +08:00
|
|
|
|
serviceConfig = {
|
|
|
|
|
Restart = "on-failure";
|
2022-09-11 13:24:51 +02:00
|
|
|
|
SuccessExitStatus = "3 4";
|
2025-04-01 20:10:43 +02:00
|
|
|
|
RestartForceExitStatus = "3 4";
|
2016-04-14 16:42:04 +08:00
|
|
|
|
User = cfg.user;
|
2016-09-26 15:40:21 +08:00
|
|
|
|
Group = cfg.group;
|
2025-04-01 20:10:43 +02:00
|
|
|
|
ExecStartPre =
|
|
|
|
|
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) ''
|
|
|
|
|
install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.cert} ${cfg.configDir}/cert.pem
|
|
|
|
|
''}
|
|
|
|
|
${optionalString (cfg.key != null) ''
|
|
|
|
|
install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.key} ${cfg.configDir}/key.pem
|
|
|
|
|
''}
|
|
|
|
|
''}";
|
2018-10-09 22:13:58 +02:00
|
|
|
|
ExecStart = ''
|
|
|
|
|
${cfg.package}/bin/syncthing \
|
|
|
|
|
-no-browser \
|
2023-10-25 09:18:42 +02:00
|
|
|
|
-gui-address=${if isUnixGui then "unix://" else ""}${cfg.guiAddress} \
|
2023-10-31 11:43:16 -04:00
|
|
|
|
-config=${cfg.configDir} \
|
2023-12-11 20:41:16 -05:00
|
|
|
|
-data=${cfg.databaseDir} \
|
2023-10-31 11:43:16 -04:00
|
|
|
|
${escapeShellArgs cfg.extraFlags}
|
2018-10-09 22:13:58 +02:00
|
|
|
|
'';
|
2020-01-20 21:15:47 +01:00
|
|
|
|
MemoryDenyWriteExecute = true;
|
|
|
|
|
NoNewPrivileges = true;
|
|
|
|
|
PrivateDevices = true;
|
|
|
|
|
PrivateMounts = true;
|
|
|
|
|
PrivateTmp = true;
|
|
|
|
|
PrivateUsers = true;
|
|
|
|
|
ProtectControlGroups = true;
|
|
|
|
|
ProtectHostname = true;
|
|
|
|
|
ProtectKernelModules = true;
|
|
|
|
|
ProtectKernelTunables = true;
|
|
|
|
|
RestrictNamespaces = true;
|
|
|
|
|
RestrictRealtime = true;
|
|
|
|
|
RestrictSUIDSGID = true;
|
|
|
|
|
CapabilityBoundingSet = [
|
2025-04-01 20:10:43 +02:00
|
|
|
|
"~CAP_SYS_PTRACE"
|
|
|
|
|
"~CAP_SYS_ADMIN"
|
|
|
|
|
"~CAP_SETGID"
|
|
|
|
|
"~CAP_SETUID"
|
|
|
|
|
"~CAP_SETPCAP"
|
|
|
|
|
"~CAP_SYS_TIME"
|
|
|
|
|
"~CAP_KILL"
|
2020-01-20 21:15:47 +01:00
|
|
|
|
];
|
2014-04-03 18:54:10 +02:00
|
|
|
|
};
|
2016-09-26 15:40:21 +08:00
|
|
|
|
};
|
2025-04-01 20:10:43 +02:00
|
|
|
|
syncthing-init = mkIf (cleanedConfig != { }) {
|
2021-07-27 18:15:41 +02:00
|
|
|
|
description = "Syncthing configuration updater";
|
2021-08-01 14:44:46 +02:00
|
|
|
|
requisite = [ "syncthing.service" ];
|
2019-04-21 23:05:07 +02:00
|
|
|
|
after = [ "syncthing.service" ];
|
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
|
User = cfg.user;
|
|
|
|
|
RemainAfterExit = true;
|
2022-07-23 13:57:25 +02:00
|
|
|
|
RuntimeDirectory = "syncthing-init";
|
2019-04-21 23:05:07 +02:00
|
|
|
|
Type = "oneshot";
|
|
|
|
|
ExecStart = updateConfig;
|
|
|
|
|
};
|
|
|
|
|
};
|
2016-08-06 18:20:18 +03:00
|
|
|
|
};
|
2014-04-03 18:54:10 +02:00
|
|
|
|
};
|
|
|
|
|
}
|