From patchwork Sun Feb 16 09:35:20 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Zacchaeus Scheffer X-Patchwork-Id: 38722 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id 4DBE227BBEA; Sun, 16 Feb 2025 09:36:26 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-7.5 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED,RCVD_IN_VALIDITY_RPBL,RCVD_IN_VALIDITY_SAFE, SPF_HELO_PASS,URIBL_SBL_A autolearn=ham autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id CB92027BBE2 for ; Sun, 16 Feb 2025 09:36:23 +0000 (GMT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tjb4I-0008Hf-0X; Sun, 16 Feb 2025 04:36:06 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tjb4F-0008Gp-Bo for guix-patches@gnu.org; Sun, 16 Feb 2025 04:36:03 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tjb4E-0002PD-LX for guix-patches@gnu.org; Sun, 16 Feb 2025 04:36:02 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:References:In-Reply-To:From:To:Subject; bh=DveX72Na6jX8Yp2sZ0Dfklg77k/q82XvlEbAqXO3iL4=; b=MWAq1j6oomSd4OeV/xR/8nc6DQ51mt6SwDcK8o6IwaaR6y+xY5qTyUNFUdkcWWfCr9x+CeCG1MYN/uhC2utTltIoGoVWMc0RY9dQ4DAh2Z8SCF2YUAUIpJnE/OkcRPPyIFEuiVum6d7De34WK7qRUt30ejS9FzHpmEUJX7n09OnI/wD2GoP0AgvX/uIUbMw6eA8JbL+LI5rR3FIs066RQNKttL1KWfqY0/cq1UWy+E8Mjd8HGRBi4HAnw5Umk1WV6Z7RHl2ecoxx1u4KpVD6c/F4lVEQvMBut7qF1IQ5USjw73WlWHb0L9Cly0vLeSu+O4prxxjPIffCU95V9B6dCw==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1tjb4E-0003af-Cb for guix-patches@gnu.org; Sun, 16 Feb 2025 04:36:02 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#75959] [PATCH v10] services: syncthing: Add support for config file generation. Resent-From: Zacchaeus Scheffer Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Sun, 16 Feb 2025 09:36:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 75959 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: Leo Famulari Cc: Bruno Victal , 75959@debbugs.gnu.org Received: via spool by 75959-submit@debbugs.gnu.org id=B75959.173969853313758 (code B ref 75959); Sun, 16 Feb 2025 09:36:02 +0000 Received: (at 75959) by debbugs.gnu.org; 16 Feb 2025 09:35:33 +0000 Received: from localhost ([127.0.0.1]:60193 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tjb3j-0003Zn-Gg for submit@debbugs.gnu.org; Sun, 16 Feb 2025 04:35:33 -0500 Received: from [47.204.136.169] (port=48066 helo=hun.zacchae.us) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1tjb3e-0003ZX-Mb for 75959@debbugs.gnu.org; Sun, 16 Feb 2025 04:35:29 -0500 DKIM-Signature: v=1; a=ed25519-sha256; q=dns/txt; c=relaxed/relaxed; d=zacchae.us; s=my_ed_sel; h=Content-Transfer-Encoding:Content-Type: MIME-Version:Message-ID:Date:References:In-Reply-To:Subject:Cc:To:From:Sender :Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=DveX72Na6jX8Yp2sZ0Dfklg77k/q82XvlEbAqXO3iL4=; i=zacchae.us; b=ssrqvILndeaz JazkKkn4fSSbKg3r1HSsSvkAQQREJKamcG4ivhcRnndouzknJijJ87o6WOHgBuERT39VjFmBAQ==; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=zacchae.us; s=my_rsa_sel; h=Content-Transfer-Encoding:Content-Type:MIME-Version: Message-ID:Date:References:In-Reply-To:Subject:Cc:To:From:Sender:Reply-To: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=DveX72Na6jX8Yp2sZ0Dfklg77k/q82XvlEbAqXO3iL4=; i=zacchae.us; b=a1J9QxFhvWo6 iKZRIHdG58emMzI3pFvYN2JUV/X8htp80by7MVhtCA5A6JVOdNnjKK6GVy2HnyNwNFY6YNl/2ImZ3 X0/wgaKHuwq5BtXUPvchKt5pBwQCfhtGmO/nO6asmz78Y9ZRafV7SkONa9hnX2dlpn1y8TyNFORwA 4lJ2sPhwZKuEpVzIrzRweVIkhDLFpB/FXUUHKit0MkzNAqadM9cbD1oDkNuiT/0PwWVB1NGcq1QYq i2VPVg5o2pS/t/URPPNo0rdqZXfOztjxvPYbDbxxkPdHEdFbKZqEQ05XvpAspOy/tsqO9OL3iLmTR 5WbwDaQs/QjeHR08Deab7A==; Received: from localhost.home ([127.0.0.1]:37756 helo=hun) by hun.zacchae.us with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98) (envelope-from ) id 1tjb3X-000000008Fc-3MOZ; Sun, 16 Feb 2025 04:35:20 -0500 From: Zacchaeus Scheffer In-Reply-To: (Leo Famulari's message of "Sun, 16 Feb 2025 02:13:41 -0500") References: <871pvyg0kd.fsf@zacchae.us> Date: Sun, 16 Feb 2025 04:35:20 -0500 Message-ID: <87tt8udyg7.fsf_-_@zacchae.us> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches From a573fd78e6b8d10b32eb10a753423073c7bbaeef Mon Sep 17 00:00:00 2001 From: Zacchaeus 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(-) 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-}. + +@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 +;;; Copyright © 2025 Zacchaeus ;;; ;;; 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..5cc7eadd57 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 ;;; Copyright © 2023 Justin Veilleux +;;; Copyright © 2025 Zacchaeus ;;; ;;; 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,414 @@ (define-module (gnu services syncthing) ;;; ;;; Code: +(define-record-type* + 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")) + (skip-introduction-removals syncthing-device-skip-introduction-removals (default "false")) + (introduced-by syncthing-device-introduced-by (default "")) + (addresses syncthing-device-addresses (default '("dynamic"))) + (paused syncthing-device-paused (default "false")) + (auto-accept-folders syncthing-device-auto-accept-folders (default "false")) + (max-send-kbps syncthing-device-max-send-kbps (default "0")) + (max-recv-kbps syncthing-device-max-recv-kbps (default "0")) + (max-request-kib syncthing-device-max-request-kib (default "0")) + (untrusted syncthing-device-untrusted (default "false")) + (remote-gui-port syncthing-device-remote-gui-port (default "0")) + (num-connections syncthing-device-num-connections (default "0"))) + +(define syncthing-device->sxml + (match-record-lambda + (id + name compression introducer skip-introduction-removals introduced-by + addresses paused auto-accept-folders max-send-kbps max-recv-kbps + max-request-kib untrusted remote-gui-port num-connections) + `(device (@ (id ,id) + (name ,name) + (compression ,compression) + (introducer ,introducer) + (skipIntroductionRemovals ,skip-introduction-removals) + (introducedBy ,introduced-by)) + ,@(map (lambda (address) `(address ,address)) addresses) + (paused ,paused) + (autoAcceptFolders ,auto-accept-folders) + (maxSendKbps ,max-send-kbps) + (maxRecvKbps ,max-recv-kbps) + (maxRequestKiB ,max-request-kib) + (untrusted ,untrusted) + (remoteGUIPort ,remote-gui-port) + (numConnections ,num-connections)))) + +(define-record-type* + syncthing-folder-device make-syncthing-folder-device + syncthing-folder-device? + (device syncthing-folder-device-device) + (introduced-by syncthing-folder-device-introduced-by (default (syncthing-device (id "")))) + (encryption-password syncthing-folder-device-encryption-password (default ""))) + +(define syncthing-folder-device->sxml + (match-record-lambda + (device introduced-by encryption-password) + `(device (@ (id ,(syncthing-device-id device)) + (introducedBy ,(syncthing-device-id introduced-by))) + (encryptionPassword ,encryption-password)))) + +(define-record-type* + syncthing-folder make-syncthing-folder + syncthing-folder? + (id syncthing-folder-id (default #f)) + (label syncthing-folder-label) + (path syncthing-folder-path) + (type syncthing-folder-type (default "sendreceive")) + (rescan-interval-s syncthing-folder-rescan-interval-s (default "3600")) + (fs-watcher-enabled syncthing-folder-fs-watcher-enabled (default "true")) + (fs-watcher-delay-s syncthing-folder-fs-watcher-delay-s (default "10")) + (fs-watcher-timeout-s syncthing-folder-fs-watcher-timeout-s (default "0")) + (ignore-perms syncthing-folder-ignore-perms (default "false")) + (auto-normalize syncthing-folder-auto-normalize (default "true")) + (devices syncthing-folder-devices (default '()) + (sanitize (lambda (folder-device-list) + (map (lambda (device) + (if (syncthing-folder-device? device) + device + (syncthing-folder-device (device device)))) + folder-device-list)))) + (filesystem-type syncthing-folder-filesystem-type (default "basic")) + (min-disk-free-unit syncthing-folder-min-disk-free-unit (default "%")) + (min-disk-free syncthing-folder-min-disk-free (default "1")) + (versioning-type syncthing-folder-versioning-type (default #f)) + (versioning-fs-path syncthing-folder-versioning-fs-path (default "")) + (versioning-fs-type syncthing-folder-versioning-fs-type (default "basic")) + (versioning-cleanup-interval-s syncthing-folder-versioning-cleanup-interval-s (default "3600")) + (versioning-cleanout-days syncthing-folder-versioning-cleanout-days (default #f)) + (versioning-keep syncthing-folder-versioning-keep (default #f)) + (versioning-max-age syncthing-folder-versioning-max-age (default #f)) + (versioning-command syncthing-folder-versioning-command (default #f)) + (copiers syncthing-folder-copiers (default "0")) + (puller-max-pending-kib syncthing-folder-puller-max-pending-kib (default "0")) + (hashers syncthing-folder-hashers (default "0")) + (order syncthing-folder-order (default "random")) + (ignore-delete syncthing-folder-ignore-delete (default "false")) + (scan-progress-interval-s syncthing-folder-scan-progress-interval-s (default "0")) + (puller-pause-s syncthing-folder-puller-pause-s (default "0")) + (max-conflicts syncthing-folder-max-conflicts (default "10")) + (disable-sparse-files syncthing-folder-disable-sparse-files (default "false")) + (disable-temp-indexes syncthing-folder-disable-temp-indexes (default "false")) + (paused syncthing-folder-paused (default "false")) + (weak-hash-threshold-pct syncthing-folder-weak-hash-threshold-pct (default "25")) + (marker-name syncthing-folder-marker-name (default ".stfolder")) + (copy-ownership-from-parent syncthing-folder-copy-ownership-from-parent (default "false")) + (mod-time-window-s syncthing-folder-mod-time-window-s (default "0")) + (max-concurrent-writes syncthing-folder-max-concurrent-writes (default "2")) + (disable-fsync syncthing-folder-disable-fsync (default "false")) + (block-pull-order syncthing-folder-block-pull-order (default "standard")) + (copy-range-method syncthing-folder-copy-range-method (default "standard")) + (case-sensitive-fs syncthing-folder-case-sensitive-fs (default "false")) + (junctions-as-dirs syncthing-folder-junctions-as-dirs (default "false")) + (sync-ownership syncthing-folder-sync-ownership (default "false")) + (send-ownership syncthing-folder-send-ownership (default "false")) + (sync-xattrs syncthing-folder-sync-xattrs (default "false")) + (send-xattrs syncthing-folder-send-xattrs (default "false")) + (xattr-filter-max-single-entry-size syncthing-folder-xattr-filter-max-single-entry-size (default "1024")) + (xattr-filter-max-total-size syncthing-folder-xattr-filter-max-total-size (default "4096"))) + +;; 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. +(define (maybe-param symbol value) + (if value `((param (@ (key ,(symbol->string symbol)) (val ,value)) "")) '())) + +(define syncthing-folder->sxml + (match-record-lambda + (id + label path type rescan-interval-s fs-watcher-enabled fs-watcher-delay-s + fs-watcher-timeout-s ignore-perms auto-normalize devices filesystem-type + min-disk-free-unit min-disk-free versioning-type versioning-fs-path + versioning-fs-type versioning-cleanup-interval-s versioning-cleanout-days + versioning-keep versioning-max-age versioning-command copiers + puller-max-pending-kib hashers order ignore-delete scan-progress-interval-s + puller-pause-s max-conflicts disable-sparse-files disable-temp-indexes paused + weak-hash-threshold-pct marker-name copy-ownership-from-parent mod-time-window-s + max-concurrent-writes disable-fsync block-pull-order copy-range-method + case-sensitive-fs junctions-as-dirs sync-ownership send-ownership sync-xattrs + send-xattrs xattr-filter-max-single-entry-size xattr-filter-max-total-size) + `(folder (@ (id ,(if id id label)) + (label ,label) + (path ,path) + (type ,type) + (rescanIntervalS ,rescan-interval-s) + (fsWatcherEnabled ,fs-watcher-enabled) + (fsWatcherDelayS ,fs-watcher-delay-s) + (fsWatcherTimeoutS ,fs-watcher-timeout-s) + (ignorePerms ,ignore-perms) + (autoNormalize ,auto-normalize)) + (filesystemType ,filesystem-type) + ,@(map syncthing-folder-device->sxml + devices) + (minDiskFree (@ (unit ,min-disk-free-unit)) + ,min-disk-free) + (versioning ,@(if versioning-type + `((@ (type ,versioning-type))) + '()) + ,@(maybe-param 'cleanoutDays versioning-cleanout-days) + ,@(maybe-param 'keep versioning-keep) + ,@(maybe-param 'maxAge versioning-max-age) + ,@(maybe-param 'command versioning-command) + (cleanupIntervalS ,versioning-cleanup-interval-s) + (fsPath ,versioning-fs-path) + (fsType ,versioning-fs-type)) + (copiers ,copiers) + (pullerMaxPendingKiB ,puller-max-pending-kib) + (hashers ,hashers) + (order ,order) + (ignoreDelete ,ignore-delete) + (scanProgressIntervalS ,scan-progress-interval-s) + (pullerPauseS ,puller-pause-s) + (maxConflicts ,max-conflicts) + (disableSparseFiles ,disable-sparse-files) + (disableTempIndexes ,disable-temp-indexes) + (paused ,paused) + (weakHashThresholdPct ,weak-hash-threshold-pct) + (markerName ,marker-name) + (copyOwnershipFromParent ,copy-ownership-from-parent) + (modTimeWindowS ,mod-time-window-s) + (maxConcurrentWrites ,max-concurrent-writes) + (disableFsync ,disable-fsync) + (blockPullOrder ,block-pull-order) + (copyRangeMethod ,copy-range-method) + (caseSensitiveFS ,case-sensitive-fs) + (junctionsAsDirs ,junctions-as-dirs) + (syncOwnership ,sync-ownership) + (sendOwnership ,send-ownership) + (syncXattrs ,sync-xattrs) + (sendXattrs ,send-xattrs) + (xattrFilter (maxSingleEntrySize ,xattr-filter-max-single-entry-size) + (maxTotalSize ,xattr-filter-max-total-size))))) + +(define-record-type* + syncthing-config-file make-syncthing-config-file + syncthing-config-file? + (folders syncthing-config-folders + ; this matches syncthing's default + (default (list (syncthing-folder (id "default") + (label "Default Folder") + (path "~/Sync"))))) + (devices syncthing-config-devices + (default '())) + (gui-enabled syncthing-config-gui-enabled (default "true")) + (gui-tls syncthing-config-gui-tls (default "false")) + (gui-debugging syncthing-config-gui-debugging (default "false")) + (gui-send-basic-auth-prompt syncthing-config-gui-send-basic-auth-prompt (default "false")) + (gui-address syncthing-config-gui-address (default "127.0.0.1:8384")) + (gui-user syncthing-config-gui-user (default #f)) + (gui-password syncthing-config-gui-password (default #f)) + (gui-apikey syncthing-config-gui-apikey (default #f)) + (gui-theme syncthing-config-gui-theme (default "default")) + (ldap-enabled syncthing-config-ldap-enabled (default #f)) + (ldap-address syncthing-config-ldap-address (default "")) + (ldap-bind-dn syncthing-config-ldap-bind-dn (default "")) + (ldap-transport syncthing-config-ldap-transport (default "")) + (ldap-insecure-skip-verify syncthing-config-ldap-insecure-skip-verify (default "")) + (ldap-search-base-dn syncthing-config-ldap-search-base-dn (default "")) + (ldap-search-filter syncthing-config-ldap-search-filter (default "")) + (listen-address syncthing-config-listen-address (default "default")) + (global-announce-server syncthing-config-global-announce-server (default "default")) + (global-announce-enabled syncthing-config-global-announce-enabled (default "true")) + (local-announce-enabled syncthing-config-local-announce-enabled (default "true")) + (local-announce-port syncthing-config-local-announce-port (default "21027")) + (local-announce-mcaddr syncthing-config-local-announce-mcaddr (default "[ff12::8384]:21027")) + (max-send-kbps syncthing-config-max-send-kbps (default "0")) + (max-recv-kbps syncthing-config-max-recv-kbps (default "0")) + (reconnection-interval-s syncthing-config-reconnection-interval-s (default "60")) + (relays-enabled syncthing-config-relays-enabled (default "true")) + (relay-reconnect-interval-m syncthing-config-relay-reconnect-interval-m (default "10")) + (start-browser syncthing-config-start-browser (default "true")) + (nat-enabled syncthing-config-nat-enabled (default "true")) + (nat-lease-minutes syncthing-config-nat-lease-minutes (default "60")) + (nat-renewal-minutes syncthing-config-nat-renewal-minutes (default "30")) + (nat-timeout-seconds syncthing-config-nat-timeout-seconds (default "10")) + (ur-accepted syncthing-config-ur-accepted (default "0")) + (ur-seen syncthing-config-ur-seen (default "0")) + (ur-unique-id syncthing-config-ur-unique-id (default "")) + (ur-url syncthing-config-ur-url (default "https://data.syncthing.net/newdata")) + (ur-post-insecurely syncthing-config-ur-post-insecurely (default "false")) + (ur-initial-delay-s syncthing-config-ur-initial-delay-s (default "1800")) + (auto-upgrade-interval-h syncthing-config-auto-upgrade-interval-h (default "12")) + (upgrade-to-pre-releases syncthing-config-upgrade-to-pre-releases (default "false")) + (keep-temporaries-h syncthing-config-keep-temporaries-h (default "24")) + (cache-ignored-files syncthing-config-cache-ignored-files (default "false")) + (progress-update-interval-s syncthing-config-progress-update-interval-s (default "5")) + (limit-bandwidth-in-lan syncthing-config-limit-bandwidth-in-lan (default "false")) + (min-home-disk-free-unit syncthing-config-min-home-disk-free-unit (default "%")) + (min-home-disk-free syncthing-config-min-home-disk-free (default "1")) + (releases-url syncthing-config-releases-url (default "https://upgrades.syncthing.net/meta.json")) + (overwrite-remote-device-names-on-connect syncthing-config-overwrite-remote-device-names-on-connect (default "false")) + (temp-index-min-blocks syncthing-config-temp-index-min-blocks (default "10")) + (unacked-notification-id syncthing-config-unacked-notification-id (default "authenticationUserAndPassword")) + (traffic-class syncthing-config-traffic-class (default "0")) + (set-low-priority syncthing-config-set-low-priority (default "true")) + (max-folder-concurrency syncthing-config-max-folder-concurrency (default "0")) + (crash-reporting-url syncthing-config-crash-reporting-url (default "https://crash.syncthing.net/newcrash")) + (crash-reporting-enabled syncthing-config-crash-reporting-enabled (default "true")) + (stun-keepalive-start-s syncthing-config-stun-keepalive-start-s (default "180")) + (stun-keepalive-min-s syncthing-config-stun-keepalive-min-s (default "20")) + (stun-server syncthing-config-stun-server (default "default")) + (database-tuning syncthing-config-database-tuning (default "auto")) + (max-concurrent-incoming-request-kib syncthing-config-max-concurrent-incoming-request-kib (default "0")) + (announce-lan-addresses syncthing-config-announce-lan-addresses (default "true")) + (send-full-index-on-upgrade syncthing-config-send-full-index-on-upgrade (default "false")) + (connection-limit-enough syncthing-config-connection-limit-enough (default "0")) + (connection-limit-max syncthing-config-connection-limit-max (default "0")) + (insecure-allow-old-tlsVersions syncthing-config-insecure-allow-old-tlsVersions (default "false")) + (connection-priority-tcp-lan syncthing-config-connection-priority-tcp-lan (default "10")) + (connection-priority-quic-lan syncthing-config-connection-priority-quic-lan (default "20")) + (connection-priority-tcp-wan syncthing-config-connection-priority-tcp-wan (default "30")) + (connection-priority-quic-wan syncthing-config-connection-priority-quic-wan (default "40")) + (connection-priority-relay syncthing-config-connection-priority-relay (default "50")) + (connection-priority-upgrade-threshold syncthing-config-connection-priority-upgrade-threshold (default "0")) + (default-folder syncthing-config-default-folder + (default (syncthing-folder (label "") (path "~")))) + (default-device syncthing-config-default-device + (default (syncthing-device (id "")))) + (default-ignores syncthing-config-default-ignores (default ""))) + +(define syncthing-config-file->sxml + (match-record-lambda + (folders + devices gui-enabled gui-tls gui-debugging gui-send-basic-auth-prompt + gui-address gui-user gui-password gui-apikey gui-theme ldap-enabled + ldap-address ldap-bind-dn ldap-transport ldap-insecure-skip-verify + ldap-search-base-dn ldap-search-filter listen-address global-announce-server + global-announce-enabled local-announce-enabled local-announce-port + local-announce-mcaddr max-send-kbps max-recv-kbps reconnection-interval-s + relays-enabled relay-reconnect-interval-m start-browser nat-enabled + nat-lease-minutes nat-renewal-minutes nat-timeout-seconds ur-accepted + ur-seen ur-unique-id ur-url ur-post-insecurely ur-initial-delay-s + auto-upgrade-interval-h upgrade-to-pre-releases keep-temporaries-h + cache-ignored-files progress-update-interval-s limit-bandwidth-in-lan + min-home-disk-free-unit min-home-disk-free releases-url + overwrite-remote-device-names-on-connect temp-index-min-blocks + unacked-notification-id traffic-class set-low-priority max-folder-concurrency + crash-reporting-url crash-reporting-enabled stun-keepalive-start-s + stun-keepalive-min-s stun-server database-tuning + max-concurrent-incoming-request-kib announce-lan-addresses + send-full-index-on-upgrade connection-limit-enough connection-limit-max + insecure-allow-old-tlsVersions connection-priority-tcp-lan + connection-priority-quic-lan connection-priority-tcp-wan + connection-priority-quic-wan connection-priority-relay + connection-priority-upgrade-threshold default-folder default-device + default-ignores) + `(configuration (@ (version "37")) + ,@(map syncthing-folder->sxml + folders) + ;; collect any devices in any folders, as well as any + ;; devices explicitly added. + ,@(map syncthing-device->sxml + (delete-duplicates + (append devices + (apply append + (map (lambda (folder) + (map syncthing-folder-device-device + (syncthing-folder-devices folder))) + folders))) + ;; devices are the same if their id's are equal + (lambda (device1 device2) + (string= (syncthing-device-id device1) + (syncthing-device-id device2))))) + (gui (@ (enabled ,gui-enabled) + (tls ,gui-tls) + (debugging ,gui-debugging) + (sendBasicAuthPrompt ,gui-send-basic-auth-prompt)) + (address ,gui-address) + ,@(if gui-user `((user ,gui-user)) '()) + ,@(if gui-password `((password ,gui-password)) '()) + ,@(if gui-apikey `((apikey ,gui-apikey)) '()) + (theme ,gui-theme)) + (ldap ,(if ldap-enabled + `((address ,ldap-address) + (bindDN ,ldap-bind-dn) + ,@(if ldap-transport + `((transport ,ldap-transport)) + '()) + ,@(if ldap-insecure-skip-verify + `((insecureSkipVerify ,ldap-insecure-skip-verify)) + '()) + ,@(if ldap-search-base-dn + `((searchBaseDN ,ldap-search-base-dn)) + '()) + ,@(if ldap-search-filter + `((searchFilter ,ldap-search-filter)) + '())) + "")) + (options (listenAddress ,listen-address) + (globalAnnounceServer ,global-announce-server) + (globalAnnounceEnabled ,global-announce-enabled) + (localAnnounceEnabled ,local-announce-enabled) + (localAnnouncePort ,local-announce-port) + (localAnnounceMCAddr ,local-announce-mcaddr) + (maxSendKbps ,max-send-kbps) + (maxRecvKbps ,max-recv-kbps) + (reconnectionIntervalS ,reconnection-interval-s) + (relaysEnabled ,relays-enabled) + (relayReconnectIntervalM ,relay-reconnect-interval-m) + (startBrowser ,start-browser) + (natEnabled ,nat-enabled) + (natLeaseMinutes ,nat-lease-minutes) + (natRenewalMinutes ,nat-renewal-minutes) + (natTimeoutSeconds ,nat-timeout-seconds) + (urAccepted ,ur-accepted) + (urSeen ,ur-seen) + (urUniqueID ,ur-unique-id) + (urURL ,ur-url) + (urPostInsecurely ,ur-post-insecurely) + (urInitialDelayS ,ur-initial-delay-s) + (autoUpgradeIntervalH ,auto-upgrade-interval-h) + (upgradeToPreReleases ,upgrade-to-pre-releases) + (keepTemporariesH ,keep-temporaries-h) + (cacheIgnoredFiles ,cache-ignored-files) + (progressUpdateIntervalS ,progress-update-interval-s) + (limitBandwidthInLan ,limit-bandwidth-in-lan) + (minHomeDiskFree (@ (unit ,min-home-disk-free-unit)) + ,min-home-disk-free) + (releasesURL ,releases-url) + (overwriteRemoteDeviceNamesOnConnect ,overwrite-remote-device-names-on-connect) + (tempIndexMinBlocks ,temp-index-min-blocks) + (unackedNotificationID ,unacked-notification-id) + (trafficClass ,traffic-class) + (setLowPriority ,set-low-priority) + (maxFolderConcurrency ,max-folder-concurrency) + (crashReportingURL ,crash-reporting-url) + (crashReportingEnabled ,crash-reporting-enabled) + (stunKeepaliveStartS ,stun-keepalive-start-s) + (stunKeepaliveMinS ,stun-keepalive-min-s) + (stunServer ,stun-server) + (databaseTuning ,database-tuning) + (maxConcurrentIncomingRequestKiB ,max-concurrent-incoming-request-kib) + (announceLANAddresses ,announce-lan-addresses) + (sendFullIndexOnUpgrade ,send-full-index-on-upgrade) + (connectionLimitEnough ,connection-limit-enough) + (connectionLimitMax ,connection-limit-max) + (insecureAllowOldTLSVersions ,insecure-allow-old-tlsVersions) + (connectionPriorityTcpLan ,connection-priority-tcp-lan) + (connectionPriorityQuicLan ,connection-priority-quic-lan) + (connectionPriorityTcpWan ,connection-priority-tcp-wan) + (connectionPriorityQuicWan ,connection-priority-quic-wan) + (connectionPriorityRelay ,connection-priority-relay) + (connectionPriorityUpgradeThreshold ,connection-priority-upgrade-threshold)) + (defaults + ,(syncthing-folder->sxml default-folder) + ,(syncthing-device->sxml default-device) + (ignores ,default-ignores))))) + + +(define (serialize-syncthing-config-file config) + (with-output-to-string + (lambda () + (sxml->xml (cons '*TOP* (list (syncthing-config-file->sxml config))))))) + (define-record-type* syncthing-configuration make-syncthing-configuration syncthing-configuration? @@ -50,12 +470,14 @@ (define-record-type* (default "users")) (home syncthing-configuration-home ;string (default #f)) + (config-file syncthing-configuration-config-file + (default #f)) ; syncthing-config-file or file-like (home-service? syncthing-configuration-home-service? (default for-home?) (innate))) (define syncthing-shepherd-service (match-record-lambda - (syncthing arguments logflags user group home home-service?) + (syncthing arguments logflags user group home home-service? config-file) (list (shepherd-service (provision (if home-service? @@ -64,39 +486,75 @@ (define syncthing-shepherd-service (string-append "syncthing-" user))))) (documentation "Run syncthing.") (requirement (if home-service? '() '(loopback user-processes))) - (start #~(make-forkexec-constructor - (append (list (string-append #$syncthing "/bin/syncthing") - "--no-browser" - "--no-restart" - (string-append "--logflags=" (number->string #$logflags))) - '#$arguments) - #:user #$(and (not home-service?) user) - #:group #$(and (not home-service?) group) - #:environment-variables - (append - (list - (string-append "HOME=" - (or #$home - (passwd:dir - (getpw (if (and #$home-service? - (not #$user)) - (getuid) - #$user))))) - "SSL_CERT_DIR=/etc/ssl/certs" - "SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt") - (filter (negate ;XXX: 'remove' is not in (guile) - (lambda (str) - (or (string-prefix? "HOME=" str) - (string-prefix? "SSL_CERT_DIR=" str) - (string-prefix? "SSL_CERT_FILE=" str)))) - (environ))))) + (start #~(lambda _ + ;; If we are managing the config, and it's not a home + ;; service, then exepect the config file at + ;; /var/lib/syncthing-. This makes sure the ownership + ;; is correct + (unless (or #$(not config-file) #$home-service?) + (let ((user-pw (getpw #$user))) + (chown (string-append "/var/lib/syncthing-" #$user) + (passwd:uid user-pw) + (passwd:gid user-pw))) + (chmod (string-append "/var/lib/syncthing-" #$user) #o700)) + (make-forkexec-constructor + (append (list (string-append #$syncthing "/bin/syncthing") + ;; Do not try to try to lauch a browser on startup. + "--no-browser" + ;; If syncthing crashes, let the service fail. + "--no-restart" + (string-append "--logflags=" (number->string #$logflags))) + ;; Optionally move data and configuration home to + ;; /var/lib/syncthing-. + (if (or #$(not config-file) #$home-service?) '() + (list (string-append "--home=/var/lib/syncthing-" #$user))) + '#$arguments) + #:user #$(and (not home-service?) user) + #:group #$(and (not home-service?) group) + #:environment-variables + (append + (list + (string-append "HOME=" + (or #$home + (passwd:dir + (getpw (if (and #$home-service? + (not #$user)) + (getuid) + #$user))))) + "SSL_CERT_DIR=/etc/ssl/certs" + "SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt") + (filter (negate ;XXX: 'remove' is not in (guile) + (lambda (str) + (or (string-prefix? "HOME=" str) + (string-prefix? "SSL_CERT_DIR=" str) + (string-prefix? "SSL_CERT_FILE=" str)))) + (environ)))))) (respawn? #f) (stop #~(make-kill-destructor)))))) + +(define syncthing-files-service + (match-record-lambda (config-file user home home-service?) + (if config-file + ;; When used as a system service, this service might be executed + ;; before a user's home even exists, causing it to be owned by root, + ;; and the skeletons to never be applied to that user's home. In such + ;; cases, put the config at /var/lib/syncthnig-/config.xml + `((,(if home-service? + ".config/syncthing/config.xml" + (string-append "/var/lib/syncthing-" user "/config.xml")) + ,(if (file-like? config-file) + config-file + (plain-file "syncthin-config.xml" (serialize-syncthing-config-file + config-file))))) + '()))) + (define syncthing-service-type (service-type (name 'syncthing) (extensions (list (service-extension shepherd-root-service-type - syncthing-shepherd-service))) + syncthing-shepherd-service) + (service-extension special-files-service-type + syncthing-files-service))) (description "Run @uref{https://github.com/syncthing/syncthing, Syncthing} decentralized continuous file system synchronization.")))