[PATCH] (home-)syncthing-service: added support for config serialization

  • Done
  • quality assurance status badge
Details
6 participants
  • Zacchaeus
  • Gabriel Santos
  • Leo Famulari
  • Ludovic Courtès
  • Bruno Victal
  • Rodion Goritskov
Owner
unassigned
Submitted by
Zacchaeus
Severity
normal

Debbugs page

Zacchaeus wrote 1 months ago
(address . guix-patches@gnu.org)
20250130215954.9394-1-eikcaz@zacchae.us
From 48c227546ea15aadbd5f5832d8cd30887f65ace9 Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH] (home-)syncthing-service: added support for config
serialization

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---
doc/guix.texi | 281 ++++++++++++++++++-
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 459 +++++++++++++++++++++++++++++++-
3 files changed, 752 insertions(+), 5 deletions(-)

Toggle diff (427 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..966fe852a4 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22669,8 +22670,284 @@ This assumes that the specified group exists.
Common configuration and data directory. The default configuration
directory is @file{$HOME} of the specified Syncthing @code{user}.
-@end table
-@end deftp
+@item @code{syncthing-config-file} (default: @var{#f})
+Either a file-like object that resolves to a syncthing configuraton xml
+file, or a syncthing-config-file record (see below).
+
+@end table
+@end deftp
+
+In the below, only details specific to Guix, or related to how your
+device will ``ping'' others, are presented. Otherwise, you should
+consult @uref{https://docs.syncthing.net/users/config.html, Syncthing
+config documentation}. Camelcase is preserved below only as to be
+consistent with its appearance in Syncthing code/documentation. If you
+would like to migrate to Guix-powerd Syncthing configuration, the
+generated config adds newlines/whitespace to the produced config such
+that your old config can be diff'ed with the new one.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s, or strings corresponding to
+the device ids. A device entry corresponding to the current device is
+silently ignored by Syncthing.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+gui-user and gui-password (see belowe).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-sendBasicAuthPrompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+@item @code{gui-apikey} (default: @var{"Vuky3VHVseQEoSk9YgxhSkNTnjQmqYK9"})
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bindDN} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecureSkipVerify} (default: @var{""})
+@item @code{ldap-searchBaseDN} (default: @var{""})
+@item @code{ldap-searchFilter} (default: @var{""})
+@item @code{listenAddress} (default: @var{"default"})
+@item @code{globalAnnounceServer} (default: @var{"default"})
+@item @code{globalAnnounceEnabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{localAnnounceEnabled} (default: @var{"true"})
+This makes devices find eachother very easily on the same LAN. Often,
+this will allow you to just plug an ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{localAnnouncePort} (default: @var{"21027"})
+@item @code{localAnnounceMCAddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{reconnectionIntervalS} (default: @var{"60"})
+@item @code{relaysEnabled} (default: @var{"true"})
+This option allows your Syncthing instance to coordinate with a global
+network of relays to enable syncing between devices when all other
+methods fail.
+
+@item @code{relayReconnectIntervalM} (default: @var{"10"})
+@item @code{startBrowser} (default: @var{"true"})
+@item @code{natEnabled} (default: @var{"true"})
+@item @code{natLeaseMinutes} (default: @var{"60"})
+@item @code{natRenewalMinutes} (default: @var{"30"})
+@item @code{natTimeoutSeconds} (default: @var{"10"})
+@item @code{urAccepted} (default: @var{"0"})
+ur* options control usage reporting. Set to -1 to disable, or positive
+to enable. The default (0) has reporting disabled, but you will be
+asked to decide in the GUI.
+
+@item @code{urSeen} (default: @var{"0"})
+@item @code{urUniqueID} (default: @var{""})
+@item @code{urURL} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{urPostInsecurely} (default: @var{"false"})
+@item @code{urInitialDelayS} (default: @var{"1800"})
+@item @code{autoUpgradeIntervalH} (default: @var{"12"})
+@item @code{upgradeToPreReleases} (default: @var{"false"})
+@item @code{keepTemporariesH} (default: @var{"24"})
+@item @code{cacheIgnoredFiles} (default: @var{"false"})
+@item @code{progressUpdateIntervalS} (default: @var{"5"})
+@item @code{limitBandwidthInLan} (default: @var{"false"})
+@item @code{minHomeDiskFree-unit} (default: @var{"%"})
+@item @code{minHomeDiskFree} (default: @var{"1"})
+@item @code{releasesURL} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwriteRemoteDeviceNamesOnConnect} (default: @var{"false"})
+@item @code{tempIndexMinBlocks} (default: @var{"10"})
+@item @code{unackedNotificationID} (default: @var{"authenticationUserAndPassword"})
+@item @code{trafficClass} (default: @var{"0"})
+@item @code{setLowPriority} (default: @var{"true"})
+@item @code{maxFolderConcurrency} (default: @var{"0"})
+@item @code{crashReportingURL} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crashReportingEnabled} (default: @var{"true"})
+@item @code{stunKeepaliveStartS} (default: @var{"180"})
+@item @code{stunKeepaliveMinS} (default: @var{"20"})
+@item @code{stunServer} (default: @var{"default"})
+@item @code{databaseTuning} (default: @var{"auto"})
+@item @code{maxConcurrentIncomingRequestKiB} (default: @var{"0"})
+@item @code{announceLANAddresses} (default: @var{"true"})
+@item @code{sendFullIndexOnUpgrade} (default: @var{"false"})
+@item @code{connectionLimitEnough} (default: @var{"0"})
+@item @code{connectionLimitMax} (default: @var{"0"})
+@item @code{insecureAllowOldTLSVersions} (default: @var{"false"})
+@item @code{connectionPriorityTcpLan} (default: @var{"10"})
+@item @code{connectionPriorityQuicLan} (default: @var{"20"})
+@item @code{connectionPriorityTcpWan} (default: @var{"30"})
+@item @code{connectionPriorityQuicWan} (default: @var{"40"})
+@item @code{connectionPriorityRelay} (default: @var{"50"})
+@item @code{connectionPriorityUpgradeThreshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+The default-* above do not affect folders and devices added by the Guix
+interface. They will, however, affect folders and devices that are
+added through the GUI, or by an ``introducer''.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to sync with.
+
+@table @asis
+@item @code{id}
+A long hash tied to the keys generated by Syncthing on the first launch.
+You can obtain this from the Syncthing GUI or by inpsecting an existing
+Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+Human readable device name for viewing in the GUI or in scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skipIntroductionRemovals} (default: @var{"false"})
+@item @code{introducedBy} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. The special value
+``dynamic'' will have syncthing use several means to find the device.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{autoAcceptFolders} (default: @var{"false"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{maxRequestKiB} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remoteGUIPort} (default: @var{"0"})
+@item @code{numConnections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synced.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This id cannot match the id of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+Human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescanIntervalS} (default: @var{"3600"})
+@item @code{fsWatcherEnabled} (default: @var{"true"})
+@item @code{fsWatcherDelayS} (default: @var{"10"})
+@item @code{ignorePerms} (default: @var{"false"})
+@item @code{autoNormalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+Devices should be a list of other Syncthing devices. If the current
+device is included, it is silently ignored by syncthing (which makes for
+lazier scheme code). Each device can be listed as a string representing
+the device id, a @code{syncthing-device} object, or a
+@code{syncthing-folder-device} object.
+
+@item @code{filesystemType} (default: @var{"basic"})
+@item @code{minDiskFree-unit} (default: @var{"%"})
+@item @code{minDiskFree} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fsPath} (default: @var{""})
+@item @code{versioning-fsType} (default: @var{"basic"})
+@item @code{versioning-cleanupIntervalS} (default: @var{"3600"})
+@item @code{versioning-cleanoutDays} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-maxAge} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{pullerMaxPendingKiB} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignoreDelete} (default: @var{"false"})
+@item @code{scanProgressIntervalS} (default: @var{"0"})
+@item @code{pullerPauseS} (default: @var{"0"})
+@item @code{maxConflicts} (default: @var{"10"})
+@item @code{disableSparseFiles} (default: @var{"false"})
+@item @code{disableTempIndexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weakHashThresholdPct} (default: @var{"25"})
+@item @code{markerName} (default: @var{".stfolder"})
+@item @code{copyOwnershipFromParent} (default: @var{"false"})
+@item @code{modTimeWindowS} (default: @var{"0"})
+@item @code{maxConcurrentWrites} (default: @var{"2"})
+@item @code{disableFsync} (default: @var{"false"})
+@item @code{blockPullOrder} (default: @var{"standard"})
+@item @code{copyRangeMethod} (default: @var{"standard"})
+@item @code{caseSensitiveFS} (default: @var{"false"})
+@item @code{junctionsAsDirs} (default: @var{"false"})
+@item @code{syncOwnership} (default: @var{"false"})
+@item @code{sendOwnership} (default: @var{"false"})
+@item @code{syncXattrs} (default: @var{"false"})
+@item @code{sendXattrs} (default: @var{"false"})
+@item @code{xattrFilter-maxSingleEntrySize} (default: @var{"1024"})
+@item @code{xattrFilter-maxTotalSize} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+There is some configuration which is specific to the relationship
+between a specific folder and a specific device. If you are fine
+leaving these as their default, then you can simply specify a
+syncthing-device instead of a syncthing-folder-device.
+
+@table @asis
+@item @code{id} (default: @var{""})
+id can be provided as a string of the id, or a @code{syncthing-device}.
+
+@item @code{introducedBy} (default: @var{""})
+@item @code{encryptionPassword} (default: @var{""})
+if encryptionPassword is non-empty, then it will be used as a password
+to encrypt file chunks as they are synced to that device. For more info
+on syncing to devices you don't totally trust, see
+@uref{https://docs.syncthing.net/users/untrusted.html, Syncthing Documentation Untrusted}.
+Note that file transfers are always end-to-end encrypted, regardless of
+this setting.
+
+@end table
+@end deftp
+
+Here is a more complex example configuration for illustrative purposes:
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("mydomain.com"))))
+ (bob-desktop "KYIMEGO-...-FT77EAO"))
+ (syncthing-configuration
+ (user "alice")
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (id bob-desktop)
+ (encryptionPassword "mypassword")))))))
+ ;; any device used above should be in this list
+ (devices (list laptop desktop bob-desktop))))
+@end lisp
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-service-type syncthing-service-type))
+ ;; system->home-service-type does not convert special-files-service-type to
+ ;; home-files-service-type, so redefine extensios
+ (extensions (list (service-extension home-files-service-type
+ syncthing-files-service)
+ (service-extension home-shepherd-service-type
+ syncthing-shepherd-service)))
(default-value (for-home (syncthing-configuration)))))
diff --git a/gnu/services/syncthing.scm b/gnu/services/syncthing.scm
index a7a9c6aadd..4f0d4c1082 100644
--- a/gnu/services/syncthing.scm
+++ b/gnu/services/syncthing.scm
@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2023 Justin Veilleux <terramorpha@cock.li>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -25,9 +26,20 @@ (define-module (gnu services syncthing)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
+ #:use-module (sxml simple)
#:export (syncthing-configuration
syncthing-configuration?
- syncthing-service-type))
+ syncthing-device
+ syncthing-device?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-folder-device
+ syncthing-folder-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-service-type
+ syncthing-shepherd-service
+ syncthing-files-service))
;;; Commentary:
;;;
@@ -35,6 +47,431 @@ (define-module (gnu services syncthing)
;;;
;;; Code:
+(define-record-type* <syncthing-device>
+ syncthing-device make-syncthing-device
+ syncthing-device?
+ (id syncthing-device-id)
+ (name syncthing-device-name (default ""))
+ (compression syncthing-device-compression (default "metadata"))
+ (introducer syncthing-device-introducer (default "false"))
+ (skipIntroductionRemovals syncthing-device-skipIntroductionRemovals (default "false"))
+ (introducedBy syncthing-device-introducedBy (default ""))
+ (addresses syncthing-device-addresses (default '("dynamic")))
+ (paused syncthing-device-paused (default "false"))
+ (autoAcceptFolders syncthing-device-autoAcceptFolders (default "false"))
+ (maxSendKbps syncthing-device-maxSendKbps (default "0"))
+ (maxRecvKbps syncthing-device-maxRecvKbps (default "0"))
+ (maxRequestKiB syncthing-device-maxRequestKiB (default "0"))
+ (untrusted syncthing-device-untrusted (default "false"))
+ (remoteGUIPort syncthing-device-remoteGUIPort (default "0"))
+ (numConnections syncthing-device-numConnections (default "0")))
+
+(define syncthing-device->sxml
+ (match-record-lambda <syncthing-device>
+ (id name compression introducer skipIntroductionRemovals introducedBy addresses paused autoAcceptFolders maxSendKbps maxRecvKbps maxRequestKiB untrusted remoteGUIPort numConnections)
+ `(device (@ (id ,id)
+ (name ,name)
+ (compression ,compression)
+ (introducer ,introducer)
+ (skipIntroductionRemovals ,skipIntroductionRemovals)
+ (introducedBy ,introducedBy))
+ ,@(map (lambda (address) `(address ,address)) addresses)
+ (paused ,paused)
+ (autoAcceptFolders ,autoAcceptFolders)
+ (maxSendKbps ,maxSendKbps)
+ (maxRecvKbps ,maxRecvKbps)
+ (maxRequestKiB ,maxRequestKiB)
+ (untrusted ,untrusted)
+ (remoteGUIPort ,remoteGUIPort)
+ (numConnections ,numConnections))))
+
+(define (id-or-device->id id-or-device)
+ (if (syncthing-device? id-or-device)
+ (syncthing-device-id id-or-device)
+ id-or-device))
+
+(define-record-type* <syncthing-folder-device>
+ syncthing-folder-device make-syncthing-folder-device
+ syncthing-folder-device?
+ (id syncthing-folder-device-id
+ (sanitize id-or-device->id))
+ (introducedBy syncthing-folder-device-introducedBy (default "")
+ (sanitize id-or-device->id))
+ (encryptionPassword syncthing-folder-device-encryptionPassword (default "")))
+
+(define syncthing-folder-device->sxml
+ (match-record-lambda <sync
This message was truncated. Download the full message here.
Zacchaeus wrote 1 months ago
(address . 75959@debbugs.gnu.org)
87bjvmjb1z.fsf@zacchae.us
Hi Guix!


In order to test my implementation, I compared two pairs of configs:

- the default config generated by syncthing with the default config
generated by (syncthing-configuration (syncthing-config-file
(syncthing-config-file))).

- my syncthing-gui-generated config with the config generated by a
syncthing-config-file reflecting my (rather complex) setup.

There are a few differences:

- The core differences hinge around the fact that the current device ID
is not known in advance. Of course, if you have already run
syncthing and know your device ID, you can specify that in your
config.

- The default ~/Sync folder normally includes the current device.
The documentaiton makes it clear that this is OK (and also I have
tested this).

- The config does not include the current device. The documentation
says "One or more device elements must be present in the file", but
it seems to work fine dispite this.

- The default folder template normally includes the current device.
Also fine.

- There are also three miscelaneous differences:

- The default device template normally omits the "name" field. I
suppose I could have added code to fix this, but I know it doesn't
break anything so I opted for slightly simpler code.

- The API key doesn't match. Of course it doesn't.

- The ~/Sync folder is normally specified by /home/<user>/Sync, but
since I know ~/Sync works, I found that better than trying to
compute ~/ in scheme.
In summary, there are differences, but those differences have been
accounted for and don't introduce bugs. There were a few undocumented
behaviors around the absense or presense of a value. In such cases, I
implemented whatever behavior was observed in a config file maintained
by the Syncthing GUI.

I considered submitting this as two patches, one for the home service
and one for the system service, but patching just the system service
broke the home service. Is that a valid reason to make it one patch?
If not, I can split up the patches and resubmit.

Addressing the elephant in the room, as I mentioned in the
documentation, I used camelCase only to match syncthing documentation.
For instance, the ldap searchFilter setting is configured by
(syncthing-config-file (ldap-searchFilter "...")). As nauseous as it
made me to write something like that, I think translating names to
snake-case adds too much confusion/complexity no matter where you add
it. It is useful when refencencing Syncthing documentation to know
exactly what keyword too look for. Though I believe exceptions exist to
every rule, and that this is maybe the only ecxeption to the no
camelcase rule, please do tell me if there is a better way and I will
resubmit the patch.


eikcaz-
Zacchaeus wrote 1 months ago
[PATCH] services: syncthing: Added support for config file serialization.
(address . 75959@debbugs.gnu.org)
877c6aj9a7.fsf@zacchae.us
From 0e0a2b727f3e898440a437edd9ed9b5924547c91 Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH] services: syncthing: Added support for config file
serialization.

* gnu/services/syncthing.scm: (syncthing-config-file) (syncthing-folder)
(syncthing-device) (syncthing-folder-device): New records;
(syncthing-service-type): added special-files-service-type extension for the
config file; (syncthing-files-service): service to create config file
* gnu/home/services/syncthing.scm: (home-syncthing-service-type): extended
home-files-services-type and re-exported more things from
gnu/services/syncthing.scm
* doc/guix.texi: (syncthing-service-type): document additions

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---
Adds Syncthing config file serialization to Guix. My original patch
commit string was not formatted correctly. Please see my revised patch
here. More info on how I tested that everything worked can be found in
a seperate email in this thread.

doc/guix.texi | 281 ++++++++++++++++++-
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 459 +++++++++++++++++++++++++++++++-
3 files changed, 752 insertions(+), 5 deletions(-)

Toggle diff (407 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..966fe852a4 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22669,8 +22670,284 @@ This assumes that the specified group exists.
Common configuration and data directory. The default configuration
directory is @file{$HOME} of the specified Syncthing @code{user}.
-@end table
-@end deftp
+@item @code{syncthing-config-file} (default: @var{#f})
+Either a file-like object that resolves to a syncthing configuraton xml
+file, or a syncthing-config-file record (see below).
+
+@end table
+@end deftp
+
+In the below, only details specific to Guix, or related to how your
+device will ``ping'' others, are presented. Otherwise, you should
+consult @uref{https://docs.syncthing.net/users/config.html, Syncthing
+config documentation}. Camelcase is preserved below only as to be
+consistent with its appearance in Syncthing code/documentation. If you
+would like to migrate to Guix-powerd Syncthing configuration, the
+generated config adds newlines/whitespace to the produced config such
+that your old config can be diff'ed with the new one.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s, or strings corresponding to
+the device ids. A device entry corresponding to the current device is
+silently ignored by Syncthing.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+gui-user and gui-password (see belowe).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-sendBasicAuthPrompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+@item @code{gui-apikey} (default: @var{"Vuky3VHVseQEoSk9YgxhSkNTnjQmqYK9"})
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bindDN} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecureSkipVerify} (default: @var{""})
+@item @code{ldap-searchBaseDN} (default: @var{""})
+@item @code{ldap-searchFilter} (default: @var{""})
+@item @code{listenAddress} (default: @var{"default"})
+@item @code{globalAnnounceServer} (default: @var{"default"})
+@item @code{globalAnnounceEnabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{localAnnounceEnabled} (default: @var{"true"})
+This makes devices find eachother very easily on the same LAN. Often,
+this will allow you to just plug an ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{localAnnouncePort} (default: @var{"21027"})
+@item @code{localAnnounceMCAddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{reconnectionIntervalS} (default: @var{"60"})
+@item @code{relaysEnabled} (default: @var{"true"})
+This option allows your Syncthing instance to coordinate with a global
+network of relays to enable syncing between devices when all other
+methods fail.
+
+@item @code{relayReconnectIntervalM} (default: @var{"10"})
+@item @code{startBrowser} (default: @var{"true"})
+@item @code{natEnabled} (default: @var{"true"})
+@item @code{natLeaseMinutes} (default: @var{"60"})
+@item @code{natRenewalMinutes} (default: @var{"30"})
+@item @code{natTimeoutSeconds} (default: @var{"10"})
+@item @code{urAccepted} (default: @var{"0"})
+ur* options control usage reporting. Set to -1 to disable, or positive
+to enable. The default (0) has reporting disabled, but you will be
+asked to decide in the GUI.
+
+@item @code{urSeen} (default: @var{"0"})
+@item @code{urUniqueID} (default: @var{""})
+@item @code{urURL} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{urPostInsecurely} (default: @var{"false"})
+@item @code{urInitialDelayS} (default: @var{"1800"})
+@item @code{autoUpgradeIntervalH} (default: @var{"12"})
+@item @code{upgradeToPreReleases} (default: @var{"false"})
+@item @code{keepTemporariesH} (default: @var{"24"})
+@item @code{cacheIgnoredFiles} (default: @var{"false"})
+@item @code{progressUpdateIntervalS} (default: @var{"5"})
+@item @code{limitBandwidthInLan} (default: @var{"false"})
+@item @code{minHomeDiskFree-unit} (default: @var{"%"})
+@item @code{minHomeDiskFree} (default: @var{"1"})
+@item @code{releasesURL} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwriteRemoteDeviceNamesOnConnect} (default: @var{"false"})
+@item @code{tempIndexMinBlocks} (default: @var{"10"})
+@item @code{unackedNotificationID} (default: @var{"authenticationUserAndPassword"})
+@item @code{trafficClass} (default: @var{"0"})
+@item @code{setLowPriority} (default: @var{"true"})
+@item @code{maxFolderConcurrency} (default: @var{"0"})
+@item @code{crashReportingURL} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crashReportingEnabled} (default: @var{"true"})
+@item @code{stunKeepaliveStartS} (default: @var{"180"})
+@item @code{stunKeepaliveMinS} (default: @var{"20"})
+@item @code{stunServer} (default: @var{"default"})
+@item @code{databaseTuning} (default: @var{"auto"})
+@item @code{maxConcurrentIncomingRequestKiB} (default: @var{"0"})
+@item @code{announceLANAddresses} (default: @var{"true"})
+@item @code{sendFullIndexOnUpgrade} (default: @var{"false"})
+@item @code{connectionLimitEnough} (default: @var{"0"})
+@item @code{connectionLimitMax} (default: @var{"0"})
+@item @code{insecureAllowOldTLSVersions} (default: @var{"false"})
+@item @code{connectionPriorityTcpLan} (default: @var{"10"})
+@item @code{connectionPriorityQuicLan} (default: @var{"20"})
+@item @code{connectionPriorityTcpWan} (default: @var{"30"})
+@item @code{connectionPriorityQuicWan} (default: @var{"40"})
+@item @code{connectionPriorityRelay} (default: @var{"50"})
+@item @code{connectionPriorityUpgradeThreshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+The default-* above do not affect folders and devices added by the Guix
+interface. They will, however, affect folders and devices that are
+added through the GUI, or by an ``introducer''.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to sync with.
+
+@table @asis
+@item @code{id}
+A long hash tied to the keys generated by Syncthing on the first launch.
+You can obtain this from the Syncthing GUI or by inpsecting an existing
+Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+Human readable device name for viewing in the GUI or in scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skipIntroductionRemovals} (default: @var{"false"})
+@item @code{introducedBy} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. The special value
+``dynamic'' will have syncthing use several means to find the device.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{autoAcceptFolders} (default: @var{"false"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{maxRequestKiB} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remoteGUIPort} (default: @var{"0"})
+@item @code{numConnections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synced.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This id cannot match the id of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+Human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescanIntervalS} (default: @var{"3600"})
+@item @code{fsWatcherEnabled} (default: @var{"true"})
+@item @code{fsWatcherDelayS} (default: @var{"10"})
+@item @code{ignorePerms} (default: @var{"false"})
+@item @code{autoNormalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+Devices should be a list of other Syncthing devices. If the current
+device is included, it is silently ignored by syncthing (which makes for
+lazier scheme code). Each device can be listed as a string representing
+the device id, a @code{syncthing-device} object, or a
+@code{syncthing-folder-device} object.
+
+@item @code{filesystemType} (default: @var{"basic"})
+@item @code{minDiskFree-unit} (default: @var{"%"})
+@item @code{minDiskFree} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fsPath} (default: @var{""})
+@item @code{versioning-fsType} (default: @var{"basic"})
+@item @code{versioning-cleanupIntervalS} (default: @var{"3600"})
+@item @code{versioning-cleanoutDays} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-maxAge} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{pullerMaxPendingKiB} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignoreDelete} (default: @var{"false"})
+@item @code{scanProgressIntervalS} (default: @var{"0"})
+@item @code{pullerPauseS} (default: @var{"0"})
+@item @code{maxConflicts} (default: @var{"10"})
+@item @code{disableSparseFiles} (default: @var{"false"})
+@item @code{disableTempIndexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weakHashThresholdPct} (default: @var{"25"})
+@item @code{markerName} (default: @var{".stfolder"})
+@item @code{copyOwnershipFromParent} (default: @var{"false"})
+@item @code{modTimeWindowS} (default: @var{"0"})
+@item @code{maxConcurrentWrites} (default: @var{"2"})
+@item @code{disableFsync} (default: @var{"false"})
+@item @code{blockPullOrder} (default: @var{"standard"})
+@item @code{copyRangeMethod} (default: @var{"standard"})
+@item @code{caseSensitiveFS} (default: @var{"false"})
+@item @code{junctionsAsDirs} (default: @var{"false"})
+@item @code{syncOwnership} (default: @var{"false"})
+@item @code{sendOwnership} (default: @var{"false"})
+@item @code{syncXattrs} (default: @var{"false"})
+@item @code{sendXattrs} (default: @var{"false"})
+@item @code{xattrFilter-maxSingleEntrySize} (default: @var{"1024"})
+@item @code{xattrFilter-maxTotalSize} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+There is some configuration which is specific to the relationship
+between a specific folder and a specific device. If you are fine
+leaving these as their default, then you can simply specify a
+syncthing-device instead of a syncthing-folder-device.
+
+@table @asis
+@item @code{id} (default: @var{""})
+id can be provided as a string of the id, or a @code{syncthing-device}.
+
+@item @code{introducedBy} (default: @var{""})
+@item @code{encryptionPassword} (default: @var{""})
+if encryptionPassword is non-empty, then it will be used as a password
+to encrypt file chunks as they are synced to that device. For more info
+on syncing to devices you don't totally trust, see
+@uref{https://docs.syncthing.net/users/untrusted.html, Syncthing Documentation Untrusted}.
+Note that file transfers are always end-to-end encrypted, regardless of
+this setting.
+
+@end table
+@end deftp
+
+Here is a more complex example configuration for illustrative purposes:
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("mydomain.com"))))
+ (bob-desktop "KYIMEGO-...-FT77EAO"))
+ (syncthing-configuration
+ (user "alice")
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (id bob-desktop)
+ (encryptionPassword "mypassword")))))))
+ ;; any device used above should be in this list
+ (devices (list laptop desktop bob-desktop))))
+@end lisp
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-service-type syncthing-service-type))
+ ;; system->home-service-type does not convert special-files-service-type to
+ ;; home-files-service-type, so redefine extensios
+ (extensions (list (service-extension home-files-service-type
+ syncthing-files-service)
+ (service-extension home-shepherd-service-type
+ syncthing-shepherd-service)))
(default-value (for-home (syncthing-configuration)))))
diff --git a/gnu/services/syncthing.scm b/gnu/services/syncthing.scm
index a7a9c6aadd..4f0d4c1082 100644
--- a/gnu/services/syncthing.scm
+++ b/gnu/services/syncthing.scm
@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2023 Justin Veilleux <terramorpha@cock.li>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -25,9 +26,20 @@ (define-module (gnu services syncthing)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
+ #:use-module (sxml simple)
#:export (syncthing-configuration
syncthing-configuration?
- syncthing-service-type))
+ syncthing-device
+ syncthing-device?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-folder-device
+ syncthing-folder-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-service-type
+ syncthing-shepherd-service
+ syncthing-files-service))
;;; Commentary:
;;;
@@ -35,6 +47,431 @@ (define-module (gnu services syncthing)
;;;
;;; Code:
+(define-record-type* <syncthing-device>
+ syncthing-device make-syncthing-device
+ syncthing-device?
+ (id syncthing-device-id)
+ (name syncthing-device-name (default ""))
+ (compression syncthing-device-compression (default "metadata"))
+ (introducer syncthing-device-introducer (default "false"))
+ (skipIntroductionRemovals syncthing-device-skipIntroductionRemovals (default "false"))
+ (introducedBy syncthing-device-introducedBy (default ""))
+ (addresses syncthing-device-addresses (default '("dynamic")))
+ (paused syncthing-device-paused (default "false"))
+ (autoAcceptFolders syncthing-device-autoAcceptFolders (default "false"))
+ (maxSendKbps syncthing-device-maxSendKbps (default "0"))
+ (maxRecvKbps syncthing-device-maxRecvKbps (default "0"))
+ (maxRequestKiB syncthing-device-maxRequestKiB (default "0"))
+ (untrusted syncthing-device-untrusted (default "false"))
+ (remoteGUIPort syncthing-device-remoteGUIPort (default "0"))
+ (numConnections syncthing-device-numConnections (default "0")))
+
+(define syncthing-device->sxml
+ (match-record-lambda <syncthing-device>
+ (id name compression introducer skipIntroductionRemovals introducedBy addresses paused autoAcceptFolders maxSendKbps maxRecvKbps maxRequestKiB untrusted remoteGUIPort numConnections)
+ `(device (@ (id ,id)
+ (name ,name)
+ (compression ,compression)
+ (introducer ,introducer)
+ (skipIntroductionRemovals ,skipIntroductionRemovals)
+ (introducedBy ,introducedBy))
+ ,@(map (lambda (address) `(address ,address)) addresses)
+ (paused ,paused)
+ (autoAcceptFolders ,autoAcceptFolders)
+ (maxSendKbps ,maxSendKbps)
+ (maxRecvKbps ,maxRecvKbps)
+ (maxRequestKiB ,maxRequestKi
This message was truncated. Download the full message here.
Leo Famulari wrote 1 months ago
Re: [bug#75959] [PATCH] (home-)syncthing-service: added support for config serialization
(name . Zacchaeus)(address . eikcaz@zacchae.us)(address . 75959@debbugs.gnu.org)
Z6FpybdJklSZ440b@jasmine.lan
On Sat, Feb 01, 2025 at 03:58:48AM -0500, Zacchaeus wrote:
Toggle quote (4 lines)
> - The default ~/Sync folder normally includes the current device.
> The documentaiton makes it clear that this is OK (and also I have
> tested this).

For reference:

"All mentioned devices are those that will be sharing the folder in
question. Each mentioned device must have a separate device element
later in the file. It is customary that the local device ID is included
in all folders. Syncthing will currently add this automatically if it is
not present in the configuration file."

Zacchaeus wrote 1 months ago
Re: [PATCH] services: syncthing: Added support for config file serialization.
(address . 75959@debbugs.gnu.org)
87lduli9kx.fsf@zacchae.us
From cb625d0548dd97bb124ae6821f3555e095823ae7 Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH] services: syncthing: Added support for config file
serialization.

* gnu/services/syncthing.scm: (syncthing-config-file) (syncthing-folder)
(syncthing-device) (syncthing-folder-device): New records;
(syncthing-service-type): added special-files-service-type extension for the
config file; (syncthing-files-service): service to create config file
* gnu/home/services/syncthing.scm: (home-syncthing-service-type): extended
home-files-services-type and re-exported more things from
gnu/services/syncthing.scm
* doc/guix.texi: (syncthing-service-type): document additions

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---

I realized it was silly to have users specify devices in
syncthing-config-file whin 98% of the time this is obvious from the
folders. (Usually each device shares at least one folder.) I added
some code to extract the devices from the folders. I also updated the
documentation accordingly. See earlier standalone email for details
on how I verified that this service functions correctly. I followed
those exact same steps this time.

doc/guix.texi | 285 +++++++++++++++++++
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 466 +++++++++++++++++++++++++++++++-
3 files changed, 765 insertions(+), 3 deletions(-)

Toggle diff (402 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..f9986d195a 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22669,9 +22670,293 @@ This assumes that the specified group exists.
Common configuration and data directory. The default configuration
directory is @file{$HOME} of the specified Syncthing @code{user}.
+@item @code{syncthing-config-file} (default: @var{#f})
+Either a file-like object that resolves to a syncthing configuration xml
+file, or a syncthing-config-file record (see below).
+
+@end table
+@end deftp
+
+In the below, only details specific to Guix, or related to how your
+device will ``ping'' others, are presented. Otherwise, you should
+consult @uref{https://docs.syncthing.net/users/config.html, Syncthing
+config documentation}. Camelcase is preserved below only as to be
+consistent with its appearance in Syncthing code/documentation. If you
+would like to migrate to Guix-powered Syncthing configuration, the
+generated config adds newlines/whitespace to the produced config such
+that your old config can be diff'ed with the new one. You can still
+modify Syncthing from the GUI or through ``introducer'' and
+``autoAcceptFolders'' mechanisms, but such changes will be reset on
+reconfigure.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s. Guix will
+automatically add any devices specified in any `folders' to this list.
+There are instances when you want to connect to a device despite not
+(initially) sharing any folders (such as a device with
+autoAcceptFolders). In such instances, you should specify those devices
+here. If multiple versions of the same device (same id) are discovered,
+the one in this list is prioritized. Otherwise, the first instance in
+the first folder is used.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+gui-user and gui-password (see below).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-sendBasicAuthPrompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+@item @code{gui-apikey} (default: @var{"Vuky3VHVseQEoSk9YgxhSkNTnjQmqYK9"})
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bindDN} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecureSkipVerify} (default: @var{""})
+@item @code{ldap-searchBaseDN} (default: @var{""})
+@item @code{ldap-searchFilter} (default: @var{""})
+@item @code{listenAddress} (default: @var{"default"})
+@item @code{globalAnnounceServer} (default: @var{"default"})
+@item @code{globalAnnounceEnabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{localAnnounceEnabled} (default: @var{"true"})
+This makes devices find each other very easily on the same LAN. Often,
+this will allow you to just plug an Ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{localAnnouncePort} (default: @var{"21027"})
+@item @code{localAnnounceMCAddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{reconnectionIntervalS} (default: @var{"60"})
+@item @code{relaysEnabled} (default: @var{"true"})
+This option allows your Syncthing instance to coordinate with a global
+network of relays to enable syncing between devices when all other
+methods fail.
+
+@item @code{relayReconnectIntervalM} (default: @var{"10"})
+@item @code{startBrowser} (default: @var{"true"})
+@item @code{natEnabled} (default: @var{"true"})
+@item @code{natLeaseMinutes} (default: @var{"60"})
+@item @code{natRenewalMinutes} (default: @var{"30"})
+@item @code{natTimeoutSeconds} (default: @var{"10"})
+@item @code{urAccepted} (default: @var{"0"})
+ur* options control usage reporting. Set to -1 to disable, or positive
+to enable. The default (0) has reporting disabled, but you will be
+asked to decide in the GUI.
+
+@item @code{urSeen} (default: @var{"0"})
+@item @code{urUniqueID} (default: @var{""})
+@item @code{urURL} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{urPostInsecurely} (default: @var{"false"})
+@item @code{urInitialDelayS} (default: @var{"1800"})
+@item @code{autoUpgradeIntervalH} (default: @var{"12"})
+@item @code{upgradeToPreReleases} (default: @var{"false"})
+@item @code{keepTemporariesH} (default: @var{"24"})
+@item @code{cacheIgnoredFiles} (default: @var{"false"})
+@item @code{progressUpdateIntervalS} (default: @var{"5"})
+@item @code{limitBandwidthInLan} (default: @var{"false"})
+@item @code{minHomeDiskFree-unit} (default: @var{"%"})
+@item @code{minHomeDiskFree} (default: @var{"1"})
+@item @code{releasesURL} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwriteRemoteDeviceNamesOnConnect} (default: @var{"false"})
+@item @code{tempIndexMinBlocks} (default: @var{"10"})
+@item @code{unackedNotificationID} (default: @var{"authenticationUserAndPassword"})
+@item @code{trafficClass} (default: @var{"0"})
+@item @code{setLowPriority} (default: @var{"true"})
+@item @code{maxFolderConcurrency} (default: @var{"0"})
+@item @code{crashReportingURL} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crashReportingEnabled} (default: @var{"true"})
+@item @code{stunKeepaliveStartS} (default: @var{"180"})
+@item @code{stunKeepaliveMinS} (default: @var{"20"})
+@item @code{stunServer} (default: @var{"default"})
+@item @code{databaseTuning} (default: @var{"auto"})
+@item @code{maxConcurrentIncomingRequestKiB} (default: @var{"0"})
+@item @code{announceLANAddresses} (default: @var{"true"})
+@item @code{sendFullIndexOnUpgrade} (default: @var{"false"})
+@item @code{connectionLimitEnough} (default: @var{"0"})
+@item @code{connectionLimitMax} (default: @var{"0"})
+@item @code{insecureAllowOldTLSVersions} (default: @var{"false"})
+@item @code{connectionPriorityTcpLan} (default: @var{"10"})
+@item @code{connectionPriorityQuicLan} (default: @var{"20"})
+@item @code{connectionPriorityTcpWan} (default: @var{"30"})
+@item @code{connectionPriorityQuicWan} (default: @var{"40"})
+@item @code{connectionPriorityRelay} (default: @var{"50"})
+@item @code{connectionPriorityUpgradeThreshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+The default-* above do not affect folders and devices added by the Guix
+interface. They will, however, affect folders and devices that are
+added through the GUI, by an ``introducer'', or a device with
+``autoAcceptFolders''.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to sync with.
+
+@table @asis
+@item @code{id}
+A long hash tied to the keys generated by Syncthing on the first launch.
+You can obtain this from the Syncthing GUI or by inspecting an existing
+Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+Human readable device name for viewing in the GUI or in scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skipIntroductionRemovals} (default: @var{"false"})
+@item @code{introducedBy} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. The special value
+``dynamic'' will have syncthing use several means to find the device.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{autoAcceptFolders} (default: @var{"false"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{maxRequestKiB} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remoteGUIPort} (default: @var{"0"})
+@item @code{numConnections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synced.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This id cannot match the id of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+Human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescanIntervalS} (default: @var{"3600"})
+@item @code{fsWatcherEnabled} (default: @var{"true"})
+@item @code{fsWatcherDelayS} (default: @var{"10"})
+@item @code{ignorePerms} (default: @var{"false"})
+@item @code{autoNormalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+Devices should be a list of other Syncthing devices. You do not need to
+specify the current device. Each device can be listed as a a
+@code{syncthing-device} record or a @code{syncthing-folder-device}
+record if you want files to be encrypted on disk.
+
+@item @code{filesystemType} (default: @var{"basic"})
+@item @code{minDiskFree-unit} (default: @var{"%"})
+@item @code{minDiskFree} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fsPath} (default: @var{""})
+@item @code{versioning-fsType} (default: @var{"basic"})
+@item @code{versioning-cleanupIntervalS} (default: @var{"3600"})
+@item @code{versioning-cleanoutDays} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-maxAge} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{pullerMaxPendingKiB} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignoreDelete} (default: @var{"false"})
+@item @code{scanProgressIntervalS} (default: @var{"0"})
+@item @code{pullerPauseS} (default: @var{"0"})
+@item @code{maxConflicts} (default: @var{"10"})
+@item @code{disableSparseFiles} (default: @var{"false"})
+@item @code{disableTempIndexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weakHashThresholdPct} (default: @var{"25"})
+@item @code{markerName} (default: @var{".stfolder"})
+@item @code{copyOwnershipFromParent} (default: @var{"false"})
+@item @code{modTimeWindowS} (default: @var{"0"})
+@item @code{maxConcurrentWrites} (default: @var{"2"})
+@item @code{disableFsync} (default: @var{"false"})
+@item @code{blockPullOrder} (default: @var{"standard"})
+@item @code{copyRangeMethod} (default: @var{"standard"})
+@item @code{caseSensitiveFS} (default: @var{"false"})
+@item @code{junctionsAsDirs} (default: @var{"false"})
+@item @code{syncOwnership} (default: @var{"false"})
+@item @code{sendOwnership} (default: @var{"false"})
+@item @code{syncXattrs} (default: @var{"false"})
+@item @code{sendXattrs} (default: @var{"false"})
+@item @code{xattrFilter-maxSingleEntrySize} (default: @var{"1024"})
+@item @code{xattrFilter-maxTotalSize} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+There is some configuration which is specific to the relationship
+between a specific folder and a specific device. If you are fine
+leaving these as their default, then you can simply specify a
+syncthing-device instead of a @code{syncthing-folder-device} in
+@code{syncthing-folder}s.
+
+@table @asis
+@item @code{device}
+device should be a @code{syncthing-device} for which this configuration
+applies.
+
+@item @code{introducedBy} (default: @var{""})
+@item @code{encryptionPassword} (default: @var{""})
+if encryptionPassword is non-empty, then it will be used as a password
+to encrypt file chunks as they are synced to that device. For more info
+on syncing to devices you don't totally trust, see
+@uref{https://docs.syncthing.net/users/untrusted.html, Syncthing Documentation Untrusted}.
+Note that file transfers are always end-to-end encrypted, regardless of
+this setting.
+
@end table
@end deftp
+Here is a more complex example configuration for illustrative purposes:
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("mydomain.example"))))
+ (bob-desktop (syncthing-device (id "KYIMEGO-...-FT77EAO"))))
+ (syncthing-configuration
+ (user "alice")
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (device bob-desktop)
+ (encryptionPassword "mypassword")))))))))
+@end lisp
+
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
@cindex SSH server
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-service-type syncthing-service-type))
+ ;; system->home-service-type does not convert special-files-service-type to
+ ;; home-files-service-type, so redefine extensios
+ (extensions (list (service-extension home-files-service-type
+ syncthing-files-service)
+ (service-extension home-shepherd-service-type
+ syncthing-shepherd-service)))
(default-value (for-home (syncthing-configuration)))))
diff --git a/gnu/services/syncthing.scm b/gnu/services/syncthing.scm
index a7a9c6aadd..2a58da8290 100644
--- a/gnu/services/syncthing.scm
+++ b/gnu/services/syncthing.scm
@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2023 Justin Veilleux <terramorpha@cock.li>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -25,9 +26,20 @@ (define-module (gnu services syncthing)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
+ #:use-module (sxml simple)
#:export (syncthing-configuration
syncthing-configuration?
- syncthing-service-type))
+ syncthing-device
+ syncthing-device?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-folder-device
+ syncthing-folder-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-service-type
+ syncthing-shepherd-service
+ syncthing-files-service))
;;; Commentary:
;;;
@@ -35,6 +47,438 @@ (define-module (gnu services syncthing)
;;;
;;; Code:
+(define-record-type* <syncthing-device>
+ syncthing-device make-syncthing-device
+ syncthing-device?
+ (id syncthing-device-id)
+ (name syncthing-device-name (default ""))
+ (compression syncthing-device-compression (default "metadata"))
+ (introducer syncthing-device-introducer (default "false"))
+ (skipIntroductionRemovals syncthing-device-skipIntroductionRemovals (default "false"))
+ (introducedBy syncthing-device-introducedBy (default ""))
+ (addresses syncthing-device-addresses (default '("dynamic")))
+ (paused syncthing-device-paused (default "false"))
+ (autoAcceptFolders syncthing-device-autoAcceptFolders (default "false"))
+ (maxSendKbps syncthing-device-maxSendKbps (default "0"))
+ (maxRecvKbps syncthing-device-maxRecvKbps (default "0"))
+ (maxRequestKiB syncthing-device-maxRequestKiB (default "0"))
+ (untrusted syncthing-device-untrusted (default "false"))
+ (remoteGUIPort syncthing-device-remoteGUIPort (default "0"))
+ (numConnections syncthing-device-numConnections (default "0")))
+
+(define syncthing-device->sxml
+ (match-record-lambda <syncthing-device>
+ (id name compression introducer skipIntroductionRemovals introducedBy addresses paused autoAcceptFolders maxSendKbps maxRecvK
This message was truncated. Download the full message here.
Zacchaeus wrote 1 months ago
[PATCH] services: syncthing: Added support for config file serialization.
(address . 75959@debbugs.gnu.org)
87frkqhg8w.fsf@zacchae.us
From 7ef311e85b1198c752b2eec57caa0256227e079c Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH] services: syncthing: Added support for config file
serialization.

* gnu/services/syncthing.scm: (syncthing-config-file) (syncthing-folder)
(syncthing-device) (syncthing-folder-device): New records;
(syncthing-service-type): added special-files-service-type extension for the
config file; (syncthing-files-service): service to create config file
* gnu/home/services/syncthing.scm: (home-syncthing-service-type): extended
home-files-services-type and re-exported more things from
gnu/services/syncthing.scm
* doc/guix.texi: (syncthing-service-type): document additions

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---
Fixed a bug caused by running system service (home service was fine).
Also changed syncthing-config-file field of syncthing-configuration to
config-file (syncthing-config-file record name unchanged). Hence,
setting the config file looks something like:

(syncthing-configuration (config-file (syncthing-config-file ...)))

This matches the pattern observed in other services like:

(connman-configuration (general-configuration (connman-general-configuration ...)))


doc/guix.texi | 288 ++++++++++++++++++++
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 466 +++++++++++++++++++++++++++++++-
3 files changed, 768 insertions(+), 3 deletions(-)

Toggle diff (401 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..2a4829a6a6 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22669,9 +22670,296 @@ This assumes that the specified group exists.
Common configuration and data directory. The default configuration
directory is @file{$HOME} of the specified Syncthing @code{user}.
+@item @code{config-file} (default: @var{#f})
+Either a file-like object that resolves to a syncthing configuration xml
+file, or a syncthing-config-file record (see below). If set to #f, Guix
+will not try to generate a config file, and the syncthing will generate
+a default one which will not be touched on reconfigure.
+
+@end table
+@end deftp
+
+In the below, only details specific to Guix, or related to how your
+device will ``ping'' others, are presented. Otherwise, you should
+consult @uref{https://docs.syncthing.net/users/config.html, Syncthing
+config documentation}. Camelcase is preserved below only as to be
+consistent with its appearance in Syncthing code/documentation. If you
+would like to migrate to Guix-powered Syncthing configuration, the
+generated config adds newlines/whitespace to the produced config such
+that your old config can be diff'ed with the new one. You can still
+modify Syncthing from the GUI or through ``introducer'' and
+``autoAcceptFolders'' mechanisms, but such changes will be reset on
+reconfigure.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s. Guix will
+automatically add any devices specified in any `folders' to this list.
+There are instances when you want to connect to a device despite not
+(initially) sharing any folders (such as a device with
+autoAcceptFolders). In such instances, you should specify those devices
+here. If multiple versions of the same device (same id) are discovered,
+the one in this list is prioritized. Otherwise, the first instance in
+the first folder is used.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+gui-user and gui-password (see below).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-sendBasicAuthPrompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+@item @code{gui-apikey} (default: @var{"Vuky3VHVseQEoSk9YgxhSkNTnjQmqYK9"})
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bindDN} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecureSkipVerify} (default: @var{""})
+@item @code{ldap-searchBaseDN} (default: @var{""})
+@item @code{ldap-searchFilter} (default: @var{""})
+@item @code{listenAddress} (default: @var{"default"})
+@item @code{globalAnnounceServer} (default: @var{"default"})
+@item @code{globalAnnounceEnabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{localAnnounceEnabled} (default: @var{"true"})
+This makes devices find each other very easily on the same LAN. Often,
+this will allow you to just plug an Ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{localAnnouncePort} (default: @var{"21027"})
+@item @code{localAnnounceMCAddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{reconnectionIntervalS} (default: @var{"60"})
+@item @code{relaysEnabled} (default: @var{"true"})
+This option allows your Syncthing instance to coordinate with a global
+network of relays to enable syncing between devices when all other
+methods fail.
+
+@item @code{relayReconnectIntervalM} (default: @var{"10"})
+@item @code{startBrowser} (default: @var{"true"})
+@item @code{natEnabled} (default: @var{"true"})
+@item @code{natLeaseMinutes} (default: @var{"60"})
+@item @code{natRenewalMinutes} (default: @var{"30"})
+@item @code{natTimeoutSeconds} (default: @var{"10"})
+@item @code{urAccepted} (default: @var{"0"})
+ur* options control usage reporting. Set to -1 to disable, or positive
+to enable. The default (0) has reporting disabled, but you will be
+asked to decide in the GUI.
+
+@item @code{urSeen} (default: @var{"0"})
+@item @code{urUniqueID} (default: @var{""})
+@item @code{urURL} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{urPostInsecurely} (default: @var{"false"})
+@item @code{urInitialDelayS} (default: @var{"1800"})
+@item @code{autoUpgradeIntervalH} (default: @var{"12"})
+@item @code{upgradeToPreReleases} (default: @var{"false"})
+@item @code{keepTemporariesH} (default: @var{"24"})
+@item @code{cacheIgnoredFiles} (default: @var{"false"})
+@item @code{progressUpdateIntervalS} (default: @var{"5"})
+@item @code{limitBandwidthInLan} (default: @var{"false"})
+@item @code{minHomeDiskFree-unit} (default: @var{"%"})
+@item @code{minHomeDiskFree} (default: @var{"1"})
+@item @code{releasesURL} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwriteRemoteDeviceNamesOnConnect} (default: @var{"false"})
+@item @code{tempIndexMinBlocks} (default: @var{"10"})
+@item @code{unackedNotificationID} (default: @var{"authenticationUserAndPassword"})
+@item @code{trafficClass} (default: @var{"0"})
+@item @code{setLowPriority} (default: @var{"true"})
+@item @code{maxFolderConcurrency} (default: @var{"0"})
+@item @code{crashReportingURL} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crashReportingEnabled} (default: @var{"true"})
+@item @code{stunKeepaliveStartS} (default: @var{"180"})
+@item @code{stunKeepaliveMinS} (default: @var{"20"})
+@item @code{stunServer} (default: @var{"default"})
+@item @code{databaseTuning} (default: @var{"auto"})
+@item @code{maxConcurrentIncomingRequestKiB} (default: @var{"0"})
+@item @code{announceLANAddresses} (default: @var{"true"})
+@item @code{sendFullIndexOnUpgrade} (default: @var{"false"})
+@item @code{connectionLimitEnough} (default: @var{"0"})
+@item @code{connectionLimitMax} (default: @var{"0"})
+@item @code{insecureAllowOldTLSVersions} (default: @var{"false"})
+@item @code{connectionPriorityTcpLan} (default: @var{"10"})
+@item @code{connectionPriorityQuicLan} (default: @var{"20"})
+@item @code{connectionPriorityTcpWan} (default: @var{"30"})
+@item @code{connectionPriorityQuicWan} (default: @var{"40"})
+@item @code{connectionPriorityRelay} (default: @var{"50"})
+@item @code{connectionPriorityUpgradeThreshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+The default-* above do not affect folders and devices added by the Guix
+interface. They will, however, affect folders and devices that are
+added through the GUI, by an ``introducer'', or a device with
+``autoAcceptFolders''.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to sync with.
+
+@table @asis
+@item @code{id}
+A long hash tied to the keys generated by Syncthing on the first launch.
+You can obtain this from the Syncthing GUI or by inspecting an existing
+Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+Human readable device name for viewing in the GUI or in scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skipIntroductionRemovals} (default: @var{"false"})
+@item @code{introducedBy} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. The special value
+``dynamic'' will have syncthing use several means to find the device.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{autoAcceptFolders} (default: @var{"false"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{maxRequestKiB} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remoteGUIPort} (default: @var{"0"})
+@item @code{numConnections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synced.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This id cannot match the id of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+Human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescanIntervalS} (default: @var{"3600"})
+@item @code{fsWatcherEnabled} (default: @var{"true"})
+@item @code{fsWatcherDelayS} (default: @var{"10"})
+@item @code{ignorePerms} (default: @var{"false"})
+@item @code{autoNormalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+Devices should be a list of other Syncthing devices. You do not need to
+specify the current device. Each device can be listed as a a
+@code{syncthing-device} record or a @code{syncthing-folder-device}
+record if you want files to be encrypted on disk.
+
+@item @code{filesystemType} (default: @var{"basic"})
+@item @code{minDiskFree-unit} (default: @var{"%"})
+@item @code{minDiskFree} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fsPath} (default: @var{""})
+@item @code{versioning-fsType} (default: @var{"basic"})
+@item @code{versioning-cleanupIntervalS} (default: @var{"3600"})
+@item @code{versioning-cleanoutDays} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-maxAge} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{pullerMaxPendingKiB} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignoreDelete} (default: @var{"false"})
+@item @code{scanProgressIntervalS} (default: @var{"0"})
+@item @code{pullerPauseS} (default: @var{"0"})
+@item @code{maxConflicts} (default: @var{"10"})
+@item @code{disableSparseFiles} (default: @var{"false"})
+@item @code{disableTempIndexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weakHashThresholdPct} (default: @var{"25"})
+@item @code{markerName} (default: @var{".stfolder"})
+@item @code{copyOwnershipFromParent} (default: @var{"false"})
+@item @code{modTimeWindowS} (default: @var{"0"})
+@item @code{maxConcurrentWrites} (default: @var{"2"})
+@item @code{disableFsync} (default: @var{"false"})
+@item @code{blockPullOrder} (default: @var{"standard"})
+@item @code{copyRangeMethod} (default: @var{"standard"})
+@item @code{caseSensitiveFS} (default: @var{"false"})
+@item @code{junctionsAsDirs} (default: @var{"false"})
+@item @code{syncOwnership} (default: @var{"false"})
+@item @code{sendOwnership} (default: @var{"false"})
+@item @code{syncXattrs} (default: @var{"false"})
+@item @code{sendXattrs} (default: @var{"false"})
+@item @code{xattrFilter-maxSingleEntrySize} (default: @var{"1024"})
+@item @code{xattrFilter-maxTotalSize} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+There is some configuration which is specific to the relationship
+between a specific folder and a specific device. If you are fine
+leaving these as their default, then you can simply specify a
+syncthing-device instead of a @code{syncthing-folder-device} in
+@code{syncthing-folder}s.
+
+@table @asis
+@item @code{device}
+device should be a @code{syncthing-device} for which this configuration
+applies.
+
+@item @code{introducedBy} (default: @var{""})
+@item @code{encryptionPassword} (default: @var{""})
+if encryptionPassword is non-empty, then it will be used as a password
+to encrypt file chunks as they are synced to that device. For more info
+on syncing to devices you don't totally trust, see
+@uref{https://docs.syncthing.net/users/untrusted.html, Syncthing Documentation Untrusted}.
+Note that file transfers are always end-to-end encrypted, regardless of
+this setting.
+
@end table
@end deftp
+Here is a more complex example configuration for illustrative purposes:
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("mydomain.example"))))
+ (bob-desktop (syncthing-device (id "KYIMEGO-...-FT77EAO"))))
+ (syncthing-configuration
+ (user "alice")
+ (config-file
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (device bob-desktop)
+ (encryptionPassword "mypassword"))))))))))))
+@end lisp
+
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
@cindex SSH server
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-service-type syncthing-service-type))
+ ;; system->home-service-type does not convert special-files-service-type to
+ ;; home-files-service-type, so redefine extensios
+ (extensions (list (service-extension home-files-service-type
+ syncthing-files-service)
+ (service-extension home-shepherd-service-type
+ syncthing-shepherd-service)))
(default-value (for-home (syncthing-configuration)))))
diff --git a/gnu/services/syncthing.scm b/gnu/services/syncthing.scm
index a7a9c6aadd..31e3dbe75f 100644
--- a/gnu/services/syncthing.scm
+++ b/gnu/services/syncthing.scm
@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2023 Justin Veilleux <terramorpha@cock.li>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -25,9 +26,20 @@ (define-module (gnu services syncthing)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
+ #:use-module (sxml simple)
#:export (syncthing-configuration
syncthing-configuration?
- syncthing-service-type))
+ syncthing-device
+ syncthing-device?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-folder-device
+ syncthing-folder-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-service-type
+ syncthing-shepherd-service
+ syncthing-files-service))
;;; Commentary:
;;;
@@ -35,6 +47,438 @@ (define-module (gnu services syncthing)
;;;
;;; Code:
+(define-record-type* <syncthing-device>
+ syncthing-device make-syncthing-device
+ syncthing-device?
+ (id syncthing-device-id)
+ (name syncthing-device-name (default ""))
+ (compression syncthing-device-compression (default "metadata"))
+ (introducer syncthing-device-introducer (default "false"))
+ (skipIntroductionRemovals syncthing-device-skipIntroductionRemovals (default "false"))
+ (introducedBy syncthing-device-introducedBy (default ""))
+ (addresses syncthing-device-addresses (default '("dynamic")))
+ (paused syncthing-device-paused (default "false"))
+ (autoAcceptFolders syncthing-device-autoAcceptFolders (default "false"))
+ (maxSendKbps syncthing-device-maxSendKbps (default "0"))
+ (maxRecvKbps syncthing-device-maxRecvKbps (default "0"))
+ (maxRequestKiB syncthing-device-maxRequestKiB (default "0"))
+ (untrusted syncthing-device-untrusted (default "false"))
+ (remoteGUIPort syncthing-device-remoteGUIPort (default "0"))
+ (numConnections syncthing-device-numConnection
This message was truncated. Download the full message here.
Leo Famulari wrote 1 months ago
(name . Zacchaeus)(address . eikcaz@zacchae.us)(address . 75959@debbugs.gnu.org)
Z6VSDhVzPtWXowK9@jasmine.lan
On Thu, Feb 06, 2025 at 05:15:27PM -0500, Zacchaeus wrote:
Toggle quote (6 lines)
> From 7ef311e85b1198c752b2eec57caa0256227e079c Mon Sep 17 00:00:00 2001
> From: Zacchaeus <eikcaz@zacchae.us>
> Date: Sun, 21 Jul 2024 00:54:25 -0700
> Subject: [PATCH] services: syncthing: Added support for config file
> serialization.

Thanks for the updated patch! By the way, when you generate the patches,
please use the Git option --reroll-count, so that we can keep track of
the revisions.

I applied it to the current master branch, and created a VM image based
on the lightweight-desktop template in our repo (attached).

Then, I copied the image out of the store, made it writable, and booted
it with QEMU.

------
$ ./pre-inst-env guix system image --image-type=qcow2 --no-grafts doc/os-config-lightweight-desktop.texi-syncthing --fallback --max-jobs=1 --cores=12 --keep-going --image-size=10G -v3
/gnu/store/86zz6i1x55irgg1r74riil8avgl8hlp9-image.qcow2
$ cp /gnu/store/86zz6i1x55irgg1r74riil8avgl8hlp9-image.qcow2 ~/tmp/guix.qcow2 && chmod 600 ~/tmp/guix.qcow2
$ qemu-system-x86_64 -nic user,model=virtio-net-pci -enable-kvm -m 1024 /home/leo/tmp/guix.qcow2
------

However, when I have included an instance of syncthing-service-type in
the OS declaration, my user's home directory is owned by root, and I'm
unable to log in as my user. When I remove syncthing-service-type, that
problem does not exist.

Any ideas?
;; -*- mode: scheme; -*-
;; This is an operating system configuration template
;; for a "desktop" setup without full-blown desktop
;; environments.

(use-modules (gnu) (gnu system nss))
(use-service-modules desktop syncthing)
(use-package-modules bootloaders emacs emacs-xyz ratpoison suckless wm
web-browsers
web
xdisorg
xorg)

(operating-system
(host-name "antelope")
(timezone "Europe/Paris")
(locale "en_US.utf8")

(kernel-arguments (list "console=ttyS0,115200"))

;; Use the UEFI variant of GRUB with the EFI System
;; Partition mounted on /boot/efi.
(bootloader (bootloader-configuration
(bootloader grub-bootloader)
(targets '("/dev/sdX"))))

;; Assume the target root file system is labelled "my-root",
;; and the EFI System Partition has UUID 1234-ABCD.
(file-systems (cons
(file-system
(device (file-system-label "my-root"))
(mount-point "/")
(type "ext4"))
%base-file-systems))

(users (cons* (user-account
(name "leo")
(password "")
(comment "leo")
(group "users")
(supplementary-groups '("wheel" "netdev"
"audio" "video")))
%base-user-accounts))

;; Add a bunch of window managers; we can choose one at
;; the log-in screen with F1.
(packages (append (list
;; terminal emulator
dillo
netsurf
rxvt-unicode
xterm)
%base-packages))

;; Use the "desktop" services, which include the X11
;; log-in service, networking with NetworkManager, and more.
(services
(cons* (service xfce-desktop-service-type)
(service syncthing-service-type
(let ((laptop (syncthing-device (id "M...")))
(desktop (syncthing-device (id "X...")
(addresses '("tcp://foo")))))
(syncthing-configuration
(user "leo")
(config-file (syncthing-config-file
(folders (list (syncthing-folder
(label "some-files")
(path "~/data")
(devices (list desktop laptop)))
(syncthing-folder
(label "critical-files")
(path "~/secrets")
(devices
(list desktop
laptop
(syncthing-folder-device
(device desktop)
(encryptionPassword "mypassword"))))))))))))
%desktop-services))

;; Allow resolution of '.local' host names with mDNS.
(name-service-switch %mdns-host-lookup-nss))
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEaEByLu7k06ZO5T6saqwZY3V/R/8FAmelUgoACgkQaqwZY3V/
R/9HuRAAipDfpgEqO0zmrrmFQmaPcy1F/OSTWxQvLyLQCkInl/YH6qRY3IbPjyB7
nutSjIY6xTQnYFE9oomW7V/yVi3zXv/tMPnRvp3yi0+6Orsfbsg2ZC9jHyG76p2G
MvAIbHKB59GcsZ2rZjSVVzymmDyv13KOy8FG7ZQPYATaI5kURBxIJkpB2nrszGvW
kk76WHmbQFeNPOcrf/BV0AJ9zcRX7+eKvL90Vwhxb44KQbuR/4jJehwffFjL1yr1
rJ7TRqhfKbuyM31vTeh9WF1Pceifl66yzoZ8eHy635scucZUi6G1sxuSRy3ESdfo
KUrOksemwmTE3dpKz6Aa5KJnfB4LWozBX0F9xmhsKbC4oaIkS9FmbVQVbY47RMSk
0JHno2g258uwRChsODLLkl5G5NGB1JcJiGaOnvD6sqtx1Z7mzboVS/G3tO6ChxaD
1VYJa1PteU/4JPUS4RrrhTvyI4asCpCohEdUhn1JnLkQ2iCGXzHmIkB8F0HlmM2u
sHBXbEeMrdFIO2apqiqJmzLQGR06cIjOSh7nuMfODjoEgXK+DAsShuHGDwKPDZcl
5Ya/itPWahETcnTrv5fNq15g/NA2AukyIZyqKX2uiv4kUHvNqeAKo8DcXfxy/+z8
wlrvePVn/oZPMDZsZxE6PtylACjrpF3H60FSw44LSCzIm4hUbJw=
=M/mG
-----END PGP SIGNATURE-----


Zacchaeus wrote 1 months ago
[PATCH v5] services: syncthing: Added support for config file serialization.
(address . 75959@debbugs.gnu.org)
878qqigygb.fsf@zacchae.us
From ad2bb352f8ff2cd93a2a1694faf353e02854ef2b Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH v5] services: syncthing: Added support for config file
serialization.

* gnu/services/syncthing.scm: (syncthing-config-file) (syncthing-folder)
(syncthing-device) (syncthing-folder-device): New records;
(syncthing-service-type): added special-files-service-type extension for the
config file; (syncthing-files-service): service to create config file
* gnu/home/services/syncthing.scm: (home-syncthing-service-type): extended
home-files-services-type and re-exported more things from
gnu/services/syncthing.scm
* doc/guix.texi: (syncthing-service-type): document additions

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---
The previous patch had an issue with fresh system installs: the
Syncthing service would populate ~/.config/syncthing/config.xml too
early, so ~ would be owned by root, and ~ would be non-empty so
skeletons would never be copied in. I addressed this by moving
syncthing's config home to /var/lib/syncthing-<user>. I only do this if
it is a system service AND you are specifying a config-file. This is
important for backwards compatibility. We wouldn't want people's
syncthing to suddenly start looking in /var/lib/ for their gui-managed
config after a system upgrade.

doc/guix.texi | 290 +++++++++++++++++
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 533 ++++++++++++++++++++++++++++++--
3 files changed, 810 insertions(+), 30 deletions(-)

Toggle diff (399 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..1fdc772a1d 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22669,9 +22670,298 @@ This assumes that the specified group exists.
Common configuration and data directory. The default configuration
directory is @file{$HOME} of the specified Syncthing @code{user}.
+@item @code{config-file} (default: @var{#f})
+Either a file-like object that resolves to a syncthing configuration xml
+file, or a syncthing-config-file record (see below). If set to #f, Guix
+will not try to generate a config file, and the syncthing will generate
+a default one which will not be touched on reconfigure. Specifying this
+in a system service moves Syncthing's common configuration and data
+directory to @file{/var/lib/syncthnig-<user>}.
+
+@end table
+@end deftp
+
+In the below, only details specific to Guix, or related to how your
+device will ``ping'' others, are presented. Otherwise, you should
+consult @uref{https://docs.syncthing.net/users/config.html, Syncthing
+config documentation}. Camelcase is preserved below only as to be
+consistent with its appearance in Syncthing code/documentation. If you
+would like to migrate to Guix-powered Syncthing configuration, the
+generated config adds newlines/whitespace to the produced config such
+that your old config can be diff'ed with the new one. You can still
+modify Syncthing from the GUI or through ``introducer'' and
+``autoAcceptFolders'' mechanisms, but such changes will be reset on
+reconfigure.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s. Guix will
+automatically add any devices specified in any `folders' to this list.
+There are instances when you want to connect to a device despite not
+(initially) sharing any folders (such as a device with
+autoAcceptFolders). In such instances, you should specify those devices
+here. If multiple versions of the same device (same id) are discovered,
+the one in this list is prioritized. Otherwise, the first instance in
+the first folder is used.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+gui-user and gui-password (see below).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-sendBasicAuthPrompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+@item @code{gui-apikey} (default: @var{"Vuky3VHVseQEoSk9YgxhSkNTnjQmqYK9"})
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bindDN} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecureSkipVerify} (default: @var{""})
+@item @code{ldap-searchBaseDN} (default: @var{""})
+@item @code{ldap-searchFilter} (default: @var{""})
+@item @code{listenAddress} (default: @var{"default"})
+@item @code{globalAnnounceServer} (default: @var{"default"})
+@item @code{globalAnnounceEnabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{localAnnounceEnabled} (default: @var{"true"})
+This makes devices find each other very easily on the same LAN. Often,
+this will allow you to just plug an Ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{localAnnouncePort} (default: @var{"21027"})
+@item @code{localAnnounceMCAddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{reconnectionIntervalS} (default: @var{"60"})
+@item @code{relaysEnabled} (default: @var{"true"})
+This option allows your Syncthing instance to coordinate with a global
+network of relays to enable syncing between devices when all other
+methods fail.
+
+@item @code{relayReconnectIntervalM} (default: @var{"10"})
+@item @code{startBrowser} (default: @var{"true"})
+@item @code{natEnabled} (default: @var{"true"})
+@item @code{natLeaseMinutes} (default: @var{"60"})
+@item @code{natRenewalMinutes} (default: @var{"30"})
+@item @code{natTimeoutSeconds} (default: @var{"10"})
+@item @code{urAccepted} (default: @var{"0"})
+ur* options control usage reporting. Set to -1 to disable, or positive
+to enable. The default (0) has reporting disabled, but you will be
+asked to decide in the GUI.
+
+@item @code{urSeen} (default: @var{"0"})
+@item @code{urUniqueID} (default: @var{""})
+@item @code{urURL} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{urPostInsecurely} (default: @var{"false"})
+@item @code{urInitialDelayS} (default: @var{"1800"})
+@item @code{autoUpgradeIntervalH} (default: @var{"12"})
+@item @code{upgradeToPreReleases} (default: @var{"false"})
+@item @code{keepTemporariesH} (default: @var{"24"})
+@item @code{cacheIgnoredFiles} (default: @var{"false"})
+@item @code{progressUpdateIntervalS} (default: @var{"5"})
+@item @code{limitBandwidthInLan} (default: @var{"false"})
+@item @code{minHomeDiskFree-unit} (default: @var{"%"})
+@item @code{minHomeDiskFree} (default: @var{"1"})
+@item @code{releasesURL} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwriteRemoteDeviceNamesOnConnect} (default: @var{"false"})
+@item @code{tempIndexMinBlocks} (default: @var{"10"})
+@item @code{unackedNotificationID} (default: @var{"authenticationUserAndPassword"})
+@item @code{trafficClass} (default: @var{"0"})
+@item @code{setLowPriority} (default: @var{"true"})
+@item @code{maxFolderConcurrency} (default: @var{"0"})
+@item @code{crashReportingURL} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crashReportingEnabled} (default: @var{"true"})
+@item @code{stunKeepaliveStartS} (default: @var{"180"})
+@item @code{stunKeepaliveMinS} (default: @var{"20"})
+@item @code{stunServer} (default: @var{"default"})
+@item @code{databaseTuning} (default: @var{"auto"})
+@item @code{maxConcurrentIncomingRequestKiB} (default: @var{"0"})
+@item @code{announceLANAddresses} (default: @var{"true"})
+@item @code{sendFullIndexOnUpgrade} (default: @var{"false"})
+@item @code{connectionLimitEnough} (default: @var{"0"})
+@item @code{connectionLimitMax} (default: @var{"0"})
+@item @code{insecureAllowOldTLSVersions} (default: @var{"false"})
+@item @code{connectionPriorityTcpLan} (default: @var{"10"})
+@item @code{connectionPriorityQuicLan} (default: @var{"20"})
+@item @code{connectionPriorityTcpWan} (default: @var{"30"})
+@item @code{connectionPriorityQuicWan} (default: @var{"40"})
+@item @code{connectionPriorityRelay} (default: @var{"50"})
+@item @code{connectionPriorityUpgradeThreshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+The default-* above do not affect folders and devices added by the Guix
+interface. They will, however, affect folders and devices that are
+added through the GUI, by an ``introducer'', or a device with
+``autoAcceptFolders''.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to sync with.
+
+@table @asis
+@item @code{id}
+A long hash tied to the keys generated by Syncthing on the first launch.
+You can obtain this from the Syncthing GUI or by inspecting an existing
+Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+Human readable device name for viewing in the GUI or in scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skipIntroductionRemovals} (default: @var{"false"})
+@item @code{introducedBy} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. The special value
+``dynamic'' will have syncthing use several means to find the device.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{autoAcceptFolders} (default: @var{"false"})
+@item @code{maxSendKbps} (default: @var{"0"})
+@item @code{maxRecvKbps} (default: @var{"0"})
+@item @code{maxRequestKiB} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remoteGUIPort} (default: @var{"0"})
+@item @code{numConnections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synced.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This id cannot match the id of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+Human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescanIntervalS} (default: @var{"3600"})
+@item @code{fsWatcherEnabled} (default: @var{"true"})
+@item @code{fsWatcherDelayS} (default: @var{"10"})
+@item @code{ignorePerms} (default: @var{"false"})
+@item @code{autoNormalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+Devices should be a list of other Syncthing devices. You do not need to
+specify the current device. Each device can be listed as a a
+@code{syncthing-device} record or a @code{syncthing-folder-device}
+record if you want files to be encrypted on disk.
+
+@item @code{filesystemType} (default: @var{"basic"})
+@item @code{minDiskFree-unit} (default: @var{"%"})
+@item @code{minDiskFree} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fsPath} (default: @var{""})
+@item @code{versioning-fsType} (default: @var{"basic"})
+@item @code{versioning-cleanupIntervalS} (default: @var{"3600"})
+@item @code{versioning-cleanoutDays} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-maxAge} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{pullerMaxPendingKiB} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignoreDelete} (default: @var{"false"})
+@item @code{scanProgressIntervalS} (default: @var{"0"})
+@item @code{pullerPauseS} (default: @var{"0"})
+@item @code{maxConflicts} (default: @var{"10"})
+@item @code{disableSparseFiles} (default: @var{"false"})
+@item @code{disableTempIndexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weakHashThresholdPct} (default: @var{"25"})
+@item @code{markerName} (default: @var{".stfolder"})
+@item @code{copyOwnershipFromParent} (default: @var{"false"})
+@item @code{modTimeWindowS} (default: @var{"0"})
+@item @code{maxConcurrentWrites} (default: @var{"2"})
+@item @code{disableFsync} (default: @var{"false"})
+@item @code{blockPullOrder} (default: @var{"standard"})
+@item @code{copyRangeMethod} (default: @var{"standard"})
+@item @code{caseSensitiveFS} (default: @var{"false"})
+@item @code{junctionsAsDirs} (default: @var{"false"})
+@item @code{syncOwnership} (default: @var{"false"})
+@item @code{sendOwnership} (default: @var{"false"})
+@item @code{syncXattrs} (default: @var{"false"})
+@item @code{sendXattrs} (default: @var{"false"})
+@item @code{xattrFilter-maxSingleEntrySize} (default: @var{"1024"})
+@item @code{xattrFilter-maxTotalSize} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+There is some configuration which is specific to the relationship
+between a specific folder and a specific device. If you are fine
+leaving these as their default, then you can simply specify a
+syncthing-device instead of a @code{syncthing-folder-device} in
+@code{syncthing-folder}s.
+
+@table @asis
+@item @code{device}
+device should be a @code{syncthing-device} for which this configuration
+applies.
+
+@item @code{introducedBy} (default: @var{""})
+@item @code{encryptionPassword} (default: @var{""})
+if encryptionPassword is non-empty, then it will be used as a password
+to encrypt file chunks as they are synced to that device. For more info
+on syncing to devices you don't totally trust, see
+@uref{https://docs.syncthing.net/users/untrusted.html, Syncthing Documentation Untrusted}.
+Note that file transfers are always end-to-end encrypted, regardless of
+this setting.
+
@end table
@end deftp
+Here is a more complex example configuration for illustrative purposes:
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("mydomain.example"))))
+ (bob-desktop (syncthing-device (id "KYIMEGO-...-FT77EAO"))))
+ (syncthing-configuration
+ (user "alice")
+ (config-file
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (device bob-desktop)
+ (encryptionPassword "mypassword"))))))))))))
+@end lisp
+
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
@cindex SSH server
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-service-type syncthing-service-type))
+ ;; system->home-service-type does not convert special-files-service-type to
+ ;; home-files-service-type, so redefine extensios
+ (extensions (list (service-extension home-files-service-type
+ syncthing-files-service)
+ (service-extension home-shepherd-service-type
+ syncthing-shepherd-service)))
(default-value (for-home (syncthing-configuration)))))
diff --git a/gnu/services/syncthing.scm b/gnu/services/syncthing.scm
index a7a9c6aadd..5b9567b716 100644
--- a/gnu/services/syncthing.scm
+++ b/gnu/services/syncthing.scm
@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2023 Justin Veilleux <terramorpha@cock.li>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -25,9 +26,20 @@ (define-module (gnu services syncthing)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
+ #:use-module (sxml simple)
#:export (syncthing-configuration
syncthing-configuration?
- syncthing-service-type))
+ syncthing-device
+ syncthing-device?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-folder-device
+ syncthing-folder-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-service-type
+ syncthing-shepherd-service
+ syncthing-files-service))
;;; Commentary:
;;;
@@ -35,6 +47,438 @@ (define-module (gnu services syncthing)
;;;
;;; Code:
+(define-record-type* <syncthing-device>
+ syncthing-device make-syncthing-device
+ syncthing-device?
+ (id syncthing-device-id)
+ (name syncthing-device-name (default ""))
+ (compression syncthing-device-compression (default "metadata"))
+ (introducer syncthing-device-introducer (default "false"))
+ (skipIntroductionRemovals syncthing-device-skipIntroductionRemovals (default "false"))
+ (introducedBy syncthing-device-introducedBy (default ""))
+ (addresses syncthing-device-addresses (default '("dynamic")))
+ (paused syncthing-device-paused (default "false"))
+ (autoAcceptFolders syncthing-device-autoAcceptFolders (default "false"))
+ (maxSendKbps syncthing-device-maxSendKbps (default "0"))
+ (maxRecvKbps syncthing-device-maxRecvKbps (default "0"))
This message was truncated. Download the full message here.
Leo Famulari wrote 1 months ago
(name . Zacchaeus)(address . eikcaz@zacchae.us)(address . 75959@debbugs.gnu.org)
Z6aM5zRpu3CdICgd@jasmine.lan
Attachment: file
Zacchaeus Scheffer wrote 1 months ago
(name . Leo Famulari)(address . leo@famulari.name)(address . 75959@debbugs.gnu.org)
874j15gs0l.fsf@zacchae.us
Leo Famulari <leo@famulari.name> writes:

Toggle quote (13 lines)
>> Subject: [PATCH v5] services: syncthing: Added support for config file
>> serialization.
>
> Great! I tested the system-level service according to the config in my
> last email and was able to successfully integrate into my existing
> Syncthing cluster.
>
> Since we have already worked out the bugs on IRC, my feedback here will
> be largely cosmetic.
>
> I think of this patch as being more about generating Syncthing config
> files, rather than serializing them.

Makes sense. I'll update serialization -> generation

Toggle quote (14 lines)
>> * gnu/services/syncthing.scm: (syncthing-config-file) (syncthing-folder)
>> (syncthing-device) (syncthing-folder-device): New records;
>> (syncthing-service-type): added special-files-service-type extension for the
>> config file; (syncthing-files-service): service to create config file
>> * gnu/home/services/syncthing.scm: (home-syncthing-service-type): extended
>> home-files-services-type and re-exported more things from
>> gnu/services/syncthing.scm
>> * doc/guix.texi: (syncthing-service-type): document additions
>
> In the commit title and message, please capitalize and punctuate
> sentences normally, and avoid unecessary abbreviations. Try to write
> imperative sentences. For example, "Add support for configuration file
> serialization [or generation]".

Will do.

Toggle quote (4 lines)
> When listing the new records / variables, they can be contained in the
> same parenthetical list, like (syncthing-config-file, syncthing-folder,
> [...]): New records.

Interesting. I'll do as you suggest, but that's not what I've seen in
other commit messages. e.g. commit
e73cf57a204f2bf430c90930394afa08e9ec3399

Toggle quote (26 lines)
>> @@ -22669,9 +22670,298 @@ This assumes that the specified group exists.
>> Common configuration and data directory. The default configuration
>> directory is @file{$HOME} of the specified Syncthing @code{user}.
>>
>> +@item @code{config-file} (default: @var{#f})
>> +Either a file-like object that resolves to a syncthing configuration xml
>> +file, or a syncthing-config-file record (see below). If set to #f, Guix
>> +will not try to generate a config file, and the syncthing will generate
>> +a default one which will not be touched on reconfigure. Specifying this
>> +in a system service moves Syncthing's common configuration and data
>> +directory to @file{/var/lib/syncthnig-<user>}.
>
> Again, use standard capitalization, etc. Also, use texinfo markup and
> cross-references when useful:
>
> https://www.gnu.org/software/texinfo/manual/texinfo/html_node/Cross-References.html
>
> For example, "Either a file-like object that resolves to a Syncthing
> configuration XML file, or a @code{syncthing-config-file} record (see
> below). When set to @code{#f} [...]"
>
> If you like, I can copy-edit the documentation portion of this patch for
> spelling, style, and grammar, as well as mark it up with texinfo. Let me
> know. If you'd prefer I don't, I'll send another review with feedback on
> those subjects.

I'll try to clean up these points myself first (I feel bad giving you
more work), but feel free to make changes as you see fit. I'm aware
that my writing needs work...

Toggle quote (24 lines)
>> +In the below, only details specific to Guix, or related to how your
>> +device will ``ping'' others, are presented. Otherwise, you should
>> +consult @uref{https://docs.syncthing.net/users/config.html, Syncthing
>> +config documentation}. Camelcase is preserved below only as to be
>> +consistent with its appearance in Syncthing code/documentation. If you
>> +would like to migrate to Guix-powered Syncthing configuration, the
>> +generated config adds newlines/whitespace to the produced config such
>> +that your old config can be diff'ed with the new one. You can still
>> +modify Syncthing from the GUI or through ``introducer'' and
>> +``autoAcceptFolders'' mechanisms, but such changes will be reset on
>> +reconfigure.
>
> I have some misgivings about the use of camel case in user-facing
> interfaces. I do understand the utility of preserving the upstream
> names, but I wonder how much utility it really provides. In my
> experience, it's not typical to edit Syncthing's config.xml "by hand" (I
> did it some when I first started with Syncthing), so I'd expect users'
> to have little familiarity with these names, nor for them to desire a
> direct mapping between them. Additionally, there is utility in all Guix
> interfaces sharing a consistent style, and the camel case is not
> surfaced in Syncthing's own GUI.
>
> What do you think?

I explained my reasoning in this thread and in the IRC, and didn't
recieve much pushback. However, I hadn't even realized that the
Syncthing GUI doesn't use camelcase... I've been generating config
files via Emacs Org Mode src blocks with heavy use of noweb-refs, so my
experience is probably skewed. If this were my library, I would
definitely use camelcase because I think that is clearest, but it's not
that important to me, and I don't think the draw-backs are that large.
I'll just put more effort into adding links to syncthing documentation
for things that don't map so obviously (e.g. ldap-transport is not found
in the documentation as ldapTransport, it is the transport field of the
ldap element).

Toggle quote (7 lines)
>> +@item @code{localAnnouncePort} (default: @var{"21027"})
>> +@item @code{localAnnounceMCAddr} (default: @var{"[ff12::8384]:21027"})
>
> Not important for this patch, but do you have any ideas to facilitate
> multi-user systems, with respect to automatically using alternate
> ports?

No, and I'm not sure you should as automatically changing a port means
another machine doesn't know which port to look at (or so I assume,
maybe nameservers fix this issue, but I'd rather not bake in a
dependancy on third-party services). Maybe this would make sense if
using guix-deploy because then you could tell each machine how the ports
match up.

Toggle quote (11 lines)
>> +@deftp {Data Type} syncthing-folder-device
>> +There is some configuration which is specific to the relationship
>> +between a specific folder and a specific device. If you are fine
>> +leaving these as their default, then you can simply specify a
>> +syncthing-device instead of a @code{syncthing-folder-device} in
>> +@code{syncthing-folder}s.
>
> I think this paragraph is not clear enough, although it starts to make
> sense after reading about the parameters. Still I think there is room
> for improvement.

Basically, if you have D devices and F folders, then there are D*F
relationships between those folders. Specifically, we need to know, for
a given folder, if a device should only recieve encrypted data, and from
where we know that device (so we can automatically remove this device if
the introducing device is removed). This could be accomplished by
having devices be listed in a folder as a tuple (device password
introducer), but I think that would be abusing lists, so a record seems
appropriate here. I'll try and make this text more clear as to this
record's function.

Toggle quote (8 lines)
>> +if encryptionPassword is non-empty, then it will be used as a password
>> +to encrypt file chunks as they are synced to that device. For more info
>> +on syncing to devices you don't totally trust, see
>> +@uref{https://docs.syncthing.net/users/untrusted.html, Syncthing Documentation Untrusted}.
>
> I would write the hyperlink as "Syncthing Documentation on Untrusted
> Devices".

Will do

Toggle quote (13 lines)
>> +Here is a more complex example configuration for illustrative purposes:
>> +@lisp
>> +(service syncthing-service-type
>> + (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
>> + (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
>> + (addresses '("mydomain.example"))))
>
> Let's use the reserved domain name 'example.com'. Also, Syncthing
> requires these addresses to use a "protocol specific prefix", so how
> about "tcp://example.com"?
>
> https://docs.syncthing.net/users/config.html#config-option-device.address

Yes that's right

Toggle quote (16 lines)
>> @@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
>
> Okay. Sounds like you've tested the home service satisfactorily.
>
>> @@ -25,9 +26,20 @@ (define-module (gnu services syncthing)
>> +;; Some parameters, when empty, are fully omitted from the config file. It is
>> +;; unknown if this causes a functional difference, but stick to the normal
>> +;; program's behavior to be safe.
>
> Agreed
>
>> + (gui-apikey syncthing-config-gui-apikey (default "Vuky3VHVseQEoSk9YgxhSkNTnjQmqYK9"))
>
> I've never used the GUI API, but shouldn't this key be generated
> per-device, rather than hard-coded here?

That's a good point. It's probably bad to have ANY default here. I
just tested, and syncthing does not complain when apikey is omitted, so
I've made that change. I'll add notes to this, the GUI password, and
encryption passwords that these are stored in /gnu/store and are
globally readable (GUI password is encrypted, but encryption passwords
are plain-text).

Toggle quote (8 lines)
>> +;; It is useful to be able to view the xml output by Guix, and to be able to
>> +;; diff it with a user's previous config, especially when migrating one's
>> +;; config to Guix. This function adds whitespace that matches the whitespace
>> +;; of config files managed by Syncthing for easy diffing.
>> +(define (indent-sxml sxml indent-increment current-indent)
>
> Nice. Hopefully this doesn't go stale anytime soon.

The whitespace rules have been consistent for as long as I've used
syncthing, so my hopes are high.

Toggle quote (3 lines)
> Overall, it's looking good. Thanks a lot for this contribution! Let me
> know what you think about the feedback I've given here.

Thank you for reviewing! I'll submit another patch shortly implementing
what we have discussed.
Zacchaeus Scheffer wrote 1 months ago
[PATCH v6] services: syncthing: Add support for config file generation.
87zfixf6yg.fsf@zacchae.us
From c63deb42caf24f34a22b1a56c37146d1cfdff4ae Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH v6] services: syncthing: Add support for config file
generation.

* gnu/services/syncthing.scm: (syncthing-config-file,
syncthing-folder, syncthing-device, syncthing-folder-device): New
records; (syncthing-service-type): Add special-files-service-type
extension for the config file; (syncthing-files-service): Add service
to create config file.
* gnu/home/services/syncthing.scm: (home-syncthing-service-type):
Extend home-files-services-type and re-exported more things from
gnu/services/syncthing.scm.
* doc/guix.texi: (syncthing-service-type): Document changes.

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---
I noticed a bug in the documentation; the syncthing-configuration home
field does (and did) not do what it claimed. I went ahead and updated
that documentation since I was already updating the Syncthing section.
I translated camelCase to kebab-case. Hopefully this does not cause
much confusion for the end user.

I tried to improve my grammar from my previous patch, but I don't think
I was successful; feel free to make any of those corrections/additions
as you see fit. I also remembered to warn the user about any sensitive
information they may be putting in /gnu/store. I wrapped all the record
instances I could find in @code{...}, but I may have missed some.

I also updated the commit comment as per lfam's request.

doc/guix.texi | 308 +++++++++++++++++-
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 533 ++++++++++++++++++++++++++++++--
3 files changed, 826 insertions(+), 32 deletions(-)

Toggle diff (384 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..d8f35c5b16 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22666,12 +22667,315 @@ The group as which the Syncthing service is to be run.
This assumes that the specified group exists.
@item @code{home} (default: @var{#f})
-Common configuration and data directory. The default configuration
-directory is @file{$HOME} of the specified Syncthing @code{user}.
+Sets the @code{HOME} variable for the syncthing daemon. The default is
+@file{$HOME} of the specified Syncthing @code{user}.
+
+@item @code{config-file} (default: @var{#f})
+Either a file-like object that resolves to a Syncthing configuration xml
+file, or a @code{syncthing-config-file} record (see below). If set to
+@code{#f}, Guix will not try to generate a config file, and Syncthing
+will generate a default one which will not be touched on reconfigure.
+Specifying this in a system service moves Syncthing's common
+configuration and data directory (@code{--home} in
+@uref{https://docs.syncthing.net/users/syncthing.html}) to
+@file{/var/lib/syncthnig-<user>}.
+
+@end table
+@end deftp
+
+In the below, only details specific to Guix, or related to how your
+device will connect to unrelated devices (relays, nameservers), are
+presented. Otherwise, you should consult
+@uref{https://docs.syncthing.net/users/config.html, Syncthing config
+documentation}; camelCase there is converted to kebab-case here. If
+you would like to migrate to a Guix-powered Syncthing configuration, the
+generated config adds newlines/whitespace to the produced config such
+that your old config can be @code{diff}'ed with the new one. You can
+still modify Syncthing from the GUI or through @code{introducer} and
+@code{autoAcceptFolders} mechanisms, but such changes will be reset on
+reconfigure.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the Syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s. Guix will
+automatically add any devices specified in any `folders' to this list.
+There are instances when you want to connect to a device despite not
+(initially) sharing any folders (such as a device with
+autoAcceptFolders). In such instances, you should specify those devices
+here. If multiple versions of the same device (as determined by
+comparing device id) are discovered, the one in this list is
+prioritized. Otherwise, the first instance in the first folder is used.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+gui-user and gui-password (see below).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-send-basic-auth-prompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+A bcrypt hash of the GUI password. Remember that this will be globally
+exposed in @file{/gnu/store}.
+@item @code{gui-apikey} (default: @var{#f})
+You must specify this to use the Syncthing REST interface. Also exposed
+in @file{/gnu/store}.
+
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bind-dn} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecure-skip-verify} (default: @var{""})
+@item @code{ldap-search-base-dn} (default: @var{""})
+@item @code{ldap-search-filter} (default: @var{""})
+@item @code{listen-address} (default: @var{"default"})
+@item @code{global-announce-server} (default: @var{"default"})
+@item @code{global-announce-enabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{local-announce-enabled} (default: @var{"true"})
+This makes devices find each other very easily on the same LAN. Often,
+this will allow you to just plug an Ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{local-announce-port} (default: @var{"21027"})
+@item @code{local-announce-mcaddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{reconnection-interval-s} (default: @var{"60"})
+@item @code{relays-enabled} (default: @var{"true"})
+This option allows your Syncthing instance to coordinate with a global
+network of relays to enable syncing between devices when all other
+methods fail.
+
+@item @code{relay-reconnect-interval-m} (default: @var{"10"})
+@item @code{start-browser} (default: @var{"true"})
+@item @code{nat-enabled} (default: @var{"true"})
+@item @code{nat-lease-minutes} (default: @var{"60"})
+@item @code{nat-renewal-minutes} (default: @var{"30"})
+@item @code{nat-timeout-seconds} (default: @var{"10"})
+@item @code{ur-accepted} (default: @var{"0"})
+ur* options control usage reporting. Set to -1 to disable, or positive
+to enable. The default (0) has reporting disabled, but you will be
+asked to decide in the GUI.
+
+@item @code{ur-seen} (default: @var{"0"})
+@item @code{ur-unique-id} (default: @var{""})
+@item @code{ur-url} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{ur-post-insecurely} (default: @var{"false"})
+@item @code{ur-initial-delay-s} (default: @var{"1800"})
+@item @code{auto-upgrade-interval-h} (default: @var{"12"})
+@item @code{upgrade-to-pre-releases} (default: @var{"false"})
+@item @code{keep-temporaries-h} (default: @var{"24"})
+@item @code{cache-ignored-files} (default: @var{"false"})
+@item @code{progress-update-interval-s} (default: @var{"5"})
+@item @code{limit-bandwidth-in-lan} (default: @var{"false"})
+@item @code{min-home-disk-free-unit} (default: @var{"%"})
+@item @code{min-home-disk-free} (default: @var{"1"})
+@item @code{releases-url} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwrite-remote-device-names-on-connect} (default: @var{"false"})
+@item @code{temp-index-min-blocks} (default: @var{"10"})
+@item @code{unacked-notification-id} (default: @var{"authenticationUserAndPassword"})
+@item @code{traffic-class} (default: @var{"0"})
+@item @code{set-low-priority} (default: @var{"true"})
+@item @code{max-folder-concurrency} (default: @var{"0"})
+@item @code{crash-reporting-url} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crash-reporting-enabled} (default: @var{"true"})
+@item @code{stun-keepalive-start-s} (default: @var{"180"})
+@item @code{stun-keepalive-min-s} (default: @var{"20"})
+@item @code{stun-server} (default: @var{"default"})
+@item @code{database-tuning} (default: @var{"auto"})
+@item @code{max-concurrent-incoming-request-kib} (default: @var{"0"})
+@item @code{announce-lan-addresses} (default: @var{"true"})
+@item @code{send-full-index-on-upgrade} (default: @var{"false"})
+@item @code{connection-limit-enough} (default: @var{"0"})
+@item @code{connection-limit-max} (default: @var{"0"})
+@item @code{insecure-allow-old-tls-versions} (default: @var{"false"})
+@item @code{connection-priority-tcp-lan} (default: @var{"10"})
+@item @code{connection-priority-quic-lan} (default: @var{"20"})
+@item @code{connection-priority-tcp-wan} (default: @var{"30"})
+@item @code{connection-priority-quic-wan} (default: @var{"40"})
+@item @code{connection-priority-relay} (default: @var{"50"})
+@item @code{connection-priority-upgrade-threshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+The default-* above do not affect folders and devices added by the Guix
+interface. They will, however, affect folders and devices that are
+added through the GUI, by an @code{introducer}, or a device with
+@code{auto-accept-folders}.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synced.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This id cannot match the id of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+Human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescan-interval-s} (default: @var{"3600"})
+@item @code{fs-watcher-enabled} (default: @var{"true"})
+@item @code{fs-watcher-delay-s} (default: @var{"10"})
+@item @code{ignore-perms} (default: @var{"false"})
+@item @code{auto-normalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+This should be a list of other Syncthing devices. You do not need to
+specify the current device. Each device can be listed as a a
+@code{syncthing-device} record or a @code{syncthing-folder-device}
+record if you want files to be encrypted on disk. See below.
+
+@item @code{filesystem-type} (default: @var{"basic"})
+@item @code{min-disk-free-unit} (default: @var{"%"})
+@item @code{min-disk-free} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fs-path} (default: @var{""})
+@item @code{versioning-fs-type} (default: @var{"basic"})
+@item @code{versioning-cleanup-interval-s} (default: @var{"3600"})
+@item @code{versioning-cleanout-days} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-max-age} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{puller-max-pending-kib} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignore-delete} (default: @var{"false"})
+@item @code{scan-progress-interval-s} (default: @var{"0"})
+@item @code{puller-pause-s} (default: @var{"0"})
+@item @code{max-conflicts} (default: @var{"10"})
+@item @code{disable-sparse-files} (default: @var{"false"})
+@item @code{disable-temp-indexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weak-hash-threshold-pct} (default: @var{"25"})
+@item @code{marker-name} (default: @var{".stfolder"})
+@item @code{copy-ownership-from-parent} (default: @var{"false"})
+@item @code{mod-time-window-s} (default: @var{"0"})
+@item @code{max-concurrent-writes} (default: @var{"2"})
+@item @code{disable-fsync} (default: @var{"false"})
+@item @code{block-pull-order} (default: @var{"standard"})
+@item @code{copy-range-method} (default: @var{"standard"})
+@item @code{case-sensitive-fs} (default: @var{"false"})
+@item @code{junctions-as-dirs} (default: @var{"false"})
+@item @code{sync-ownership} (default: @var{"false"})
+@item @code{send-ownership} (default: @var{"false"})
+@item @code{sync-xattrs} (default: @var{"false"})
+@item @code{send-xattrs} (default: @var{"false"})
+@item @code{xattr-filter-max-single-entry-size} (default: @var{"1024"})
+@item @code{xattr-filter-max-total-size} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to sync with.
+
+@table @asis
+@item @code{id}
+A long hash tied to the keys generated by Syncthing on the first launch.
+You can obtain this from the Syncthing GUI or by inspecting an existing
+Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+Human readable device name for viewing in the GUI or in scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skip-introduction-removals} (default: @var{"false"})
+@item @code{introduced-by} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. The special value
+``dynamic'' will have syncthing use several means to find the device.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{auto-accept-folders} (default: @var{"false"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{max-request-kib} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remote-gui-port} (default: @var{"0"})
+@item @code{num-connections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+There are two pieces of configuration specific to the relationship
+between a specific folder and a specific device. First, syncthing needs
+to know if the remote device should be able to see the files given to
+it. Second, syncthing needs to know from where that device originates.
+See
+@href{https://docs.syncthing.net/users/config.html#config-option-folder.device,
+the Syncthing Documentation on this}. If you leave these default, then
+you can just specify @code{syncthing-device}s instead of
+@code{syncthing-folder-device}s in a @code{syncthing-folder}'s
+@code{devices} field.
+
+@table @asis
+@item @code{device}
+device should be a @code{syncthing-device} for which this configuration
+applies.
+
+@item @code{introduced-by} (default: @var{""})
+@item @code{encryption-password} (default: @var{""})
+Beware: specifying this field will include this password as plain text
+(not encrypted) and globally visible in @file{/gnu/store/}. If
+encryption-password is non-empty, then it will be used as a password to
+encrypt file chunks as they are synced to that device. For more info on
+syncing to devices you don't totally trust, see
+@uref{https://docs.syncthing.net/users/untrusted.html, Syncthing
+Documentation on Untrusted Devices}. Note that file transfers are
+always end-to-end encrypted, regardless of this setting.
@end table
@end deftp
+Here is a more complex example configuration for illustrative purposes:
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("tcp://example.com"))))
+ (bob-desktop (syncthing-device (id "KYIMEGO-...-FT77EAO"))))
+ (syncthing-configuration
+ (user "alice")
+ (config-file
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (device bob-desktop)
+ (encryption-password "mypassword"))))))))))))
+@end lisp
+
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
@cindex SSH server
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-service-type syncthing-service-type))
+ ;; system->home-service-type does not convert special-files-service-type to
+ ;; home-files-service-type, so redefine extensios
+ (extensions (list (service-extension home-files-service-type
+ syncthing-files-service)
+ (service-extension home-shepherd-service-type
+ syncthing-shepherd-service)))
(default-value (for-home (syncthing-configuration)))))
diff --git a/gnu/services/syncthing.scm b/gnu/services/syncthing.scm
index a7a9c6aadd..bdc00ec51f 100644
--- a/gnu/services/syncthing.scm
+++ b/gnu/services/syncthing.scm
@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2023 Justin Veilleux <terramorpha@cock.li>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -25,9 +26,20 @@ (define-module (gnu services syncthing)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
+ #:use-module (sxml simple)
#:export (syncthing-configurati
This message was truncated. Download the full message here.
Bruno Victal wrote 1 months ago
Re: [bug#75959] [PATCH v5] services: syncthing: Added support for config file serialization.
(address . 75959@debbugs.gnu.org)
72eb5b99-dd8d-4fa9-95fd-9054980ec92b@makinata.eu
On 2025-02-07 22:44, Leo Famulari wrote:
Toggle quote (11 lines)
>> +;; It is useful to be able to view the xml output by Guix, and to be able to
>> +;; diff it with a user's previous config, especially when migrating one's
>> +;; config to Guix. This function adds whitespace that matches the whitespace
>> +;; of config files managed by Syncthing for easy diffing.
>> +(define (indent-sxml sxml indent-increment current-indent)
>
> Nice. Hopefully this doesn't go stale anytime soon.
>
> Overall, it's looking good. Thanks a lot for this contribution! Let me
> know what you think about the feedback I've given here.

I think this addition is unnecessary since there's a well-known command
to pretty-print any XML file:
xmllint --format <the-file>

Admittedly to my untrained eyes, this indent-sxml looks complex at a
superficial glance and just adds maintenance burden without necessity
in light of xmllint existence IMO.

My 2¢,

--
Bruno.
Zacchaeus Scheffer wrote 1 months ago
87v7tkfbof.fsf@zacchae.us
From 45e289b4960e04c07dafedd39140ed2331b2be81 Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH v7] services: syncthing: Add support for config file
generation.

* gnu/services/syncthing.scm: (syncthing-config-file,
syncthing-folder, syncthing-device, syncthing-folder-device): New
records; (syncthing-service-type): Add special-files-service-type
extension for the config file; (syncthing-files-service): Add service
to create config file.
* gnu/home/services/syncthing.scm: (home-syncthing-service-type):
Extend home-files-services-type and re-exported more things from
gnu/services/syncthing.scm.
* doc/guix.texi: (syncthing-service-type): Document changes.

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---

As per Bruno's suggestion, I removed the (indent-sxml ...) function,
and added intstructions in the documentation for how to get the same
results with libxml2. I like my configs auto-indented, but Guix isn't
really the place for that functionality. If I feel inspired at some
point, maybe I'll try to add that functionality to sxml, but those
chances are low.

eikcaz-

doc/guix.texi | 316 +++++++++++++++++++-
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 506 ++++++++++++++++++++++++++++++--
3 files changed, 807 insertions(+), 32 deletions(-)

Toggle diff (395 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..d5fdda733e 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22666,12 +22667,323 @@ The group as which the Syncthing service is to be run.
This assumes that the specified group exists.
@item @code{home} (default: @var{#f})
-Common configuration and data directory. The default configuration
-directory is @file{$HOME} of the specified Syncthing @code{user}.
+Sets the @code{HOME} variable for the syncthing daemon. The default is
+@file{$HOME} of the specified Syncthing @code{user}.
+
+@item @code{config-file} (default: @var{#f})
+Either a file-like object that resolves to a Syncthing configuration xml
+file, or a @code{syncthing-config-file} record (see below). If set to
+@code{#f}, Guix will not try to generate a config file, and Syncthing
+will generate a default one which will not be touched on reconfigure.
+Specifying this in a system service moves Syncthing's common
+configuration and data directory (@code{--home} in
+@uref{https://docs.syncthing.net/users/syncthing.html}) to
+@file{/var/lib/syncthnig-<user>}.
+
+@end table
+@end deftp
+
+In the below, only details specific to Guix, or related to how your
+device will connect to unrelated devices (relays, nameservers), are
+presented. Otherwise, you should consult
+@uref{https://docs.syncthing.net/users/config.html, Syncthing config
+documentation}; camelCase there is converted to kebab-case here. If you
+are migrating to a Guix-powered Syncthing config file from a
+GUI-configured one, you can see what changes were introduced by
+@code{diff}ing them. Note that you will need to add whitespace with
+4-space indentation to the one generated by Guix. This can be done
+(requires @code{libxml2}) like so:
+
+@example
+XMLLINT_INDENT=" " xmllint --format /path/to/new/config.xml | diff /path/to/old/config.xml -
+@end example
+
+When generating a config file through Guix, you can still modify
+Syncthing from the GUI or through @code{introducer} and
+@code{autoAcceptFolders} mechanisms, but such changes will be reset on
+reconfigure.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the Syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s. Guix will
+automatically add any devices specified in any `folders' to this list.
+There are instances when you want to connect to a device despite not
+(initially) sharing any folders (such as a device with
+autoAcceptFolders). In such instances, you should specify those devices
+here. If multiple versions of the same device (as determined by
+comparing device id) are discovered, the one in this list is
+prioritized. Otherwise, the first instance in the first folder is used.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+gui-user and gui-password (see below).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-send-basic-auth-prompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+A bcrypt hash of the GUI password. Remember that this will be globally
+exposed in @file{/gnu/store}.
+@item @code{gui-apikey} (default: @var{#f})
+You must specify this to use the Syncthing REST interface. Also exposed
+in @file{/gnu/store}.
+
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bind-dn} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecure-skip-verify} (default: @var{""})
+@item @code{ldap-search-base-dn} (default: @var{""})
+@item @code{ldap-search-filter} (default: @var{""})
+@item @code{listen-address} (default: @var{"default"})
+@item @code{global-announce-server} (default: @var{"default"})
+@item @code{global-announce-enabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{local-announce-enabled} (default: @var{"true"})
+This makes devices find each other very easily on the same LAN. Often,
+this will allow you to just plug an Ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{local-announce-port} (default: @var{"21027"})
+@item @code{local-announce-mcaddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{reconnection-interval-s} (default: @var{"60"})
+@item @code{relays-enabled} (default: @var{"true"})
+This option allows your Syncthing instance to coordinate with a global
+network of relays to enable syncing between devices when all other
+methods fail.
+
+@item @code{relay-reconnect-interval-m} (default: @var{"10"})
+@item @code{start-browser} (default: @var{"true"})
+@item @code{nat-enabled} (default: @var{"true"})
+@item @code{nat-lease-minutes} (default: @var{"60"})
+@item @code{nat-renewal-minutes} (default: @var{"30"})
+@item @code{nat-timeout-seconds} (default: @var{"10"})
+@item @code{ur-accepted} (default: @var{"0"})
+ur* options control usage reporting. Set to -1 to disable, or positive
+to enable. The default (0) has reporting disabled, but you will be
+asked to decide in the GUI.
+
+@item @code{ur-seen} (default: @var{"0"})
+@item @code{ur-unique-id} (default: @var{""})
+@item @code{ur-url} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{ur-post-insecurely} (default: @var{"false"})
+@item @code{ur-initial-delay-s} (default: @var{"1800"})
+@item @code{auto-upgrade-interval-h} (default: @var{"12"})
+@item @code{upgrade-to-pre-releases} (default: @var{"false"})
+@item @code{keep-temporaries-h} (default: @var{"24"})
+@item @code{cache-ignored-files} (default: @var{"false"})
+@item @code{progress-update-interval-s} (default: @var{"5"})
+@item @code{limit-bandwidth-in-lan} (default: @var{"false"})
+@item @code{min-home-disk-free-unit} (default: @var{"%"})
+@item @code{min-home-disk-free} (default: @var{"1"})
+@item @code{releases-url} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwrite-remote-device-names-on-connect} (default: @var{"false"})
+@item @code{temp-index-min-blocks} (default: @var{"10"})
+@item @code{unacked-notification-id} (default: @var{"authenticationUserAndPassword"})
+@item @code{traffic-class} (default: @var{"0"})
+@item @code{set-low-priority} (default: @var{"true"})
+@item @code{max-folder-concurrency} (default: @var{"0"})
+@item @code{crash-reporting-url} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crash-reporting-enabled} (default: @var{"true"})
+@item @code{stun-keepalive-start-s} (default: @var{"180"})
+@item @code{stun-keepalive-min-s} (default: @var{"20"})
+@item @code{stun-server} (default: @var{"default"})
+@item @code{database-tuning} (default: @var{"auto"})
+@item @code{max-concurrent-incoming-request-kib} (default: @var{"0"})
+@item @code{announce-lan-addresses} (default: @var{"true"})
+@item @code{send-full-index-on-upgrade} (default: @var{"false"})
+@item @code{connection-limit-enough} (default: @var{"0"})
+@item @code{connection-limit-max} (default: @var{"0"})
+@item @code{insecure-allow-old-tls-versions} (default: @var{"false"})
+@item @code{connection-priority-tcp-lan} (default: @var{"10"})
+@item @code{connection-priority-quic-lan} (default: @var{"20"})
+@item @code{connection-priority-tcp-wan} (default: @var{"30"})
+@item @code{connection-priority-quic-wan} (default: @var{"40"})
+@item @code{connection-priority-relay} (default: @var{"50"})
+@item @code{connection-priority-upgrade-threshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+The default-* above do not affect folders and devices added by the Guix
+interface. They will, however, affect folders and devices that are
+added through the GUI, by an @code{introducer}, or a device with
+@code{auto-accept-folders}.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synced.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This id cannot match the id of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+Human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescan-interval-s} (default: @var{"3600"})
+@item @code{fs-watcher-enabled} (default: @var{"true"})
+@item @code{fs-watcher-delay-s} (default: @var{"10"})
+@item @code{ignore-perms} (default: @var{"false"})
+@item @code{auto-normalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+This should be a list of other Syncthing devices. You do not need to
+specify the current device. Each device can be listed as a a
+@code{syncthing-device} record or a @code{syncthing-folder-device}
+record if you want files to be encrypted on disk. See below.
+
+@item @code{filesystem-type} (default: @var{"basic"})
+@item @code{min-disk-free-unit} (default: @var{"%"})
+@item @code{min-disk-free} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fs-path} (default: @var{""})
+@item @code{versioning-fs-type} (default: @var{"basic"})
+@item @code{versioning-cleanup-interval-s} (default: @var{"3600"})
+@item @code{versioning-cleanout-days} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-max-age} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{puller-max-pending-kib} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignore-delete} (default: @var{"false"})
+@item @code{scan-progress-interval-s} (default: @var{"0"})
+@item @code{puller-pause-s} (default: @var{"0"})
+@item @code{max-conflicts} (default: @var{"10"})
+@item @code{disable-sparse-files} (default: @var{"false"})
+@item @code{disable-temp-indexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weak-hash-threshold-pct} (default: @var{"25"})
+@item @code{marker-name} (default: @var{".stfolder"})
+@item @code{copy-ownership-from-parent} (default: @var{"false"})
+@item @code{mod-time-window-s} (default: @var{"0"})
+@item @code{max-concurrent-writes} (default: @var{"2"})
+@item @code{disable-fsync} (default: @var{"false"})
+@item @code{block-pull-order} (default: @var{"standard"})
+@item @code{copy-range-method} (default: @var{"standard"})
+@item @code{case-sensitive-fs} (default: @var{"false"})
+@item @code{junctions-as-dirs} (default: @var{"false"})
+@item @code{sync-ownership} (default: @var{"false"})
+@item @code{send-ownership} (default: @var{"false"})
+@item @code{sync-xattrs} (default: @var{"false"})
+@item @code{send-xattrs} (default: @var{"false"})
+@item @code{xattr-filter-max-single-entry-size} (default: @var{"1024"})
+@item @code{xattr-filter-max-total-size} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to sync with.
+
+@table @asis
+@item @code{id}
+A long hash tied to the keys generated by Syncthing on the first launch.
+You can obtain this from the Syncthing GUI or by inspecting an existing
+Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+Human readable device name for viewing in the GUI or in scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skip-introduction-removals} (default: @var{"false"})
+@item @code{introduced-by} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. The special value
+``dynamic'' will have syncthing use several means to find the device.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{auto-accept-folders} (default: @var{"false"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{max-request-kib} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remote-gui-port} (default: @var{"0"})
+@item @code{num-connections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+There are two pieces of configuration specific to the relationship
+between a specific folder and a specific device. First, syncthing needs
+to know if the remote device should be able to see the files given to
+it. Second, syncthing needs to know from where that device originates.
+See
+@href{https://docs.syncthing.net/users/config.html#config-option-folder.device,
+the Syncthing Documentation on this}. If you leave these default, then
+you can just specify @code{syncthing-device}s instead of
+@code{syncthing-folder-device}s in a @code{syncthing-folder}'s
+@code{devices} field.
+
+@table @asis
+@item @code{device}
+device should be a @code{syncthing-device} for which this configuration
+applies.
+
+@item @code{introduced-by} (default: @var{""})
+@item @code{encryption-password} (default: @var{""})
+Beware: specifying this field will include this password as plain text
+(not encrypted) and globally visible in @file{/gnu/store/}. If
+encryption-password is non-empty, then it will be used as a password to
+encrypt file chunks as they are synced to that device. For more info on
+syncing to devices you don't totally trust, see
+@uref{https://docs.syncthing.net/users/untrusted.html, Syncthing
+Documentation on Untrusted Devices}. Note that file transfers are
+always end-to-end encrypted, regardless of this setting.
@end table
@end deftp
+Here is a more complex example configuration for illustrative purposes:
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("tcp://example.com"))))
+ (bob-desktop (syncthing-device (id "KYIMEGO-...-FT77EAO"))))
+ (syncthing-configuration
+ (user "alice")
+ (config-file
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (device bob-desktop)
+ (encryption-password "mypassword"))))))))))))
+@end lisp
+
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
@cindex SSH server
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-service-type syncthing-service-type))
+ ;; system->home-service-type does not convert special-files-service-type to
+ ;; home-files-service-type, so redefine extensios
+ (extensions (list (service-extension home-files-service-type
+ syncthing-files-service)
+ (service-extension home-shepherd-service-type
+ syncthing-shepherd-service)))
(default-value (for-home (syncthing-configuration)))))
diff --git a/gnu/services/syncthing.scm b/gnu/services/syncthing.scm
index a7a9c6aadd..2707591d51 100644
--- a/gnu/services/syncthing.scm
+++ b/gnu/services/syncthing.scm
@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2023 Justin Veilleux <terramorpha@cock.li>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -25,9 +26,20 @@ (define-module (gnu services syncthing)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
+ #:use-module (sxml simple)
#:export (syncthing-configuration
syncthing-configuration?
- syncthing-service-type))
+
This message was truncated. Download the full message here.
Zacchaeus Scheffer wrote 4 weeks ago
(name . Bruno Victal)(address . mirai@makinata.eu)
87r045f7r8.fsf@zacchae.us
Urgent: I only tested with pre-inst-env and didn't realize the updates
to doc/guix.texi cause guix to not build, so don't push upstream until
that's fixed.

eikcaz-
Zacchaeus Scheffer wrote 4 weeks ago
[PATCH v8] services: syncthing: Add support for config file generation.
(address . 75959@debbugs.gnu.org)
87o6z9f3o4.fsf@zacchae.us
From dff3153afdd31c754d7d3ddb640a2d33d43f1242 Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH v8] services: syncthing: Add support for config file
generation.

* gnu/services/syncthing.scm: (syncthing-config-file,
syncthing-folder, syncthing-device, syncthing-folder-device): New
records; (syncthing-service-type): Add special-files-service-type
extension for the config file; (syncthing-files-service): Add service
to create config file.
* gnu/home/services/syncthing.scm: (home-syncthing-service-type):
Extend home-files-services-type and re-exported more things from
gnu/services/syncthing.scm.
* doc/guix.texi: (syncthing-service-type): Document changes.

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---

All this testing and deploying with pre-inst-env, and I never even ran
make... This version builds successfully. Feel free to correct any
grammar errors. (I did try.)

eikcaz-

doc/guix.texi | 317 +++++++++++++++++++-
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 506 ++++++++++++++++++++++++++++++--
3 files changed, 808 insertions(+), 32 deletions(-)

Toggle diff (401 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..4756db30c0 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22666,12 +22667,324 @@ The group as which the Syncthing service is to be run.
This assumes that the specified group exists.
@item @code{home} (default: @var{#f})
-Common configuration and data directory. The default configuration
-directory is @file{$HOME} of the specified Syncthing @code{user}.
+Sets the @code{HOME} variable for the syncthing daemon. The default is
+@file{$HOME} of the specified Syncthing @code{user}.
+
+@item @code{config-file} (default: @var{#f})
+Either a file-like object that resolves to a Syncthing configuration xml
+file, or a @code{syncthing-config-file} record (see below). If set to
+@code{#f}, Guix will not try to generate a config file, and Syncthing
+will generate a default one which will not be touched on reconfigure.
+Specifying this in a system service moves Syncthing's common
+configuration and data directory (@code{--home} in
+@uref{https://docs.syncthing.net/users/syncthing.html}) to
+@file{/var/lib/syncthnig-<user>}.
+
+@end table
+@end deftp
+
+In the below, only details specific to Guix, or related to how your
+device will connect to unrelated devices (relays, nameservers), are
+presented. Otherwise, you should consult
+@uref{https://docs.syncthing.net/users/config.html, Syncthing config
+documentation}; camelCase there is converted to kebab-case here. If you
+are migrating to a Guix-powered Syncthing config file from a
+GUI-configured one, you can see what changes were introduced by
+@code{diff}ing them. Note that you will need to add whitespace with
+4-space indentation to the one generated by Guix. This can be done
+(requires @code{libxml2}) like so:
+
+@example
+XMLLINT_INDENT=" " xmllint --format /path/to/new/config.xml | diff /path/to/old/config.xml -
+@end example
+
+When generating a config file through Guix, you can still modify
+Syncthing from the GUI or through @code{introducer} and
+@code{autoAcceptFolders} mechanisms, but such changes will be reset on
+reconfigure.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the Syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s. Guix will
+automatically add any devices specified in any `folders' to this list.
+There are instances when you want to connect to a device despite not
+(initially) sharing any folders (such as a device with
+autoAcceptFolders). In such instances, you should specify those devices
+here. If multiple versions of the same device (as determined by
+comparing device id) are discovered, the one in this list is
+prioritized. Otherwise, the first instance in the first folder is used.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+gui-user and gui-password (see below).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-send-basic-auth-prompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+A bcrypt hash of the GUI password. Remember that this will be globally
+exposed in @file{/gnu/store}.
+@item @code{gui-apikey} (default: @var{#f})
+You must specify this to use the Syncthing REST interface. Also exposed
+in @file{/gnu/store}.
+
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bind-dn} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecure-skip-verify} (default: @var{""})
+@item @code{ldap-search-base-dn} (default: @var{""})
+@item @code{ldap-search-filter} (default: @var{""})
+@item @code{listen-address} (default: @var{"default"})
+@item @code{global-announce-server} (default: @var{"default"})
+@item @code{global-announce-enabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{local-announce-enabled} (default: @var{"true"})
+This makes devices find each other very easily on the same LAN. Often,
+this will allow you to just plug an Ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{local-announce-port} (default: @var{"21027"})
+@item @code{local-announce-mcaddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{reconnection-interval-s} (default: @var{"60"})
+@item @code{relays-enabled} (default: @var{"true"})
+This option allows your Syncthing instance to coordinate with a global
+network of relays to enable syncing between devices when all other
+methods fail.
+
+@item @code{relay-reconnect-interval-m} (default: @var{"10"})
+@item @code{start-browser} (default: @var{"true"})
+@item @code{nat-enabled} (default: @var{"true"})
+@item @code{nat-lease-minutes} (default: @var{"60"})
+@item @code{nat-renewal-minutes} (default: @var{"30"})
+@item @code{nat-timeout-seconds} (default: @var{"10"})
+@item @code{ur-accepted} (default: @var{"0"})
+ur* options control usage reporting. Set to -1 to disable, or positive
+to enable. The default (0) has reporting disabled, but you will be
+asked to decide in the GUI.
+
+@item @code{ur-seen} (default: @var{"0"})
+@item @code{ur-unique-id} (default: @var{""})
+@item @code{ur-url} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{ur-post-insecurely} (default: @var{"false"})
+@item @code{ur-initial-delay-s} (default: @var{"1800"})
+@item @code{auto-upgrade-interval-h} (default: @var{"12"})
+@item @code{upgrade-to-pre-releases} (default: @var{"false"})
+@item @code{keep-temporaries-h} (default: @var{"24"})
+@item @code{cache-ignored-files} (default: @var{"false"})
+@item @code{progress-update-interval-s} (default: @var{"5"})
+@item @code{limit-bandwidth-in-lan} (default: @var{"false"})
+@item @code{min-home-disk-free-unit} (default: @var{"%"})
+@item @code{min-home-disk-free} (default: @var{"1"})
+@item @code{releases-url} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwrite-remote-device-names-on-connect} (default: @var{"false"})
+@item @code{temp-index-min-blocks} (default: @var{"10"})
+@item @code{unacked-notification-id} (default: @var{"authenticationUserAndPassword"})
+@item @code{traffic-class} (default: @var{"0"})
+@item @code{set-low-priority} (default: @var{"true"})
+@item @code{max-folder-concurrency} (default: @var{"0"})
+@item @code{crash-reporting-url} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crash-reporting-enabled} (default: @var{"true"})
+@item @code{stun-keepalive-start-s} (default: @var{"180"})
+@item @code{stun-keepalive-min-s} (default: @var{"20"})
+@item @code{stun-server} (default: @var{"default"})
+@item @code{database-tuning} (default: @var{"auto"})
+@item @code{max-concurrent-incoming-request-kib} (default: @var{"0"})
+@item @code{announce-lan-addresses} (default: @var{"true"})
+@item @code{send-full-index-on-upgrade} (default: @var{"false"})
+@item @code{connection-limit-enough} (default: @var{"0"})
+@item @code{connection-limit-max} (default: @var{"0"})
+@item @code{insecure-allow-old-tls-versions} (default: @var{"false"})
+@item @code{connection-priority-tcp-lan} (default: @var{"10"})
+@item @code{connection-priority-quic-lan} (default: @var{"20"})
+@item @code{connection-priority-tcp-wan} (default: @var{"30"})
+@item @code{connection-priority-quic-wan} (default: @var{"40"})
+@item @code{connection-priority-relay} (default: @var{"50"})
+@item @code{connection-priority-upgrade-threshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+The default-* above do not affect folders and devices added by the Guix
+interface. They will, however, affect folders and devices that are
+added through the GUI, by an @code{introducer}, or a device with
+@code{auto-accept-folders}.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synced.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This id cannot match the id of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+Human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescan-interval-s} (default: @var{"3600"})
+@item @code{fs-watcher-enabled} (default: @var{"true"})
+@item @code{fs-watcher-delay-s} (default: @var{"10"})
+@item @code{ignore-perms} (default: @var{"false"})
+@item @code{auto-normalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+This should be a list of other Syncthing devices. You do not need to
+specify the current device. Each device can be listed as a a
+@code{syncthing-device} record or a @code{syncthing-folder-device}
+record if you want files to be encrypted on disk. See below.
+
+@item @code{filesystem-type} (default: @var{"basic"})
+@item @code{min-disk-free-unit} (default: @var{"%"})
+@item @code{min-disk-free} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fs-path} (default: @var{""})
+@item @code{versioning-fs-type} (default: @var{"basic"})
+@item @code{versioning-cleanup-interval-s} (default: @var{"3600"})
+@item @code{versioning-cleanout-days} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-max-age} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{puller-max-pending-kib} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignore-delete} (default: @var{"false"})
+@item @code{scan-progress-interval-s} (default: @var{"0"})
+@item @code{puller-pause-s} (default: @var{"0"})
+@item @code{max-conflicts} (default: @var{"10"})
+@item @code{disable-sparse-files} (default: @var{"false"})
+@item @code{disable-temp-indexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weak-hash-threshold-pct} (default: @var{"25"})
+@item @code{marker-name} (default: @var{".stfolder"})
+@item @code{copy-ownership-from-parent} (default: @var{"false"})
+@item @code{mod-time-window-s} (default: @var{"0"})
+@item @code{max-concurrent-writes} (default: @var{"2"})
+@item @code{disable-fsync} (default: @var{"false"})
+@item @code{block-pull-order} (default: @var{"standard"})
+@item @code{copy-range-method} (default: @var{"standard"})
+@item @code{case-sensitive-fs} (default: @var{"false"})
+@item @code{junctions-as-dirs} (default: @var{"false"})
+@item @code{sync-ownership} (default: @var{"false"})
+@item @code{send-ownership} (default: @var{"false"})
+@item @code{sync-xattrs} (default: @var{"false"})
+@item @code{send-xattrs} (default: @var{"false"})
+@item @code{xattr-filter-max-single-entry-size} (default: @var{"1024"})
+@item @code{xattr-filter-max-total-size} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to sync with.
+
+@table @asis
+@item @code{id}
+A long hash tied to the keys generated by Syncthing on the first launch.
+You can obtain this from the Syncthing GUI or by inspecting an existing
+Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+Human readable device name for viewing in the GUI or in scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skip-introduction-removals} (default: @var{"false"})
+@item @code{introduced-by} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. The special value
+``dynamic'' will have syncthing use several means to find the device.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{auto-accept-folders} (default: @var{"false"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{max-request-kib} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remote-gui-port} (default: @var{"0"})
+@item @code{num-connections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+There are two pieces of configuration specific to the relationship
+between a specific folder and a specific device. First, syncthing needs
+to know if the remote device should be able to see the files given to
+it. Second, syncthing needs to know from where that device originates.
+See
+@uref{https://docs.syncthing.net/users/config.html#config-option-folder.device,
+the Syncthing Documentation on this}. If you leave these default, then
+you can just specify @code{syncthing-device}s instead of
+@code{syncthing-folder-device}s in a @code{syncthing-folder}'s
+@code{devices} field.
+
+@table @asis
+@item @code{device}
+device should be a @code{syncthing-device} for which this configuration
+applies.
+
+@item @code{introduced-by} (default: @var{""})
+@item @code{encryption-password} (default: @var{""})
+Beware: specifying this field will include this password as plain text
+(not encrypted) and globally visible in @file{/gnu/store/}. If
+encryption-password is non-empty, then it will be used as a password to
+encrypt file chunks as they are synced to that device. For more info on
+syncing to devices you don't totally trust, see
+@uref{https://docs.syncthing.net/users/untrusted.html, Syncthing
+Documentation on Untrusted Devices}. Note that file transfers are
+always end-to-end encrypted, regardless of this setting.
@end table
@end deftp
+Here is a more complex example configuration for illustrative purposes:
+
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("tcp://example.com"))))
+ (bob-desktop (syncthing-device (id "KYIMEGO-...-FT77EAO"))))
+ (syncthing-configuration
+ (user "alice")
+ (config-file
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (device bob-desktop)
+ (encryption-password "mypassword"))))))))))))
+@end lisp
+
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
@cindex SSH server
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-service-type syncthing-service-type))
+ ;; system->home-service-type does not convert special-files-service-type to
+ ;; home-files-service-type, so redefine extensios
+ (extensions (list (service-extension home-files-service-type
+ syncthing-files-service)
+ (service-extension home-shepherd-service-type
+ syncthing-shepherd-service)))
(default-value (for-home (syncthing-configuration)))))
diff --git a/gnu/services/syncthing.scm b/gnu/services/syncthing.scm
index a7a9c6aadd..2707591d51 100644
--- a/gnu/services/syncthing.scm
+++ b/gnu/services/syncthing.scm
@@ -1,6 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021 Oleg Pykhalov <go.wigust@gmail.com>
;;; Copyright © 2023 Justin Veilleux <terramorpha@cock.li>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -25,9 +26,20 @@ (define-module (gnu services syncthing)
#:use-module (guix records)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
+ #:use-module (sxml simple)
#:export (syncthing-configuration
syncthing-configuration?
- syncthing-service-type))
+ syncthing-device
+ syncthing-device?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-folder-device
+ syncthing-folder
This message was truncated. Download the full message here.
Leo Famulari wrote 4 weeks ago
(name . Zacchaeus Scheffer)(address . eikcaz@zacchae.us)
Z676SmCiztcorsqK@jasmine.lan
On Tue, Feb 11, 2025 at 12:31:23AM -0500, Zacchaeus Scheffer wrote:
Toggle quote (13 lines)
> Subject: [PATCH v8] services: syncthing: Add support for config file
> generation.
>
> * gnu/services/syncthing.scm: (syncthing-config-file,
> syncthing-folder, syncthing-device, syncthing-folder-device): New
> records; (syncthing-service-type): Add special-files-service-type
> extension for the config file; (syncthing-files-service): Add service
> to create config file.
> * gnu/home/services/syncthing.scm: (home-syncthing-service-type):
> Extend home-files-services-type and re-exported more things from
> gnu/services/syncthing.scm.
> * doc/guix.texi: (syncthing-service-type): Document changes.

Great! I read through the documentation and made numerous stylistic
edits, and also added some references, links, and markup. See the
attached diff. I did not change the structure of the docs. Please let me
know what you think.

Toggle quote (9 lines)
> + (start #~(lambda _
> + ;; if we are managing the config, and it's not a home
> + ;; service, then exepect the config file at
> + ;; /var/lib/syncthing-<user>. This makes sure the ownership
> + ;; is correct
> + (unless (or #$(not config-file) #$home-service?)
> + (system* "chown" #$user (string-append "/var/lib/syncthing-" #$user))
> + (system* "chmod" "700" (string-append "/var/lib/syncthing-" #$user)))

Please use the Guile procedures chown and chmod here, rather than
shelling out. Also, I think the folder's group should be set as well
using the chown procedure; right now it remains owned by root. There are
examples in 'gnu/services'.


Toggle quote (5 lines)
> + (make-forkexec-constructor
> + (append (list (string-append #$syncthing "/bin/syncthing")
> + "--no-browser"
> + "--no-restart"

It will be helpful to add brief Scheme code comments explaining what
these options do.
Attachment: guix.texi.diff
Zacchaeus Scheffer wrote 3 weeks ago
Re: [PATCH v9] services: syncthing: Add support for config file generation.
(name . Leo Famulari)(address . leo@famulari.name)
871pvyg0kd.fsf@zacchae.us
From 1f38aeb8e2b1c5384ac915a09bad7df1f97b2543 Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH v9] services: syncthing: Add support for config file
generation.

* gnu/services/syncthing.scm: (syncthing-config-file,
syncthing-folder, syncthing-device, syncthing-folder-device): New
records; (syncthing-service-type): Add special-files-service-type
extension for the config file; (syncthing-files-service): Add service
to create config file.
* gnu/home/services/syncthing.scm: (home-syncthing-service-type):
Extend home-files-services-type and re-exported more things from
gnu/services/syncthing.scm.
* doc/guix.texi: (syncthing-service-type): Document changes.

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---

I incorporated the changes suggested by lfam, except that I kept the
semi-colon. I double-checked that it was gramatically correct, and I
think it makes to where "there" refers clear. I converted chmod/chown
to guile procedures, and added documentation on some of the flags used
to launch Syncthing.

doc/guix.texi | 334 ++++++++++++++++++++-
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 515 ++++++++++++++++++++++++++++++--
3 files changed, 833 insertions(+), 33 deletions(-)

Toggle diff (392 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..d1fbe5ffd3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22620,7 +22621,7 @@ client.
The @code{(gnu services syncthing)} module provides the following services:
@cindex syncthing
-You might want a syncthing daemon if you have files between two or more
+You might want a Syncthing daemon if you have files between two or more
computers and want to sync them in real time, safely protected from
prying eyes.
@@ -22666,12 +22667,339 @@ The group as which the Syncthing service is to be run.
This assumes that the specified group exists.
@item @code{home} (default: @var{#f})
-Common configuration and data directory. The default configuration
-directory is @file{$HOME} of the specified Syncthing @code{user}.
+Sets the @code{HOME} variable for the Syncthing daemon. The default is
+@file{$HOME} of the specified Syncthing @code{user}.
+
+@item @code{config-file} (default: @var{#f})
+Either a file-like object that resolves to a Syncthing configuration XML
+file, or a @code{syncthing-config-file} record (see below). If set to
+@code{#f}, Guix will not try to generate a config file, and Syncthing
+will generate a default configuration which will not be touched on
+reconfigure. Specifying this in a system service moves Syncthing's
+common configuration and data directory (@code{--home} in
+@uref{https://docs.syncthing.net/users/syncthing.html}) to
+@file{/var/lib/syncthing-<user>}.
+
+@end table
+@end deftp
+
+This section documents a subset of the Syncthing configuration
+options—specifically those related to Guix or those affecting how your
+computer will connect to other computers over the network (such as
+Syncthing relays or discovery servers). The configuration is fully
+documented in the upstream
+@uref{https://docs.syncthing.net/users/config.html, Syncthing config
+documentation}; camelCase there is converted to kebab-case here. If you
+are migrating from a Syncthing-managed configuration to one managed by
+Guix, you can check what changes were introduced by @code{diff}ing the
+respective @file{config.xml} files. Note that you will need to add
+whitespace with 4-space indentation to the file generated by Guix, using
+the @code{xmllint} program from the @code{libxml2} package like so:
+
+@example
+XMLLINT_INDENT=" " xmllint --format /path/to/new/config.xml | diff /path/to/old/config.xml -
+@end example
+
+When generating a configuration file through Guix, you can still
+temporarily modify Syncthing from the GUI or through @code{introducer}
+and @code{autoAcceptFolders} mechanisms, but such changes will be reset
+on reconfigure.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the Syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s. Guix will
+automatically add any devices specified in any `folders' to this list.
+There are instances when you want to connect to a device despite not
+(initially) sharing any folders (such as a device with
+autoAcceptFolders). In such instances, you should specify those devices
+here. If multiple versions of the same device (as determined by
+comparing device ID) are discovered, the one in this list is
+prioritized. Otherwise, the first instance in the first folder is used.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+@code{gui-user} and @code{gui-password} (see below).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-send-basic-auth-prompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+A bcrypt hash of the GUI password. Remember that this will be globally
+exposed in @file{/gnu/store}.
+
+@item @code{gui-apikey} (default: @var{#f})
+You must specify this to use the Syncthing REST interface. This key is
+kept in @file{/gnu/store} and is accessible to all users of the system.
+
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bind-dn} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecure-skip-verify} (default: @var{""})
+@item @code{ldap-search-base-dn} (default: @var{""})
+@item @code{ldap-search-filter} (default: @var{""})
+@item @code{listen-address} (default: @var{"default"})
+@item @code{global-announce-server} (default: @var{"default"})
+@item @code{global-announce-enabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{local-announce-enabled} (default: @var{"true"})
+This makes devices find each other very easily on the same LAN. Often,
+this will allow you to just plug an Ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{local-announce-port} (default: @var{"21027"})
+@item @code{local-announce-mcaddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{reconnection-interval-s} (default: @var{"60"})
+@item @code{relays-enabled} (default: @var{"true"})
+This option allows your Syncthing instance to use a global network of
+@uref{https://docs.syncthing.net/users/relaying.html, relays} to enable
+syncing between devices when all other methods fail. As always,
+Syncthing traffic is encrypted in transport and the relays are unable to
+decrypt it.
+
+@item @code{relay-reconnect-interval-m} (default: @var{"10"})
+@item @code{start-browser} (default: @var{"true"})
+@item @code{nat-enabled} (default: @var{"true"})
+@item @code{nat-lease-minutes} (default: @var{"60"})
+@item @code{nat-renewal-minutes} (default: @var{"30"})
+@item @code{nat-timeout-seconds} (default: @var{"10"})
+@item @code{ur-accepted} (default: @var{"0"})
+Options whose names begin with `ur-' control usage reporting. Set to -1
+to disable, or to a positive value to enable. The default (0) disables
+reporting, but causes a usage reporting consent prompt to be displayed
+in the Syncthing GUI.
+
+@item @code{ur-seen} (default: @var{"0"})
+@item @code{ur-unique-id} (default: @var{""})
+@item @code{ur-url} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{ur-post-insecurely} (default: @var{"false"})
+@item @code{ur-initial-delay-s} (default: @var{"1800"})
+@item @code{auto-upgrade-interval-h} (default: @var{"12"})
+@item @code{upgrade-to-pre-releases} (default: @var{"false"})
+@item @code{keep-temporaries-h} (default: @var{"24"})
+@item @code{cache-ignored-files} (default: @var{"false"})
+@item @code{progress-update-interval-s} (default: @var{"5"})
+@item @code{limit-bandwidth-in-lan} (default: @var{"false"})
+@item @code{min-home-disk-free-unit} (default: @var{"%"})
+@item @code{min-home-disk-free} (default: @var{"1"})
+@item @code{releases-url} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwrite-remote-device-names-on-connect} (default: @var{"false"})
+@item @code{temp-index-min-blocks} (default: @var{"10"})
+@item @code{unacked-notification-id} (default: @var{"authenticationUserAndPassword"})
+@item @code{traffic-class} (default: @var{"0"})
+@item @code{set-low-priority} (default: @var{"true"})
+@item @code{max-folder-concurrency} (default: @var{"0"})
+@item @code{crash-reporting-url} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crash-reporting-enabled} (default: @var{"true"})
+@item @code{stun-keepalive-start-s} (default: @var{"180"})
+@item @code{stun-keepalive-min-s} (default: @var{"20"})
+@item @code{stun-server} (default: @var{"default"})
+@item @code{database-tuning} (default: @var{"auto"})
+@item @code{max-concurrent-incoming-request-kib} (default: @var{"0"})
+@item @code{announce-lan-addresses} (default: @var{"true"})
+@item @code{send-full-index-on-upgrade} (default: @var{"false"})
+@item @code{connection-limit-enough} (default: @var{"0"})
+@item @code{connection-limit-max} (default: @var{"0"})
+@item @code{insecure-allow-old-tls-versions} (default: @var{"false"})
+@item @code{connection-priority-tcp-lan} (default: @var{"10"})
+@item @code{connection-priority-quic-lan} (default: @var{"20"})
+@item @code{connection-priority-tcp-wan} (default: @var{"30"})
+@item @code{connection-priority-quic-wan} (default: @var{"40"})
+@item @code{connection-priority-relay} (default: @var{"50"})
+@item @code{connection-priority-upgrade-threshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+Options whose names begin with `default-' above do not affect folders
+and devices added through the Guix configuration interface. They will,
+however, affect folders and devices that are added through the Syncthing
+GUI, by an @code{introducer}, or a device with
+@code{auto-accept-folders}.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synchronized.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This ID cannot match the ID of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+A human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescan-interval-s} (default: @var{"3600"})
+@item @code{fs-watcher-enabled} (default: @var{"true"})
+@item @code{fs-watcher-delay-s} (default: @var{"10"})
+@item @code{ignore-perms} (default: @var{"false"})
+@item @code{auto-normalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+This should be a list of other Syncthing devices. You do not need to
+specify the current device. Each device can be listed as a a
+@code{syncthing-device} record or a @code{syncthing-folder-device}
+record if you want files to be encrypted on disk. See below.
+
+@item @code{filesystem-type} (default: @var{"basic"})
+@item @code{min-disk-free-unit} (default: @var{"%"})
+@item @code{min-disk-free} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fs-path} (default: @var{""})
+@item @code{versioning-fs-type} (default: @var{"basic"})
+@item @code{versioning-cleanup-interval-s} (default: @var{"3600"})
+@item @code{versioning-cleanout-days} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-max-age} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{puller-max-pending-kib} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignore-delete} (default: @var{"false"})
+@item @code{scan-progress-interval-s} (default: @var{"0"})
+@item @code{puller-pause-s} (default: @var{"0"})
+@item @code{max-conflicts} (default: @var{"10"})
+@item @code{disable-sparse-files} (default: @var{"false"})
+@item @code{disable-temp-indexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weak-hash-threshold-pct} (default: @var{"25"})
+@item @code{marker-name} (default: @var{".stfolder"})
+@item @code{copy-ownership-from-parent} (default: @var{"false"})
+@item @code{mod-time-window-s} (default: @var{"0"})
+@item @code{max-concurrent-writes} (default: @var{"2"})
+@item @code{disable-fsync} (default: @var{"false"})
+@item @code{block-pull-order} (default: @var{"standard"})
+@item @code{copy-range-method} (default: @var{"standard"})
+@item @code{case-sensitive-fs} (default: @var{"false"})
+@item @code{junctions-as-dirs} (default: @var{"false"})
+@item @code{sync-ownership} (default: @var{"false"})
+@item @code{send-ownership} (default: @var{"false"})
+@item @code{sync-xattrs} (default: @var{"false"})
+@item @code{send-xattrs} (default: @var{"false"})
+@item @code{xattr-filter-max-single-entry-size} (default: @var{"1024"})
+@item @code{xattr-filter-max-total-size} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to synchronize folders with.
+
+@table @asis
+@item @code{id}
+A long hash representing the keys generated by Syncthing on the first
+launch. You can obtain this from the Syncthing GUI or by inspecting an
+existing Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+A human readable device name for viewing in the GUI or in Scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skip-introduction-removals} (default: @var{"false"})
+@item @code{introduced-by} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. When the special
+value ``dynamic'' is included, Syncthing will search for the device
+locally as well as via the Syncthing project's
+@uref{https://docs.syncthing.net/users/security.html#global-discovery,
+global discovery} servers.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{auto-accept-folders} (default: @var{"false"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{max-request-kib} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remote-gui-port} (default: @var{"0"})
+@item @code{num-connections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+This data type offers two folder-specific device options. First, it
+offers @code{introduced-by}, which is a record of Syncthing
+@uref{https://docs.syncthing.net/users/introducer.html, introductions}.
+Second, it offers @code{encryption-password}, by which you can set the
+password used to encrypt data that is synced with
+@uref{https://docs.syncthing.net/users/untrusted.html, untrusted
+devices}.
+
+@code{syncthing-folder-device} corresponds to the
+@uref{https://docs.syncthing.net/users/config.html#config-option-folder.device,
+`device'} option in the upstream `folder' element.
+
+If you don't need to use these options, then you can just specify
+@code{syncthing-device}s instead of @code{syncthing-folder-device}s in a
+@code{syncthing-folder}'s @code{devices} field.
+
+@table @asis
+@item @code{device}
+The @code{syncthing-device} for which this configuration applies.
+
+@item @code{introduced-by} (default: @var{""})
+@item @code{encryption-password} (default: @var{""})
+Beware: specifying this field will include this password as plain text
+(not encrypted) and globally visible in @file{/gnu/store/}. If the
+encryption-password is non-empty, then it will be used as a password to
+encrypt file chunks as they are synchronized to untrusted devices. For
+more information on syncing to devices you don't totally trust, see
+Syncthing's documentation on
+@uref{https://docs.syncthing.net/users/untrusted.html, Untrusted
+(Encrypted) Devices}. Note that data transfer is always encrypted while
+in transport ("end-to-end encryption"), regardless of this setting.
@end table
@end deftp
+Here is a more complex example configuration for illustrative purposes:
+
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("tcp://example.com"))))
+ (bob-desktop (syncthing-device (id "KYIMEGO-...-FT77EAO"))))
+ (syncthing-configuration
+ (user "alice")
+ (config-file
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (device bob-desktop)
+ (encryption-password "mypassword"))))))))))))
+@end lisp
+
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
@cindex SSH server
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-ser
This message was truncated. Download the full message here.
Leo Famulari wrote 3 weeks ago
(name . Zacchaeus Scheffer)(address . eikcaz@zacchae.us)
Z7GQJdIve7PXseNH@jasmine.lan
On Sat, Feb 15, 2025 at 08:06:42PM -0500, Zacchaeus Scheffer wrote:
Toggle quote (3 lines)
> Subject: [PATCH v9] services: syncthing: Add support for config file
> generation.

Thanks!

Toggle quote (11 lines)
> + (start #~(lambda _
> + ;; If we are managing the config, and it's not a home
> + ;; service, then exepect the config file at
> + ;; /var/lib/syncthing-<user>. This makes sure the ownership
> + ;; is correct
> + (unless (or #$(not config-file) #$home-service?)
> + (chown (string-append "/var/lib/syncthing-" #$user)
> + (passwd:uid #$user)
> + (passwd:gid #$user))
> + (chmod (string-append "/var/lib/syncthing-" #$user) #o700))

This is making the service crash, not sure why:

------
# herd start syncthing-leo
Service user-homes has been started.
herd: error: exception caught while executing 'start' on service 'syncthing-leo':
In procedure vector-ref: Wrong argument in position 1 (expecting vector): "leo"
------

Of course my user's name is leo in the test VM.

I added some print-style tracing (attached diff) and it crashes in
chown.
Toggle diff (20 lines)
diff --git a/gnu/services/syncthing.scm b/gnu/services/syncthing.scm
index cc88bf7925..9fbcf2c4de 100644
--- a/gnu/services/syncthing.scm
+++ b/gnu/services/syncthing.scm
@@ -492,10 +492,14 @@ (define syncthing-shepherd-service
;; /var/lib/syncthing-<user>. This makes sure the ownership
;; is correct
(unless (or #$(not config-file) #$home-service?)
+ (display "1 before chown") (newline)
(chown (string-append "/var/lib/syncthing-" #$user)
(passwd:uid #$user)
(passwd:gid #$user))
- (chmod (string-append "/var/lib/syncthing-" #$user) #o700))
+ (display "2 before chmod") (newline)
+ (chmod (string-append "/var/lib/syncthing-" #$user) #o700)
+ (display "3 after chmod") (newline)
+)
(make-forkexec-constructor
(append (list (string-append #$syncthing "/bin/syncthing")
;; Do not try to try to lauch a browser on startup.
Zacchaeus Scheffer wrote 3 weeks ago
Re: [PATCH v10] services: syncthing: Add support for config file generation.
(name . Leo Famulari)(address . leo@famulari.name)
87tt8udyg7.fsf_-_@zacchae.us
From a573fd78e6b8d10b32eb10a753423073c7bbaeef Mon Sep 17 00:00:00 2001
From: Zacchaeus <eikcaz@zacchae.us>
Date: Sun, 21 Jul 2024 00:54:25 -0700
Subject: [PATCH v10] services: syncthing: Add support for config file
generation.

* gnu/services/syncthing.scm: (syncthing-config-file,
syncthing-folder, syncthing-device, syncthing-folder-device): New
records; (syncthing-service-type): Add special-files-service-type
extension for the config file; (syncthing-files-service): Add service
to create config file.
* gnu/home/services/syncthing.scm: (home-syncthing-service-type):
Extend home-files-services-type and re-exported more things from
gnu/services/syncthing.scm.
* doc/guix.texi: (syncthing-service-type): Document changes.

Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
---

Last time I forgot to test the system service. Chowning/chmodding now
works. (Needed to add a getpw instead of passing string of user name.)

doc/guix.texi | 334 ++++++++++++++++++++-
gnu/home/services/syncthing.scm | 17 +-
gnu/services/syncthing.scm | 516 ++++++++++++++++++++++++++++++--
3 files changed, 834 insertions(+), 33 deletions(-)

Toggle diff (394 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index b1b6d98e74..d1fbe5ffd3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -136,6 +136,7 @@ Copyright @copyright{} 2024 Troy Figiel@*
Copyright @copyright{} 2024 Sharlatan Hellseher@*
Copyright @copyright{} 2024 45mg@*
Copyright @copyright{} 2025 Sören Tempel@*
+Copyright @copyright{} 2025 Zacchaeus@*
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -22620,7 +22621,7 @@ client.
The @code{(gnu services syncthing)} module provides the following services:
@cindex syncthing
-You might want a syncthing daemon if you have files between two or more
+You might want a Syncthing daemon if you have files between two or more
computers and want to sync them in real time, safely protected from
prying eyes.
@@ -22666,12 +22667,339 @@ The group as which the Syncthing service is to be run.
This assumes that the specified group exists.
@item @code{home} (default: @var{#f})
-Common configuration and data directory. The default configuration
-directory is @file{$HOME} of the specified Syncthing @code{user}.
+Sets the @code{HOME} variable for the Syncthing daemon. The default is
+@file{$HOME} of the specified Syncthing @code{user}.
+
+@item @code{config-file} (default: @var{#f})
+Either a file-like object that resolves to a Syncthing configuration XML
+file, or a @code{syncthing-config-file} record (see below). If set to
+@code{#f}, Guix will not try to generate a config file, and Syncthing
+will generate a default configuration which will not be touched on
+reconfigure. Specifying this in a system service moves Syncthing's
+common configuration and data directory (@code{--home} in
+@uref{https://docs.syncthing.net/users/syncthing.html}) to
+@file{/var/lib/syncthing-<user>}.
+
+@end table
+@end deftp
+
+This section documents a subset of the Syncthing configuration
+options—specifically those related to Guix or those affecting how your
+computer will connect to other computers over the network (such as
+Syncthing relays or discovery servers). The configuration is fully
+documented in the upstream
+@uref{https://docs.syncthing.net/users/config.html, Syncthing config
+documentation}; camelCase there is converted to kebab-case here. If you
+are migrating from a Syncthing-managed configuration to one managed by
+Guix, you can check what changes were introduced by @code{diff}ing the
+respective @file{config.xml} files. Note that you will need to add
+whitespace with 4-space indentation to the file generated by Guix, using
+the @code{xmllint} program from the @code{libxml2} package like so:
+
+@example
+XMLLINT_INDENT=" " xmllint --format /path/to/new/config.xml | diff /path/to/old/config.xml -
+@end example
+
+When generating a configuration file through Guix, you can still
+temporarily modify Syncthing from the GUI or through @code{introducer}
+and @code{autoAcceptFolders} mechanisms, but such changes will be reset
+on reconfigure.
+
+@deftp {Data Type} syncthing-config-file
+Data type representing the configuration file read by the Syncthing
+daemon.
+
+@table @asis
+@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
+The default here is the same as Syncthing's default. The value should
+be a list of @code{syncthing-folder}s.
+
+@item @code{devices} (default: @var{'()}
+This should be a list of @code{syncthing-device}s. Guix will
+automatically add any devices specified in any `folders' to this list.
+There are instances when you want to connect to a device despite not
+(initially) sharing any folders (such as a device with
+autoAcceptFolders). In such instances, you should specify those devices
+here. If multiple versions of the same device (as determined by
+comparing device ID) are discovered, the one in this list is
+prioritized. Otherwise, the first instance in the first folder is used.
+
+@item @code{gui-enabled} (default: @var{"true"})
+By default, any user on the computer can access the GUI and make changes
+to Syncthing. If you leave this enabled, you should probably set
+@code{gui-user} and @code{gui-password} (see below).
+
+@item @code{gui-tls} (default: @var{"false"})
+@item @code{gui-debugging} (default: @var{"false"})
+@item @code{gui-send-basic-auth-prompt} (default: @var{"false"})
+@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
+@item @code{gui-user} (default: @var{#f})
+@item @code{gui-password} (default: @var{#f})
+A bcrypt hash of the GUI password. Remember that this will be globally
+exposed in @file{/gnu/store}.
+
+@item @code{gui-apikey} (default: @var{#f})
+You must specify this to use the Syncthing REST interface. This key is
+kept in @file{/gnu/store} and is accessible to all users of the system.
+
+@item @code{gui-theme} (default: @var{"default"})
+@item @code{ldap-enabled} (default: @var{#f})
+@item @code{ldap-address} (default: @var{""})
+@item @code{ldap-bind-dn} (default: @var{""})
+@item @code{ldap-transport} (default: @var{""})
+@item @code{ldap-insecure-skip-verify} (default: @var{""})
+@item @code{ldap-search-base-dn} (default: @var{""})
+@item @code{ldap-search-filter} (default: @var{""})
+@item @code{listen-address} (default: @var{"default"})
+@item @code{global-announce-server} (default: @var{"default"})
+@item @code{global-announce-enabled} (default: @var{"true"})
+Global discovery servers can be used to help connect devices at unknown
+IP addresses by storing the last known IP address.
+
+@item @code{local-announce-enabled} (default: @var{"true"})
+This makes devices find each other very easily on the same LAN. Often,
+this will allow you to just plug an Ethernet between two devices, or
+connect one device to the other's hotspot and start syncing.
+
+@item @code{local-announce-port} (default: @var{"21027"})
+@item @code{local-announce-mcaddr} (default: @var{"[ff12::8384]:21027"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{reconnection-interval-s} (default: @var{"60"})
+@item @code{relays-enabled} (default: @var{"true"})
+This option allows your Syncthing instance to use a global network of
+@uref{https://docs.syncthing.net/users/relaying.html, relays} to enable
+syncing between devices when all other methods fail. As always,
+Syncthing traffic is encrypted in transport and the relays are unable to
+decrypt it.
+
+@item @code{relay-reconnect-interval-m} (default: @var{"10"})
+@item @code{start-browser} (default: @var{"true"})
+@item @code{nat-enabled} (default: @var{"true"})
+@item @code{nat-lease-minutes} (default: @var{"60"})
+@item @code{nat-renewal-minutes} (default: @var{"30"})
+@item @code{nat-timeout-seconds} (default: @var{"10"})
+@item @code{ur-accepted} (default: @var{"0"})
+Options whose names begin with `ur-' control usage reporting. Set to -1
+to disable, or to a positive value to enable. The default (0) disables
+reporting, but causes a usage reporting consent prompt to be displayed
+in the Syncthing GUI.
+
+@item @code{ur-seen} (default: @var{"0"})
+@item @code{ur-unique-id} (default: @var{""})
+@item @code{ur-url} (default: @var{"https://data.syncthing.net/newdata"})
+@item @code{ur-post-insecurely} (default: @var{"false"})
+@item @code{ur-initial-delay-s} (default: @var{"1800"})
+@item @code{auto-upgrade-interval-h} (default: @var{"12"})
+@item @code{upgrade-to-pre-releases} (default: @var{"false"})
+@item @code{keep-temporaries-h} (default: @var{"24"})
+@item @code{cache-ignored-files} (default: @var{"false"})
+@item @code{progress-update-interval-s} (default: @var{"5"})
+@item @code{limit-bandwidth-in-lan} (default: @var{"false"})
+@item @code{min-home-disk-free-unit} (default: @var{"%"})
+@item @code{min-home-disk-free} (default: @var{"1"})
+@item @code{releases-url} (default: @var{"https://upgrades.syncthing.net/meta.json"})
+@item @code{overwrite-remote-device-names-on-connect} (default: @var{"false"})
+@item @code{temp-index-min-blocks} (default: @var{"10"})
+@item @code{unacked-notification-id} (default: @var{"authenticationUserAndPassword"})
+@item @code{traffic-class} (default: @var{"0"})
+@item @code{set-low-priority} (default: @var{"true"})
+@item @code{max-folder-concurrency} (default: @var{"0"})
+@item @code{crash-reporting-url} (default: @var{"https://crash.syncthing.net/newcrash"})
+@item @code{crash-reporting-enabled} (default: @var{"true"})
+@item @code{stun-keepalive-start-s} (default: @var{"180"})
+@item @code{stun-keepalive-min-s} (default: @var{"20"})
+@item @code{stun-server} (default: @var{"default"})
+@item @code{database-tuning} (default: @var{"auto"})
+@item @code{max-concurrent-incoming-request-kib} (default: @var{"0"})
+@item @code{announce-lan-addresses} (default: @var{"true"})
+@item @code{send-full-index-on-upgrade} (default: @var{"false"})
+@item @code{connection-limit-enough} (default: @var{"0"})
+@item @code{connection-limit-max} (default: @var{"0"})
+@item @code{insecure-allow-old-tls-versions} (default: @var{"false"})
+@item @code{connection-priority-tcp-lan} (default: @var{"10"})
+@item @code{connection-priority-quic-lan} (default: @var{"20"})
+@item @code{connection-priority-tcp-wan} (default: @var{"30"})
+@item @code{connection-priority-quic-wan} (default: @var{"40"})
+@item @code{connection-priority-relay} (default: @var{"50"})
+@item @code{connection-priority-upgrade-threshold} (default: @var{"0"})
+@item @code{default-folder} (default: @var{(syncthing-folder (label ""))})
+@item @code{default-device} (default: @var{(syncthing-device (id ""))})
+@item @code{default-ignores} (default: @var{"")})
+Options whose names begin with `default-' above do not affect folders
+and devices added through the Guix configuration interface. They will,
+however, affect folders and devices that are added through the Syncthing
+GUI, by an @code{introducer}, or a device with
+@code{auto-accept-folders}.
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder
+Data type representing a folder to be synchronized.
+
+@table @asis
+@item @code{id} (default: @var{#f})
+This ID cannot match the ID of any other folder on this device. If left
+unspecified, it will default to the label (see below).
+
+@item @code{label}
+A human readable label for the folder.
+
+@item @code{path}
+The path at which to store this folder.
+
+@item @code{type} (default: @var{"sendreceive"})
+@item @code{rescan-interval-s} (default: @var{"3600"})
+@item @code{fs-watcher-enabled} (default: @var{"true"})
+@item @code{fs-watcher-delay-s} (default: @var{"10"})
+@item @code{ignore-perms} (default: @var{"false"})
+@item @code{auto-normalize} (default: @var{"true"})
+@item @code{devices} (default: @var{'()})
+This should be a list of other Syncthing devices. You do not need to
+specify the current device. Each device can be listed as a a
+@code{syncthing-device} record or a @code{syncthing-folder-device}
+record if you want files to be encrypted on disk. See below.
+
+@item @code{filesystem-type} (default: @var{"basic"})
+@item @code{min-disk-free-unit} (default: @var{"%"})
+@item @code{min-disk-free} (default: @var{"1"})
+@item @code{versioning-type} (default: @var{#f})
+@item @code{versioning-fs-path} (default: @var{""})
+@item @code{versioning-fs-type} (default: @var{"basic"})
+@item @code{versioning-cleanup-interval-s} (default: @var{"3600"})
+@item @code{versioning-cleanout-days} (default: @var{#f})
+@item @code{versioning-keep} (default: @var{#f})
+@item @code{versioning-max-age} (default: @var{#f})
+@item @code{versioning-command} (default: @var{#f})
+@item @code{copiers} (default: @var{"0"})
+@item @code{puller-max-pending-kib} (default: @var{"0"})
+@item @code{hashers} (default: @var{"0"})
+@item @code{order} (default: @var{"random"})
+@item @code{ignore-delete} (default: @var{"false"})
+@item @code{scan-progress-interval-s} (default: @var{"0"})
+@item @code{puller-pause-s} (default: @var{"0"})
+@item @code{max-conflicts} (default: @var{"10"})
+@item @code{disable-sparse-files} (default: @var{"false"})
+@item @code{disable-temp-indexes} (default: @var{"false"})
+@item @code{paused} (default: @var{"false"})
+@item @code{weak-hash-threshold-pct} (default: @var{"25"})
+@item @code{marker-name} (default: @var{".stfolder"})
+@item @code{copy-ownership-from-parent} (default: @var{"false"})
+@item @code{mod-time-window-s} (default: @var{"0"})
+@item @code{max-concurrent-writes} (default: @var{"2"})
+@item @code{disable-fsync} (default: @var{"false"})
+@item @code{block-pull-order} (default: @var{"standard"})
+@item @code{copy-range-method} (default: @var{"standard"})
+@item @code{case-sensitive-fs} (default: @var{"false"})
+@item @code{junctions-as-dirs} (default: @var{"false"})
+@item @code{sync-ownership} (default: @var{"false"})
+@item @code{send-ownership} (default: @var{"false"})
+@item @code{sync-xattrs} (default: @var{"false"})
+@item @code{send-xattrs} (default: @var{"false"})
+@item @code{xattr-filter-max-single-entry-size} (default: @var{"1024"})
+@item @code{xattr-filter-max-total-size} (default: @var{"4096")})
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-device
+Data type representing a device to synchronize folders with.
+
+@table @asis
+@item @code{id}
+A long hash representing the keys generated by Syncthing on the first
+launch. You can obtain this from the Syncthing GUI or by inspecting an
+existing Syncthing configuration file.
+
+@item @code{name} (default: @var{""})
+A human readable device name for viewing in the GUI or in Scheme.
+
+@item @code{compression} (default: @var{"metadata"})
+@item @code{introducer} (default: @var{"false"})
+@item @code{skip-introduction-removals} (default: @var{"false"})
+@item @code{introduced-by} (default: @var{""})
+@item @code{addresses} (default: @var{'("dynamic")})
+List of addresses at which to search for this device. When the special
+value ``dynamic'' is included, Syncthing will search for the device
+locally as well as via the Syncthing project's
+@uref{https://docs.syncthing.net/users/security.html#global-discovery,
+global discovery} servers.
+
+@item @code{paused} (default: @var{"false"})
+@item @code{auto-accept-folders} (default: @var{"false"})
+@item @code{max-send-kbps} (default: @var{"0"})
+@item @code{max-recv-kbps} (default: @var{"0"})
+@item @code{max-request-kib} (default: @var{"0"})
+@item @code{untrusted} (default: @var{"false"})
+@item @code{remote-gui-port} (default: @var{"0"})
+@item @code{num-connections} (default: @var{"0")})
+
+@end table
+@end deftp
+
+@deftp {Data Type} syncthing-folder-device
+This data type offers two folder-specific device options. First, it
+offers @code{introduced-by}, which is a record of Syncthing
+@uref{https://docs.syncthing.net/users/introducer.html, introductions}.
+Second, it offers @code{encryption-password}, by which you can set the
+password used to encrypt data that is synced with
+@uref{https://docs.syncthing.net/users/untrusted.html, untrusted
+devices}.
+
+@code{syncthing-folder-device} corresponds to the
+@uref{https://docs.syncthing.net/users/config.html#config-option-folder.device,
+`device'} option in the upstream `folder' element.
+
+If you don't need to use these options, then you can just specify
+@code{syncthing-device}s instead of @code{syncthing-folder-device}s in a
+@code{syncthing-folder}'s @code{devices} field.
+
+@table @asis
+@item @code{device}
+The @code{syncthing-device} for which this configuration applies.
+
+@item @code{introduced-by} (default: @var{""})
+@item @code{encryption-password} (default: @var{""})
+Beware: specifying this field will include this password as plain text
+(not encrypted) and globally visible in @file{/gnu/store/}. If the
+encryption-password is non-empty, then it will be used as a password to
+encrypt file chunks as they are synchronized to untrusted devices. For
+more information on syncing to devices you don't totally trust, see
+Syncthing's documentation on
+@uref{https://docs.syncthing.net/users/untrusted.html, Untrusted
+(Encrypted) Devices}. Note that data transfer is always encrypted while
+in transport ("end-to-end encryption"), regardless of this setting.
@end table
@end deftp
+Here is a more complex example configuration for illustrative purposes:
+
+@lisp
+(service syncthing-service-type
+ (let ((laptop (syncthing-device (id "VHOD2D6-...-7XRMDEN")))
+ (desktop (syncthing-device (id "64SAZ37-...-FZJ5GUA")
+ (addresses '("tcp://example.com"))))
+ (bob-desktop (syncthing-device (id "KYIMEGO-...-FT77EAO"))))
+ (syncthing-configuration
+ (user "alice")
+ (config-file
+ (syncthing-config-file
+ (folders (list (syncthing-folder
+ (label "some-files")
+ (path "~/data")
+ (devices (list desktop laptop)))
+ (syncthing-folder
+ (label "critical-files")
+ (path "~/secrets")
+ (devices
+ (list desktop
+ laptop
+ (syncthing-folder-device
+ (device bob-desktop)
+ (encryption-password "mypassword"))))))))))))
+@end lisp
+
+
Furthermore, @code{(gnu services ssh)} provides the following services.
@cindex SSH
@cindex SSH server
diff --git a/gnu/home/services/syncthing.scm b/gnu/home/services/syncthing.scm
index 8d66a167ce..dd6c752ee4 100644
--- a/gnu/home/services/syncthing.scm
+++ b/gnu/home/services/syncthing.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2025 Zacchaeus <eikcaz@zacchae.us>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,9 +25,23 @@ (define-module (gnu home services syncthing)
#:use-module (gnu home services shepherd)
#:export (home-syncthing-service-type)
#:re-export (syncthing-configuration
- syncthing-configuration?))
+ syncthing-configuration?
+ syncthing-config-file
+ syncthing-config-file?
+ syncthing-device
+ syncthing-device?
+ syncthing-folder
+ syncthing-folder?
+ syncthing-folder-device
+ syncthing-folder-device?))
(define home-syncthing-service-type
(service-type
(inherit (system->home-service-type syncthing-service-type))
+ ;; system->home-service-type does not convert special-files-service-type to
+ ;; home-files-service-type, so redefine
This message was truncated. Download the full message here.
Leo Famulari wrote 3 weeks ago
(name . Zacchaeus Scheffer)(address . eikcaz@zacchae.us)
Z7LT29gC0lnKfjFX@jasmine.lan
On Sun, Feb 16, 2025 at 04:35:20AM -0500, Zacchaeus Scheffer wrote:
Toggle quote (16 lines)
> From a573fd78e6b8d10b32eb10a753423073c7bbaeef Mon Sep 17 00:00:00 2001
> From: Zacchaeus <eikcaz@zacchae.us>
> Date: Sun, 21 Jul 2024 00:54:25 -0700
> Subject: [PATCH v10] services: syncthing: Add support for config file
> generation.
>
> * gnu/services/syncthing.scm: (syncthing-config-file,
> syncthing-folder, syncthing-device, syncthing-folder-device): New
> records; (syncthing-service-type): Add special-files-service-type
> extension for the config file; (syncthing-files-service): Add service
> to create config file.
> * gnu/home/services/syncthing.scm: (home-syncthing-service-type):
> Extend home-files-services-type and re-exported more things from
> gnu/services/syncthing.scm.
> * doc/guix.texi: (syncthing-service-type): Document changes.

Pushed as 651f8765b657e35baf85ac74a1f6b09ff71691cb

Thank you for your contribution to Guix!
Closed
Ludovic Courtès wrote 3 weeks ago
Re: [bug#75959] [PATCH v10] services: syncthing: Add support for config file generation.
(name . Zacchaeus Scheffer)(address . eikcaz@zacchae.us)
87bjv0n9zb.fsf@gnu.org
Hi,

Zacchaeus Scheffer <eikcaz@zacchae.us> skribis:

Toggle quote (18 lines)
>>From a573fd78e6b8d10b32eb10a753423073c7bbaeef Mon Sep 17 00:00:00 2001
> From: Zacchaeus <eikcaz@zacchae.us>
> Date: Sun, 21 Jul 2024 00:54:25 -0700
> Subject: [PATCH v10] services: syncthing: Add support for config file
> generation.
>
> * gnu/services/syncthing.scm: (syncthing-config-file,
> syncthing-folder, syncthing-device, syncthing-folder-device): New
> records; (syncthing-service-type): Add special-files-service-type
> extension for the config file; (syncthing-files-service): Add service
> to create config file.
> * gnu/home/services/syncthing.scm: (home-syncthing-service-type):
> Extend home-files-services-type and re-exported more things from
> gnu/services/syncthing.scm.
> * doc/guix.texi: (syncthing-service-type): Document changes.
>
> Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9

This is a nice improvement! Much better than fiddling with the GUI.
:-)

Sorry for not chiming in earlier; I just noticed a couple of stylistic
issues:

Toggle quote (8 lines)
> +This section documents a subset of the Syncthing configuration
> +options—specifically those related to Guix or those affecting how your
> +computer will connect to other computers over the network (such as
> +Syncthing relays or discovery servers). The configuration is fully
> +documented in the upstream
> +@uref{https://docs.syncthing.net/users/config.html, Syncthing config
> +documentation}; camelCase there is converted to kebab-case here. If you

“Kebab-case”, really? :-)

Toggle quote (3 lines)
> +@table @asis
> +@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}

Should be @code, not @var (throughout this table).

Could you send a patch fixing this?

BTW, ‘define-configuration’ would make it easier to generate this doc.

Toggle quote (2 lines)
> +@item @code{gui-enabled} (default: @var{"true"})

The naming convention used in all of Guix is to add a final question
mark for Boolean values; this would be ‘gui-enabled?’.

As for the value itself, it would help a lot to use #t and #f instead of
the strings "true" and "false" (both of which have truth value in
Scheme). It leads to a bit of extra work in the serializer, but I think
it’s worth it. Because then we can also have type-checking of fields.

Toggle quote (9 lines)
> +@item @code{gui-tls} (default: @var{"false"})
> +@item @code{gui-debugging} (default: @var{"false"})
> +@item @code{gui-send-basic-auth-prompt} (default: @var{"false"})
> +@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
> +@item @code{gui-user} (default: @var{#f})
> +@item @code{gui-password} (default: @var{#f})
> +A bcrypt hash of the GUI password. Remember that this will be globally
> +exposed in @file{/gnu/store}.

I believe you want @itemx for all but the first item.

Toggle quote (6 lines)
> +@item @code{local-announce-port} (default: @var{"21027"})
> +@item @code{local-announce-mcaddr} (default: @var{"[ff12::8384]:21027"})
> +@item @code{max-send-kbps} (default: @var{"0"})
> +@item @code{max-recv-kbps} (default: @var{"0"})
> +@item @code{reconnection-interval-s} (default: @var{"60"})

Similar to what I wrote above: numbers should be numbers, not strings.

Last note: the convention throughout Guix it to avoid abbreviations.

I’m not sure about the way forward; maybe we can make those typing
changes (and perhaps some naming changes) and afford some
incompatibility because the service is young? WDYT?

Ludo’.
Zacchaeus Scheffer wrote 3 weeks ago
(name . Ludovic Courtès)(address . ludo@gnu.org)
87frkc8g8u.fsf@zacchae.us
Ludovic Courtès <ludo@gnu.org> writes:

Toggle quote (25 lines)
> Hi,
>
> Zacchaeus Scheffer <eikcaz@zacchae.us> skribis:
>
>>>From a573fd78e6b8d10b32eb10a753423073c7bbaeef Mon Sep 17 00:00:00 2001
>> From: Zacchaeus <eikcaz@zacchae.us>
>> Date: Sun, 21 Jul 2024 00:54:25 -0700
>> Subject: [PATCH v10] services: syncthing: Add support for config file
>> generation.
>>
>> * gnu/services/syncthing.scm: (syncthing-config-file,
>> syncthing-folder, syncthing-device, syncthing-folder-device): New
>> records; (syncthing-service-type): Add special-files-service-type
>> extension for the config file; (syncthing-files-service): Add service
>> to create config file.
>> * gnu/home/services/syncthing.scm: (home-syncthing-service-type):
>> Extend home-files-services-type and re-exported more things from
>> gnu/services/syncthing.scm.
>> * doc/guix.texi: (syncthing-service-type): Document changes.
>>
>> Change-Id: I87eeba1ee1fdada8f29c2ee881fbc6bc4113dde9
>
> This is a nice improvement! Much better than fiddling with the GUI.
> :-)

Thanks. I'm glad you agree. Great for managing swarms of devices

Toggle quote (13 lines)
> Sorry for not chiming in earlier; I just noticed a couple of stylistic
> issues:
>
>> +This section documents a subset of the Syncthing configuration
>> +options—specifically those related to Guix or those affecting how your
>> +computer will connect to other computers over the network (such as
>> +Syncthing relays or discovery servers). The configuration is fully
>> +documented in the upstream
>> +@uref{https://docs.syncthing.net/users/config.html, Syncthing config
>> +documentation}; camelCase there is converted to kebab-case here. If you
>
> “Kebab-case”, really? :-)

Toggle quote (7 lines)
>> +@table @asis
>> +@item @code{folders} (default: @var{(list (syncthing-folder (id "default") (label "Default Folder") (path "~/Sync")))}
>
> Should be @code, not @var (throughout this table).
>
> Could you send a patch fixing this?

Sure thing. I assume I can send it to the same issue even though it is
closed?

Toggle quote (2 lines)
> BTW, ‘define-configuration’ would make it easier to generate this doc.

I originally used define-record because that is what the original
syncthing service implementation used. If there are reasons to change
this I could update this as well, but it seems fine.

Toggle quote (10 lines)
>> +@item @code{gui-enabled} (default: @var{"true"})
>
> The naming convention used in all of Guix is to add a final question
> mark for Boolean values; this would be ‘gui-enabled?’.
>
> As for the value itself, it would help a lot to use #t and #f instead of
> the strings "true" and "false" (both of which have truth value in
> Scheme). It leads to a bit of extra work in the serializer, but I think
> it’s worth it. Because then we can also have type-checking of fields.

This would be easiest to implement as a sanitizer as in:

(define (bool-string bool)
(if bool "true" "false"))
(define-record-type* <syncthing-config-file>
...
(gui-enabled <accessor> (default "true")
(sanitizer bool-string))
...

But then the guix docs would technically be wrong reporting a default
value of #t instead of the actual default value of "true". I think this
would yield a more helpful type-checking error though, so I'll go this
direction unless you think otherwise.

Toggle quote (11 lines)
>> +@item @code{gui-tls} (default: @var{"false"})
>> +@item @code{gui-debugging} (default: @var{"false"})
>> +@item @code{gui-send-basic-auth-prompt} (default: @var{"false"})
>> +@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
>> +@item @code{gui-user} (default: @var{#f})
>> +@item @code{gui-password} (default: @var{#f})
>> +A bcrypt hash of the GUI password. Remember that this will be globally
>> +exposed in @file{/gnu/store}.
>
> I believe you want @itemx for all but the first item.

Does it matter that the describing text only applies to gui-password,
not to the whole block? For now, I'll assume not and change all the
consecutive items with no specific documentation throughout to itemx.

Toggle quote (8 lines)
>> +@item @code{local-announce-port} (default: @var{"21027"})
>> +@item @code{local-announce-mcaddr} (default: @var{"[ff12::8384]:21027"})
>> +@item @code{max-send-kbps} (default: @var{"0"})
>> +@item @code{max-recv-kbps} (default: @var{"0"})
>> +@item @code{reconnection-interval-s} (default: @var{"60"})
>
> Similar to what I wrote above: numbers should be numbers, not strings.

I'll figure this out as well, using sanitizers (or leaving it as
numbers. It seems sxml allows the body of a tag to be a number, but not
values of parameters of tags, so most the time it just works.)

Toggle quote (2 lines)
> Last note: the convention throughout Guix it to avoid abbreviations.

My goal was to, as much as possible, avoid needing to maintain Syncthing
documentation in Guix. My original patch even had camelcase for record
names so the user could easily search the Syncthing documentation
knowing what keyword to search. Since you can't directly search anymore
anyway, maybe it does make sense to expand out some names. I'll do that
for these:

auth - authorization
s - seconds
m - minutes
h - hours
fs - file-system
pct - percentage
mcaddr - mac-address
recv - recieve
ur - usage-reporting

Toggle quote (6 lines)
> I’m not sure about the way forward; maybe we can make those typing
> changes (and perhaps some naming changes) and afford some
> incompatibility because the service is young? WDYT?
>
> Ludo’.

I think it's unlikely that the service update will have major adoption
in the couple days between patches, so I think a bit of incompatibility
is fine.

eikcaz-
Ludovic Courtès wrote 3 weeks ago
(name . Zacchaeus Scheffer)(address . eikcaz@zacchae.us)
87h64na76x.fsf@gnu.org
Hi,

Zacchaeus Scheffer <eikcaz@zacchae.us> skribis:

Toggle quote (3 lines)
> I believe that is an accurate term:
> https://en.wikipedia.org/wiki/Naming_convention_(programming)#Delimiter-separated_words

Woow, TIL. :-)

Toggle quote (3 lines)
> Sure thing. I assume I can send it to the same issue even though it is
> closed?

Yes, but maybe open a new issue, for clarity.

Toggle quote (20 lines)
>> As for the value itself, it would help a lot to use #t and #f instead of
>> the strings "true" and "false" (both of which have truth value in
>> Scheme). It leads to a bit of extra work in the serializer, but I think
>> it’s worth it. Because then we can also have type-checking of fields.
>
> This would be easiest to implement as a sanitizer as in:
>
> (define (bool-string bool)
> (if bool "true" "false"))
> (define-record-type* <syncthing-config-file>
> ...
> (gui-enabled <accessor> (default "true")
> (sanitizer bool-string))
> ...
>
> But then the guix docs would technically be wrong reporting a default
> value of #t instead of the actual default value of "true". I think this
> would yield a more helpful type-checking error though, so I'll go this
> direction unless you think otherwise.

I would advise against using sanitizer in this way because it would lead
to inconsistencies: users would provide a boolean, but they’d get a
string when calling the accessor.

Instead the record should contain records of the “right” type;
converting to the relevant string should be left to whatever serializes
the record to the config file.

(This is something ‘define-configuration’ really helps with; I think
it’s worth looking into it!)

Toggle quote (14 lines)
>>> +@item @code{gui-tls} (default: @var{"false"})
>>> +@item @code{gui-debugging} (default: @var{"false"})
>>> +@item @code{gui-send-basic-auth-prompt} (default: @var{"false"})
>>> +@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
>>> +@item @code{gui-user} (default: @var{#f})
>>> +@item @code{gui-password} (default: @var{#f})
>>> +A bcrypt hash of the GUI password. Remember that this will be globally
>>> +exposed in @file{/gnu/store}.
>>
>> I believe you want @itemx for all but the first item.
>
> Does it matter that the describing text only applies to gui-password,
> not to the whole block?

It does yes, so my suggestion is actually bogus, sorry!

Normally each field would be documented, not just ‘gui-password’.

Toggle quote (17 lines)
> My goal was to, as much as possible, avoid needing to maintain Syncthing
> documentation in Guix. My original patch even had camelcase for record
> names so the user could easily search the Syncthing documentation
> knowing what keyword to search. Since you can't directly search anymore
> anyway, maybe it does make sense to expand out some names. I'll do that
> for these:
>
> auth - authorization
> s - seconds
> m - minutes
> h - hours
> fs - file-system
> pct - percentage
> mcaddr - mac-address
> recv - recieve
> ur - usage-reporting

Sounds good to me.

Thanks!

Ludo’.
Zacchaeus Scheffer wrote 3 weeks ago
(name . Ludovic Courtès)(address . ludo@gnu.org)
87o6yv6o3j.fsf@zacchae.us
Hi all,

Ludovic Courtès <ludo@gnu.org> writes:

Toggle quote (8 lines)
> Hi,
>
> Zacchaeus Scheffer <eikcaz@zacchae.us> skribis:
>> Sure thing. I assume I can send it to the same issue even though it is
>> closed?
>
> Yes, but maybe open a new issue, for clarity.

I have opened a new issue at #76379

Toggle quote (31 lines)
>>> As for the value itself, it would help a lot to use #t and #f instead of
>>> the strings "true" and "false" (both of which have truth value in
>>> Scheme). It leads to a bit of extra work in the serializer, but I think
>>> it’s worth it. Because then we can also have type-checking of fields.
>>
>> This would be easiest to implement as a sanitizer as in:
>>
>> (define (bool-string bool)
>> (if bool "true" "false"))
>> (define-record-type* <syncthing-config-file>
>> ...
>> (gui-enabled <accessor> (default "true")
>> (sanitizer bool-string))
>> ...
>>
>> But then the guix docs would technically be wrong reporting a default
>> value of #t instead of the actual default value of "true". I think this
>> would yield a more helpful type-checking error though, so I'll go this
>> direction unless you think otherwise.
>
> I would advise against using sanitizer in this way because it would lead
> to inconsistencies: users would provide a boolean, but they’d get a
> string when calling the accessor.
>
> Instead the record should contain records of the “right” type;
> converting to the relevant string should be left to whatever serializes
> the record to the config file.
>
> (This is something ‘define-configuration’ really helps with; I think
> it’s worth looking into it!)

Alright, a suggestion from the Ludo made twice should not be so easily
dismissed. I should mention my original assertion here is slightly
wrong; to work, I should set (default #t), but, as you mention, the
accessor will still retrieve a string instead of a bool. I originally
tried to do my serialization through 'define-configuration', but most of
that work evaporated when I discovered sxml (which is the right way to
do it). Still, there may be some room for improvment translating to
'define-configuration'.

I will investigate how to best translate 'define-record-type*' to
'define-configuration', but in the meantime I think we should prioritize
getting my new patch (#76379) through. I renamed fields and changed
data types per your suggestions, so my new patch is backwards
incompatible with the first.

Toggle quote (16 lines)
>>>> +@item @code{gui-tls} (default: @var{"false"})
>>>> +@item @code{gui-debugging} (default: @var{"false"})
>>>> +@item @code{gui-send-basic-auth-prompt} (default: @var{"false"})
>>>> +@item @code{gui-address} (default: @var{"127.0.0.1:8384"})
>>>> +@item @code{gui-user} (default: @var{#f})
>>>> +@item @code{gui-password} (default: @var{#f})
>>>> +A bcrypt hash of the GUI password. Remember that this will be globally
>>>> +exposed in @file{/gnu/store}.
>>>
>>> I believe you want @itemx for all but the first item.
>>
>> Does it matter that the describing text only applies to gui-password,
>> not to the whole block?
>
> It does yes, so my suggestion is actually bogus, sorry!

Alright, I reverted (most of) my itemx changes in the v2 patch submitted
at #76379.

Toggle quote (2 lines)
> Normally each field would be documented, not just ‘gui-password’.

So here's the problem: Syncthing has a LOT of documentation for a LOT of
fields. Do we really want to maintain all that documentation in Guix?
I omitted documenting the majority of fields, pointing to Syncthing
documentation instead, out of caution for future Guix maintainers, not
laziness. I opted for just documenting (1) any feature that is specific
to Guix configuration and (2) anything that controls to which other
devices your device connects. For anything else, the user should
consult Syncthing documentation. Is my premise flawed, and I should
document everything? Maybe a one-liner for each is unlikely to be
invalidated by future Syncthing updates. WDYT?


eikcaz-
Gabriel Santos wrote 3 weeks ago
Re: [bug#75959] [PATCH v10] services: syncth ing: Add support for config file generation.
A4026AF3-B935-4EE7-94C1-74C690F284D8@disroot.org
Greetings,

I appreciate your efforts on this, but this completly broke
home-syncthing-service-type for me, as it's resetting my configuration,
giving my device another ID.

--
Gabriel Santos
Zacchaeus Scheffer wrote 3 weeks ago
Re: [bug#75959] [PATCH v10] services: syncthing: Add support for config file generation.
(name . Gabriel Santos)(address . gabrielsantosdesouza@disroot.org)
87eczp7lj3.fsf@zacchae.us
Hi,

Gabriel Santos <gabrielsantosdesouza@disroot.org> writes:

Toggle quote (9 lines)
> Greetings,
>
> I appreciate your efforts on this, but this completly broke
> home-syncthing-service-type for me, as it's resetting my configuration,
> giving my device another ID.
>
> --
> Gabriel Santos

I think I know why this is. At some point Syncthing changed the default
config directory from ~/.config/syncthing to ~/.local/state/syncthing.
If you move your syncthing .pem files from ~/.local/state/syncthing to
~/.config/syncthing, it will work. Since it seems Syncthing has changed
the default, this service should really do so as well. I'll update it
on the other patch that I'm submitting.

-Zacchae
Rodion Goritskov wrote 3 weeks ago
(name . Zacchaeus Scheffer)(address . eikcaz@zacchae.us)
87cyf9tv6y.fsf@goritskov.com
Zacchaeus Scheffer <eikcaz@zacchae.us> writes:

Toggle quote (22 lines)
> Hi,
>
> Gabriel Santos <gabrielsantosdesouza@disroot.org> writes:
>
>> Greetings,
>>
>> I appreciate your efforts on this, but this completly broke
>> home-syncthing-service-type for me, as it's resetting my configuration,
>> giving my device another ID.
>>
>> --
>> Gabriel Santos
>
> I think I know why this is. At some point Syncthing changed the default
> config directory from ~/.config/syncthing to ~/.local/state/syncthing.
> If you move your syncthing .pem files from ~/.local/state/syncthing to
> ~/.config/syncthing, it will work. Since it seems Syncthing has changed
> the default, this service should really do so as well. I'll update it
> on the other patch that I'm submitting.
>
> -Zacchae

Hi!

I also have a problem (cannot say if it the same as Gabriel's) - after upgrade my Syncthing
instances stopped working with the odd error "remote device missing in
cluster config".

Moving config didn't work - and, as far as I understand, Syncthing still
read the config in .config if it is present.

However, it looks like it is not because the service itself, but because
of the syncthing upgrade to 1.29.2 in commit
06d37f38606fabbace21e55ec7f2546b3ae5214f.

I checked it by inheriting the Syncthing with the older version:

Toggle quote (14 lines)
> (define-public syncthing-old
> (package
> (inherit syncthing)
> (name "syncthing-old")
> (version "1.28.1")
> (source (origin
> (method url-fetch)
> (uri (string-append "https://github.com/syncthing/syncthing"
> "/releases/download/v" version
> "/syncthing-source-v" version ".tar.gz"))
> (sha256
> (base32
> "16j5w6hdr1x2231hw0zsxm53sw34wxcs4ijjjcnzcg1vz9drjrg9"))))))

And passing it to the server:

Toggle quote (5 lines)
> (service syncthing-with-vpn-service-type
> (syncthing-configuration
> (syncthing syncthing-old)
> (user "rodion")))

After that everything started working as before.
Will try to investigate the issue further - couldn't find anything
related neither in Syncthing form nor in the Github issues.
Zacchaeus Scheffer wrote 3 weeks ago
(name . Rodion Goritskov)(address . rodion@goritskov.com)
871pvp7cv3.fsf@zacchae.us
Rodion Goritskov <rodion@goritskov.com> writes:

Toggle quote (9 lines)
>> (service syncthing-with-vpn-service-type
>> (syncthing-configuration
>> (syncthing syncthing-old)
>> (user "rodion")))
>
> After that everything started working as before.
> Will try to investigate the issue further - couldn't find anything
> related neither in Syncthing form nor in the Github issues.

If you aren't passing a non-#f value for config-file in
syncthing-configuration, then my update should have no affect on your
system, so the issue should be somewhere else, probably in the syncthing
version update you mentioned.

Still, the other issue of syncthing configuration directory location is
one I will address.

-Zacchae
Leo Famulari wrote 2 weeks ago
(name . Rodion Goritskov)(address . rodion@goritskov.com)
Z70o3_gTlKUZFEqW@jasmine.lan
On Sun, Feb 23, 2025 at 12:29:25AM +0100, Rodion Goritskov wrote:
Toggle quote (4 lines)
> I also have a problem (cannot say if it the same as Gabriel's) - after upgrade my Syncthing
> instances stopped working with the odd error "remote device missing in
> cluster config".

I see this too. I don't use the Guix service for Syncthing so it has
nothing to do with that.

Toggle quote (4 lines)
> However, it looks like it is not because the service itself, but because
> of the syncthing upgrade to 1.29.2 in commit
> 06d37f38606fabbace21e55ec7f2546b3ae5214f.

Yes, seems that way.

Toggle quote (4 lines)
> After that everything started working as before.
> Will try to investigate the issue further - couldn't find anything
> related neither in Syncthing form nor in the Github issues.

Let's try to debug a bit before reporting this upstream.
Leo Famulari wrote 2 weeks ago
(name . Rodion Goritskov)(address . rodion@goritskov.com)
Z700P217Z2luFeW0@jasmine.lan
On Sun, Feb 23, 2025 at 12:29:25AM +0100, Rodion Goritskov wrote:
Toggle quote (4 lines)
> However, it looks like it is not because the service itself, but because
> of the syncthing upgrade to 1.29.2 in commit
> 06d37f38606fabbace21e55ec7f2546b3ae5214f.

We can see here that Syncthing v1.29 requires Go 1.22 or newer:


However, the default Go version in Guix is 1.21:


I fixed the bug on our end by building Syncthing with Go 1.23:


Please let me know if you still experience this problem.
Gabriel Santos wrote 2 weeks ago
Re: [bug#75959] [PATCH v10] services: syncth ing: Add support for config file generation.
E14A46BC-04F5-4576-8EF6-3E5F26A3C3AC@disroot.org
Toggle quote (9 lines)
>I see this too. I don't use the Guix service for Syncthing so it has
>nothing to do with that.
>
>> However, it looks like it is not because the service itself, but because
>> of the syncthing upgrade to 1.29.2 in commit
>> 06d37f38606fabbace21e55ec7f2546b3ae5214f.
>
>Yes, seems that way.

Since this isn't related to the service, but rather the version,
I think it would be best to look at the
diff between the tags:


--
Gabriel Santos
Gabriel Santos wrote 2 weeks ago
2B64F5FA-305B-48C6-85CF-85DC333CDCAE@disroot.org
Toggle quote (6 lines)
>I fixed the bug on our end by building Syncthing with Go 1.23:
>
>https://git.savannah.gnu.org/cgit/guix.git/commit/?id=68cd38756b51d4abd8c796a5bcbbb9ea053eafde
>
>Please let me know if you still experience this problem.

Thank you, that was it. Updating solved the issue for me.

--
Gabriel Santos
Rodion Goritskov wrote 2 weeks ago
Re: [bug#75959] [PATCH v10] services: syncthing: Add support for config file generation.
(name . Leo Famulari)(address . leo@famulari.name)
87tt8haa7n.fsf@goritskov.com
Hi!

Syncthing 1.29.2 works great now!

Thank you for the fix, Leo.
?
Your comment

Commenting via the web interface is currently disabled.

To comment on this conversation send an email to 75959@debbugs.gnu.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 75959
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch
You may also tag this issue. See list of standard tags. For example, to set the confirmed and easy tags
mumi command -t +confirmed -t +easy
Or, remove the moreinfo tag and set the help tag
mumi command -t -moreinfo -t +help