syncthing-networking

title: syncthing-networking(7)

summary: Firewall Setup

text:
# syncthing-networking(7)

## NAME
syncthing-networking - Firewall Setup

## ROUTER SETUP

### Port Forwards
If you have a NAT router which supports UPnP, the easiest way to get a working port forward is to make sure UPnP setting is enabled on both Syncthing and the router – Syncthing will try to handle the rest. If it succeeds you will see a message in the console saying:

```
Created UPnP port mapping for external port XXXXX on UPnP device YYYYY.
```

If this is not possible or desirable, you should set up a port forwarding for ports 22000/TCP and 22000/UDP (or whichever port is set in the Sync Protocol Listen Address setting). The external forwarded ports and the internal destination ports have to be the same (e.g. 22000/TCP).

Communication in Syncthing works both ways. Therefore if you set up port forwards for one device, other devices will be able to connect to it even when they are behind a NAT network or firewall.

In the absence of port forwarding, Relaying may work well enough to get devices connected and synced, but will perform poorly in comparison to a direct connection.

### Local Discovery
The router needs to allow/forward broad-/multicasts for local discovery to work. Usually these are allowed by default in a single local subnet, but may be blocked between different subnets or even between a bridged Wi-Fi and LAN.

If you are unable to set up your router thus or your firewall as shown below, and your devices have static IP addresses, you can specify them directly by changing the default dynamic setting for Addresses to something like: tcp://192.168.1.xxx:22000, dynamic.

## LOCAL FIREWALL
If your PC has a local firewall, you will need to open the following ports for incoming and outgoing traffic:

- Port 22000/TCP: TCP based sync protocol traffic

- Port 22000/UDP: QUIC based sync protocol traffic

- Port 21027/UDP: for discovery broadcasts on IPv4 and multicasts on IPv6

If you configured a custom port in the Sync Protocol Listen Address setting, you have to adapt the firewall rules accordingly.

### Uncomplicated Firewall (ufw)
If you’re using ufw on Linux and have installed the Syncthing package <https://apt.syncthing.net/>, you can allow the necessary ports by running:

```
sudo ufw allow syncthing
```

If you also want to allow external access to the Syncthing web GUI, run:

```
sudo ufw allow syncthing-gui
```

Allowing external access is not necessary for a typical installation.

You can then verify that the ports mentioned above are allowed:

```
sudo ufw status verbose
```

In case you installed Syncthing manually you can follow the instructions to manually add the syncthing preset <https://github.com/syncthing/syncthing/tree/main/etc/firewall-ufw> to ufw.

### Firewalld
If you are using Firewalld <https://firewalld.org/> it has included support for syncthing (since version 0.5.0, January 2018), and you can enable it with:

```
sudo firewall-cmd --zone=public --add-service=syncthing --permanent
sudo firewall-cmd --reload
```

Similarly there is also a syncthing-gui service.

## REMOTE WEB GUI
To be able to access the web GUI from other computers, you need to change the GUI Listen Address setting from the default 127.0.0.1:8384 to 0.0.0.0:8384. You also need to open the port in your local firewall if you have one.

### Tunneling via SSH
If you have SSH access to the machine running Syncthing but would rather not open the web GUI port to the outside world, you can access it through a SSH tunnel instead. You can start a tunnel with a command like the following:

```
ssh -L 9999:localhost:8384 machine
```

This will bind to your local port 9999 and forward all connections from there to port 8384 on the target machine. This still works even if Syncthing is bound to listen on localhost only.

## VIA A PROXY
Syncthing can use a SOCKS5 proxy for outbound connections. Please see Using Proxies.

## AUTHOR
The Syncthing Authors

## COPYRIGHT
2014-2026, The Syncthing Authors


syncthing-relay

package: syncthing

title: syncthing-relay(7)

summary: Relay Protocol v1

text:
# syncthing-relay(7)

## NAME
syncthing-relay - Relay Protocol v1

## WHAT IS A RELAY?
Relay is a service which relays data between two devices which are not able to connect to each other directly otherwise. This is usually due to both devices being behind a NAT and neither side being able to open a port which would be directly accessible from the internet.

A relay was designed to relay BEP protocol, hence the reliance on device ID’s in the protocol spec, but at the same time it is general enough that could be reused by other protocols or applications, as the data transferred between two devices which use a relay is completely obscure and does not affect the relaying.

## OPERATION MODES
Relay listens on a single TCP socket, but has two different connection modes, where a connection mode is a predefined set of messages which the relay and the device are expecting to exchange.

The first mode is the protocol mode which allows a client to interact with the relay, for example join the relay, or request to connect to a device, given it is available on the relay. Similarly to BEP, protocol mode requires the device to connect via TLS using a strong suite of ciphers (same as BEP), which allows the relay to verify and derive the identity (Device ID) of the device.

The second mode is the session mode which after a few initial messages connects two devices directly to each other via the relay, and is a plain-text protocol, which for every byte written by one device, sends the same set of bytes to the other device and vica versa.

## IDENTIFYING THE CONNECTION MODE
Because both connection modes operate over the same single socket, a method of detecting the connection mode is required.

When a new client connects to the relay, the relay checks the first byte that the client has sent, and if that matches 0x16, that implies to us that the connection is a protocol mode connection, due to 0x16 being the first byte in the TLS handshake, and only protocol mode connections use TLS.

If the first byte is not 0x16, then we assume that the connection is a session mode connection.

## PROTOCOL MODE
Protocol mode uses TLS and protocol name defined by the TLS header should be bep-relay.

Protocol mode has two submodes: 1. Permanent protocol submode - Joining the relay, and waiting for messages from the relay asking to connect to some device which is interested in having a session with you. 2. Temporary protocol submode - Only used to request a session with a device which is connected to the relay using the permanent protocol submode.

### Permanent protocol submode
A permanent protocol submode begins with the client sending a JoinRelayRequest message, which the relay responds to with either a ResponseSuccess or ResponseAlreadyConnected message if a client with the same device ID already exists.

After the client has joined, no more messages are exchanged apart from Ping/Pong messages for general connection keep alive checking.

From this point onwards, the client stand-by’s and waits for SessionInvitation messages from the relay, which implies that some other device is trying to connect with you. SessionInvitation message contains the unique session key which then can be used to establish a connection in session mode.

If the client fails to send a JoinSessionRequest message within the first ping interval, the connection is terminated. If the client fails to send a message (even if it’s a ping message) every minute (by default), the connection is terminated.

### Temporary protocol submode
A temporary protocol submode begins with ConnectRequest message, to which the relay responds with ResponseNotFound if the device the client it is after is not available, with a RelayFull if the relay has reached its limits, or with a SessionInvitation, which contains the unique session key which then can be used to establish a connection in session mode.

The connection is terminated immediately after that.

### Example Exchange
Client A - Permanent protocol submode Client B - Temporary protocol submode

\#

Client (A)

Relay

Client (B)

1

JoinRelayRequest->

2

<-ResponseSuccess

3

Ping->

4

<-Pong

5

<-ConnectRequest(A)

6

SessionInvitation(A)->

7

<-SessionInvitation(B)

8

(Disconnects)

9

Ping->

10

<-Pong

11

Ping->

12

<-Pong

## SESSION MODE
The first and only message the client sends in the session mode is the JoinSessionRequest message which contains the session key identifying which session you are trying to join. The relay responds with one of the following messages:

1.

ResponseNotFound - Session key is invalid

2.

ResponseAlreadyConnected - Session is full (both sides already connected)

3.

ResponseSuccess - You have successfully joined the session

4.

RelayFull - Relay limits are too strict for you to be able to join the session. The relay immediately terminates the connection after sending this.

After the successful response, all the bytes written and received will be relayed between the two devices in the session directly.

### Example Exchange
Client A is the first to join the session.

Client B is the second to join the session.

Both are in session mode.

\#

Client (A)

Relay

Client (B)

1

JoinSessionRequest(A)->

2

<-ResponseSuccess

3

Data->

(Buffers data)

4

Data->

(Buffers data)

5

<-JoinSessionRequest(B)

6

ResponseSuccess->

7

Relays data ->

8

Relays data ->

9

<-Relays data

<-Data

## MESSAGES
All messages are preceded by a header message. Header message contains the magic value 0x9E79BC40, message type integer, and message length.

WARNING:

Some messages have no content, apart from the implied header which allows us to identify what type of message it is.

### Header structure

```
 0                   1                   2                   3

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             Magic                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Message Type                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Message Length                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct Header {

        unsigned int Magic;

        int MessageType;

        int MessageLength;
}
```

### Ping message (Type = 0)

```
 0                   1                   2                   3

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct Ping {
}
```

### Pong message (Type = 1)

```
 0                   1                   2                   3

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct Pong {
}
```

### JoinRelayRequest message (Type = 2)

```
 0                   1                   2                   3

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct JoinRelayRequest {
}
```

### JoinSessionRequest message (Type = 3)

```
 0                   1                   2                   3

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Length of Key                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/                                                               /
\                     Key (variable length)                     \
/                                                               /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct JoinSessionRequest {

        opaque Key<32>;
}
```

: Key

This is a unique random session key generated by the relay server. It is used to identify which session you are trying to connect to.

### Response message (Type = 4)

```
 0                   1                   2                   3

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             Code                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Length of Message                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/                                                               /
\                   Message (variable length)                   \
/                                                               /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct Response {

        int Code;

        string Message<>;
}
```

: Code

An integer representing the status code.

: Message

Message associated with the code.

```
ResponseSuccess           = Response{0, "success"}
ResponseNotFound          = Response{1, "not found"}
ResponseAlreadyConnected  = Response{2, "already connected"}
ResponseInternalError     = Response{99, "internal error"}
ResponseUnexpectedMessage = Response{100, "unexpected message"}
```

### ConnectRequest message (Type = 5)

```
 0                   1                   2                   3

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Length of ID                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/                                                               /
\                     ID (variable length)                      \
/                                                               /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct ConnectRequest {

        opaque ID<32>;
}
```

: ID

Device ID to which the client would like to connect.

### SessionInvitation message (Type = 6)

```
 0                   1                   2                   3

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Length of From                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/                                                               /
\                    From (variable length)                     \
/                                                               /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Length of Key                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/                                                               /
\                     Key (variable length)                     \
/                                                               /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Length of Address                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/                                                               /
\                   Address (variable length)                   \
/                                                               /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            0x0000             |             Port              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  Server Socket (V=0 or 1)                   |V|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct SessionInvitation {

        opaque From<32>;

        opaque Key<32>;

        opaque Address<32>;

        unsigned int Port;

        bool ServerSocket;
}
```

: From

Device ID identifying who you will be connecting with.

: Key

A unique random session key generated by the relay server. It is used to identify which session you are trying to connect to.

: Address

An optional IP address on which the relay server is expecting you to connect, in order to start a connection in session mode. Empty/all zero IP should be replaced with the relay’s public IP address that was used when establishing the protocol mode connection.

: Port

The port on which the relay server is expecting you to connect, in order to start a connection in session mode.

: Server Socket

Because both sides connecting to the relay use the client side of the socket, and some protocols behave differently depending if the connection starts on the server side or the client side, this boolean indicates which side of the connection this client should assume it’s getting. The value is inverted in the invitation which is sent to the other device, so that there is always one client socket, and one server socket.

### RelayFull message (Type = 7)

```
 0                   1                   2                   3

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
struct RelayFull {
}
```

## HOW SYNCTHING USES RELAYS, AND GENERAL SECURITY
In the case of Syncthing and BEP, when two devices connect via relay, they start their standard TLS connection encapsulated within the relay’s plain-text session connection, effectively upgrading the plain-text connection to a TLS connection.

Even though the relay could be used for man-in-the-middle attack, using TLS at the application/BEP level ensures that all the traffic is safely encrypted, and is completely meaningless to the relay. Furthermore, the secure suite of ciphers used by BEP provides forward secrecy, meaning that even if the relay did capture all the traffic, and even if the attacker did get their hands on the device keys, they would still not be able to recover/decrypt any traffic which was transported via the relay.

After establishing a relay session, Syncthing looks at the SessionInvitation message, and depending which side it has received, wraps the raw socket in either a TLS client socket or a TLS server socket depending on the ServerSocket boolean value in the SessionInvitation, and starts the TLS handshake.

From that point onwards it functions exactly the same way as if Syncthing was establishing a direct connection with the other device over the internet, performing device ID validation, and full TLS encryption, and provides the same security properties as it would provide when connecting over the internet.

## EXAMPLES OF STRONG CIPHER SUITES
ID

Name

Description

0x009F

DHE-RSA-AES256-GCM-SHA384

TLSv1.2 DH RSA AESGCM(256) AEAD

0x006B

DHE-RSA-AES256-SHA256

TLSv1.2 DH RSA AES(256) SHA256

0xC030

ECDHE-RSA-AES256-GCM-SHA384

TLSv1.2 ECDH RSA AESGCM(256) AEAD

0xC028

ECDHE-RSA-AES256-SHA384

TLSv1.2 ECDH RSA AES(256) SHA384

0x009E

DHE-RSA-AES128-GCM-SHA256

TLSv1.2 DH RSA AESGCM(128) AEAD

0x0067

DHE-RSA-AES128-SHA256

TLSv1.2 DH RSA AES(128) SHA256

0xC02F

ECDHE-RSA-AES128-GCM-SHA256

TLSv1.2 ECDH RSA AESGCM(128) AEAD

0xC027

ECDHE-RSA-AES128-SHA256

TLSv1.2 ECDH RSA AES(128) SHA256

## AUTHOR
The Syncthing Authors

## COPYRIGHT
2014-2026, The Syncthing Authors


syncthing-rest-api

title: syncthing-rest-api(7)

summary: REST API

text:
# syncthing-rest-api(7)

## NAME
syncthing-rest-api - REST API

Syncthing exposes a REST interface over HTTP on the GUI port. This is used by the GUI (from Javascript) and can be used by other processes wishing to control Syncthing. In most cases both the input and output data is in JSON format. The interface is subject to change.

## API KEY
To use the REST API an API key must be set and used. The API key can be generated in the GUI, or set in the configuration/gui/apikey element in the configuration file. To use an API key, set the request header X-API-Key to the API key value, or set it as a Bearer token in the Authorization header. For example, curl -X POST -H "X-API-Key: abc123" http://localhost:8384/rest/... or curl -X POST -H "Authorization: Bearer abc123" http://localhost:8384/rest/... can be used to invoke with curl (add -k flag when using HTTPS with a Syncthing generated or self signed certificate).

One exception to this requirement is /rest/noauth, you do not need an API key to use those endpoints. This way third-party devices and services can do simple calls that don’t expose sensitive information without having to expose your API key.

## RESULT PAGINATION
Some GET endpoints take optional page and perpage arguments for pagination. No more than perpage (defaults to 65536 if not given) result entries are returned in an array. To access further entries, passing the page parameter will advance in the results by that many pages. The actually used parameters are always returned as attributes in the response object.

## SYSTEM ENDPOINTS

### GET /rest/system/browse
Returns a list of directories matching the path given by the optional parameter current. The path can use patterns as described in Go’s filepath package <https://pkg.go.dev/path/filepath#Match>. A ‘*’ will always be appended to the given path (e.g. /tmp/ matches all its subdirectories). If the option current is not given, filesystem root paths are returned.

```
$ curl -H "X-API-Key: yourkey" localhost:8384/rest/system/browse | json_pp
[

    "/"
]
$ curl -H "X-API-Key: yourkey" localhost:8384/rest/system/browse?current=/var/ | json_pp
[

    "/var/backups/",

    "/var/cache/",

    "/var/lib/",

    "/var/local/",

    "/var/lock/",

    "/var/log/",

    "/var/mail/",

    "/var/opt/",

    "/var/run/",

    "/var/spool/",

    "/var/tmp/"
]
$ curl -H "X-API-Key: yourkey" localhost:8384/rest/system/browse?current=/var/*o | json_pp
[

    "/var/local/",

    "/var/lock/",

    "/var/log/",

    "/var/opt/",

    "/var/spool/"
]
```

### GET /rest/system/config (DEPRECATED)
Deprecated since version v1.12.0: This endpoint still works as before but is deprecated. Use /rest/config instead.

Returns the current configuration.

```
{

  "version": 35,

  "folders": [

    {

      "id": "default",

      "label": "Default Folder",

      "filesystemType": "basic",

      "path": "...",

      "type": "sendreceive",

      "devices": [

        {

          "deviceID": "...",

          "introducedBy": "",

          "encryptionPassword": ""

        }

      ],

      "rescanIntervalS": 3600,

      "fsWatcherEnabled": true,

      "fsWatcherDelayS": 10,

      "ignorePerms": false,

      "autoNormalize": true,

      "minDiskFree": {

        "value": 1,

        "unit": "%"

      },

      "versioning": {

        "type": "",

        "params": {},

        "cleanupIntervalS": 3600,

        "fsPath": "",

        "fsType": "basic"

      },

      "copiers": 0,

      "pullerMaxPendingKiB": 0,

      "hashers": 0,

      "order": "random",

      "ignoreDelete": false,

      "scanProgressIntervalS": 0,

      "pullerPauseS": 0,

      "maxConflicts": -1,

      "disableSparseFiles": false,

      "paused": false,

      "markerName": ".stfolder",

      "copyOwnershipFromParent": false,

      "modTimeWindowS": 0,

      "maxConcurrentWrites": 16,

      "disableFsync": false,

      "blockPullOrder": "standard",

      "copyRangeMethod": "standard",

      "caseSensitiveFS": false,

      "junctionsAsDirs": true

    }

  ],

  "devices": [

    {

      "deviceID": "...",

      "name": "Laptop",

      "addresses": [

        "dynamic",

        "tcp://192.168.1.2:22000"

      ],

      "compression": "metadata",

      "certName": "",

      "introducer": false,

      "skipIntroductionRemovals": false,

      "introducedBy": "",

      "paused": false,

      "allowedNetworks": [],

      "autoAcceptFolders": false,

      "maxSendKbps": 0,

      "maxRecvKbps": 0,

      "ignoredFolders": [

        {

          "time": "2022-01-09T19:09:52Z",

          "id": "br63e-wyhb7",

          "label": "Foo"

        }

      ],

      "maxRequestKiB": 0,

      "untrusted": false,

      "remoteGUIPort": 0

    }

  ],

  "gui": {

    "enabled": true,

    "address": "127.0.0.1:8384",

    "unixSocketPermissions": "",

    "user": "Username",

    "password": "$2a$10$ZFws69T4FlvWwsqeIwL.TOo5zOYqsa/.TxlUnsGYS.j3JvjFTmxo6",

    "authMode": "static",

    "useTLS": false,

    "apiKey": "k1dnz1Dd0rzTBjjFFh7CXPnrF12C49B1",

    "insecureAdminAccess": false,

    "theme": "default",

    "insecureSkipHostcheck": false,

    "insecureAllowFrameLoading": false

  },

  "ldap": {

    "address": "",

    "bindDN": "",

    "transport": "plain",

    "insecureSkipVerify": false,

    "searchBaseDN": "",

    "searchFilter": ""

  },

  "options": {

    "listenAddresses": [

      "default"

    ],

    "globalAnnounceServers": [

      "default"

    ],

    "globalAnnounceEnabled": true,

    "localAnnounceEnabled": true,

    "localAnnouncePort": 21027,

    "localAnnounceMCAddr": "[ff12::8384]:21027",

    "maxSendKbps": 0,

    "maxRecvKbps": 0,

    "reconnectionIntervalS": 60,

    "relaysEnabled": true,

    "relayReconnectIntervalM": 10,

    "startBrowser": true,

    "natEnabled": true,

    "natLeaseMinutes": 60,

    "natRenewalMinutes": 30,

    "natTimeoutSeconds": 10,

    "urAccepted": 0,

    "urSeen": 0,

    "urUniqueId": "...",

    "urURL": "https://data.syncthing.net/newdata",

    "urPostInsecurely": false,

    "urInitialDelayS": 1800,

    "autoUpgradeIntervalH": 12,

    "upgradeToPreReleases": false,

    "keepTemporariesH": 24,

    "cacheIgnoredFiles": false,

    "progressUpdateIntervalS": 5,

    "limitBandwidthInLan": false,

    "minHomeDiskFree": {

      "value": 1,

      "unit": "%"

    },

    "releasesURL": "https://upgrades.syncthing.net/meta.json",

    "alwaysLocalNets": [],

    "overwriteRemoteDeviceNamesOnConnect": false,

    "tempIndexMinBlocks": 10,

    "unackedNotificationIDs": [

      "authenticationUserAndPassword"

    ],

    "trafficClass": 0,

    "setLowPriority": true,

    "maxFolderConcurrency": 0,

    "crURL": "https://crash.syncthing.net/newcrash",

    "crashReportingEnabled": true,

    "stunKeepaliveStartS": 180,

    "stunKeepaliveMinS": 20,

    "stunServers": [

      "default"

    ],

    "maxConcurrentIncomingRequestKiB": 0,

    "announceLANAddresses": true,

    "sendFullIndexOnUpgrade": false,

    "featureFlags": [],

    "connectionLimitEnough": 0,

    "connectionLimitMax": 0

  },

  "remoteIgnoredDevices": [

    {

      "time": "2022-01-09T20:02:01Z",

      "deviceID": "...",

      "name": "...",

      "address": "192.168.0.20:22000"

    }

  ],

  "defaults": {

    "folder": {

      "id": "",

      "label": "",

      "filesystemType": "basic",

      "path": "~",

      "type": "sendreceive",

      "devices": [

        {

          "deviceID": "...",

          "introducedBy": "",

          "encryptionPassword": ""

        }

      ],

      "rescanIntervalS": 3600,

      "fsWatcherEnabled": true,

      "fsWatcherDelayS": 10,

      "ignorePerms": false,

      "autoNormalize": true,

      "minDiskFree": {

        "value": 1,

        "unit": "%"

      },

      "versioning": {

        "type": "",

        "params": {},

        "cleanupIntervalS": 3600,

        "fsPath": "",

        "fsType": "basic"

      },

      "copiers": 0,

      "pullerMaxPendingKiB": 0,

      "hashers": 0,

      "order": "random",

      "ignoreDelete": false,

      "scanProgressIntervalS": 0,

      "pullerPauseS": 0,

      "maxConflicts": 10,

      "disableSparseFiles": false,

      "paused": false,

      "markerName": ".stfolder",

      "copyOwnershipFromParent": false,

      "modTimeWindowS": 0,

      "maxConcurrentWrites": 16,

      "disableFsync": false,

      "blockPullOrder": "standard",

      "copyRangeMethod": "standard",

      "caseSensitiveFS": false,

      "junctionsAsDirs": false

    },

    "device": {

      "deviceID": "",

      "name": "",

      "addresses": [

        "dynamic"

      ],

      "compression": "metadata",

      "certName": "",

      "introducer": false,

      "skipIntroductionRemovals": false,

      "introducedBy": "",

      "paused": false,

      "allowedNetworks": [],

      "autoAcceptFolders": false,

      "maxSendKbps": 0,

      "maxRecvKbps": 0,

      "ignoredFolders": [],

      "maxRequestKiB": 0,

      "untrusted": false,

      "remoteGUIPort": 0

    }

  }
}
```

### GET /rest/system/config/insync (DEPRECATED)
Deprecated since version v1.12.0: This endpoint still works as before but is deprecated. Use /rest/config/restart-required instead.

Returns whether the config is in sync, i.e. whether the running configuration is the same as that on disk.

```
{

  "configInSync": true
}
```

### POST /rest/system/config (DEPRECATED)
Deprecated since version v1.12.0: This endpoint still works as before but is deprecated. Use Config Endpoints instead.

Post the full contents of the configuration, in the same format as returned by the corresponding GET request. When posting the configuration succeeds, the posted configuration is immediately applied, except for changes that require a restart. Query /rest/config/restart-required to check if a restart is required.

This endpoint is the main point to control Syncthing, even if the change only concerns a very small part of the config: The usual workflow is to get the config, modify the needed parts and post it again.

### GET /rest/system/connections
NOTE:

Return format changed in versions 0.13.0, 0.14.14, 1.2.0, 1.19.0, 1.23.0 and 1.25.0.

Returns the list of configured devices and some metadata associated with them.

The connection types are tcp-client, tcp-server, relay-client, relay-server, quic-client and quic-server.

```
{

  "connections": {

    "DOVII4U-SQEEESM-VZ2CVTC-CJM4YN5-QNV7DCU-5U3ASRL-YVFG6TH-W5DV5AA": {

      "address": "",

      "at": "0001-01-01T00:00:00Z",

      "clientVersion": "",

      "connected": false,

      "inBytesTotal": 0,

      "isLocal": false,

      "outBytesTotal": 0,

      "paused": false,

      "startedAt": "0001-01-01T00:00:00Z",

      "type": ""

    },

    "UYGDMA4-TPHOFO5-2VQYDCC-7CWX7XW-INZINQT-LE4B42N-4JUZTSM-IWCSXA4": {

      "address": "",

      "at": "0001-01-01T00:00:00Z",

      "clientVersion": "",

      "connected": false,

      "inBytesTotal": 0,

      "isLocal": false,

      "outBytesTotal": 0,

      "paused": false,

      "startedAt": "0001-01-01T00:00:00Z",

      "type": ""

    },

    "YZJBJFX-RDBL7WY-6ZGKJ2D-4MJB4E7-ZATSDUY-LD6Y3L3-MLFUYWE-AEMXJAC": {

      "address": "127.0.0.1:22002",

      "at": "2015-11-07T17:29:47.691548971+01:00",

      "clientVersion": "v0.12.1",

      "connected": true,

      "inBytesTotal": 556,

      "isLocal": true,

      "outBytesTotal": 550,

      "paused": false,

      "startedAt": "2015-11-07T00:09:47Z",

      "type": "tcp-client"

    }

  },

  "total": {

    "at": "2015-11-07T17:29:47.691637262+01:00",

    "inBytesTotal": 1479,

    "outBytesTotal": 1318

  }
}
```

### GET /rest/system/discovery
Returns the contents of the local discovery cache.

```
{

  "LGFPDIT7SKNNJVJZA4FC7QNCRKCE753K72BW5QD2FOZ7FRFEP57Q": [

    "192.162.129.11:22000"

  ]
}
```

### POST /rest/system/discovery
NOTE:

Removed in v0.12.0.

Post with the query parameters device and addr to add entries to the discovery cache.

```
curl -X POST http://127.0.0.1:8384/rest/system/discovery?device=LGFPDIT7SKNNJVJZA4FC7QNCRKCE753K72BW5QD2FOZ7FRFEP57Q\&addr=192.162.129.11:22000
# Or with the X-API-Key header:
curl -X POST --header "X-API-Key: TcE28kVPdtJ8COws1JdM0b2nodj77WeQ" http://127.0.0.1:8384/rest/system/discovery?device=LGFPDIT7SKNNJVJZA4FC7QNCRKCE753K72BW5QD2FOZ7FRFEP57Q\&addr=192.162.129.11:22000
```

### POST /rest/system/error/clear
Post with empty to body to remove all recent errors.

### GET /rest/system/error
NOTE:

Return format changed in 0.12.0.

Returns the list of recent errors.

```
{

  "errors": [

    {

      "when": "2014-09-18T12:59:26.549953186+02:00",

      "message": "This is an error string"

    }

  ]
}
```

### POST /rest/system/error
Post with an error message in the body (plain text) to register a new error. The new error will be displayed on any active GUI clients.

### GET /rest/system/log
Added in version 0.12.0.

Returns the list of recent log entries. The optional since parameter limits the results to message newer than the given timestamp in RFC 3339 <https://datatracker.ietf.org/doc/html/rfc3339.html> format.

```
{

  "messages": [

    {

      "when": "2014-09-18T12:59:26.549953186+02:00",

      "message": "This is a log entry"

    }

  ]
}
```

### GET /rest/system/log.txt
Returns the same information, formatted as a text log instead of a JSON object.

### GET /rest/system/loglevels
Added in version 2.0.0.

Returns the set of log facilities and their current log level.

```
{

  "levels": {

    "api": "INFO",

    "beacon": "INFO",

    ...

    "versioner": "INFO",

    "watchaggregator": "INFO"

  },

  "packages": {

    "api": "REST API",

    "beacon": "Multicast and broadcast discovery",

    ...

    "versioner": "File versioning",

    "watchaggregator": "Filesystem event watcher"

  }
}
```

### POST /rest/system/loglevels
Added in version 2.0.0.

Changes the log level for specified facilities. Post an object with the log facilites as keys and desired log level (DEBUG, INFO, WARN, or ERROR) as the values.

```
$ curl -H X-API-Key:abc123 -d '{"beacon":"DEBUG","discovery":"WARN"}' 'http://localhost:8384/rest/system/loglevels'
```

### GET /rest/system/paths
Added in version 1.21.0.

Returns the path locations used internally for storing configuration, database, and others.

```
{

  "auditLog": "/home/user/.local/share/syncthing/audit-${timestamp}.log",

  "baseDir-config": "/home/user/.config/syncthing",

  "baseDir-data": "/home/user/.local/share/syncthing",

  "baseDir-userHome": "/home/user",

  "certFile": "/home/user/.config/syncthing/cert.pem",

  "config": "/home/user/.config/syncthing/config.xml",

  "csrfTokens": "/home/user/.config/syncthing/csrftokens.txt",

  "database": "/home/user/.local/share/syncthing/index-v2",

  "defFolder": "/home/user/Sync",

  "guiAssets": "/home/user/src/syncthing/gui",

  "httpsCertFile": "/home/user/.config/syncthing/https-cert.pem",

  "httpsKeyFile": "/home/user/.config/syncthing/https-key.pem",

  "keyFile": "/home/user/.config/syncthing/key.pem",

  "logFile": "-",

  "panicLog": "/home/user/.local/share/syncthing/panic-${timestamp}.log"
}
```

### POST /rest/system/pause
Pause the given device or all devices.

Takes the optional parameter device (device ID). When omitted, pauses all devices. Returns status 200 and no content upon success, or status 500 and a plain text error on failure.

### GET /rest/system/ping
Returns a {"ping": "pong"} object.

```
{

  "ping": "pong"
}
```

### POST /rest/system/ping
Returns a {"ping": "pong"} object.

### POST /rest/system/reset
Post with empty body to erase the current index database and restart Syncthing. With no query parameters, the entire database is erased from disk. By specifying the folder parameter with a valid folder ID, only information for that folder will be erased:

```
curl -X POST -H "X-API-Key: abc123" http://localhost:8384/rest/system/reset?folder=ab1c2-def3g
```

Caution: See --reset-database for .stfolder creation side-effect and caution regarding mountpoints.

### POST /rest/system/restart
Post with empty body to immediately restart Syncthing.

### POST /rest/system/resume
Resume the given device or all devices.

Takes the optional parameter device (device ID). When omitted, resumes all devices. Returns status 200 and no content upon success, or status 500 and a plain text error on failure.

### POST /rest/system/shutdown
Post with empty body to cause Syncthing to exit and not restart.

### GET /rest/system/status
Returns information about current system status and resource usage. The CPU percent value has been deprecated from the API and will always report 0.

```
{

  "alloc": 30618136,

  "connectionServiceStatus": {

    "dynamic+https://relays.syncthing.net/endpoint": {

      "error": null,

      "lanAddresses": [

        "relay://23.92.71.120:443/?id=53STGR7-YBM6FCX-PAZ2RHM-YPY6OEJ-WYHVZO7-PCKQRCK-PZLTP7T-434XCAD&pingInterval=1m0s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=canton7"

      ],

      "wanAddresses": [

        "relay://23.92.71.120:443/?id=53STGR7-YBM6FCX-PAZ2RHM-YPY6OEJ-WYHVZO7-PCKQRCK-PZLTP7T-434XCAD&pingInterval=1m0s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=canton7"

      ]

    },

    "tcp://0.0.0.0:22000": {

      "error": null,

      "lanAddresses": [

        "tcp://0.0.0.0:22000"

      ],

      "wanAddresses": [

        "tcp://0.0.0.0:22000"

      ]

    }

  },

  "cpuPercent": 0,

  "discoveryEnabled": true,

  "discoveryErrors": {

    "global@https://discovery-v4-1.syncthing.net/v2/": "500 Internal Server Error",

    "global@https://discovery-v4-2.syncthing.net/v2/": "Post https://discovery-v4-2.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)",

    "global@https://discovery-v4-3.syncthing.net/v2/": "Post https://discovery-v4-3.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)",

    "global@https://discovery-v6-1.syncthing.net/v2/": "Post https://discovery-v6-1.syncthing.net/v2/: dial tcp [2001:470:28:4d6::5]:443: connect: no route to host",

    "global@https://discovery-v6-2.syncthing.net/v2/": "Post https://discovery-v6-2.syncthing.net/v2/: dial tcp [2604:a880:800:10::182:a001]:443: connect: no route to host",

    "global@https://discovery-v6-3.syncthing.net/v2/": "Post https://discovery-v6-3.syncthing.net/v2/: dial tcp [2400:6180:0:d0::d9:d001]:443: connect: no route to host"

  },

  "discoveryStatus": {

    "IPv4 local": {

      "error": null

    },

    "IPv6 local": {

      "error": null

    },

    "global@https://discovery-v4-1.syncthing.net/v2/": {

      "error": "500 Internal Server Error"

    },

    "global@https://discovery-v4-2.syncthing.net/v2/": {

      "error": "Post https://discovery-v4-2.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)"

    },

    "global@https://discovery-v4-3.syncthing.net/v2/": {

      "error": "Post https://discovery-v4-3.syncthing.net/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)"

    },

    "global@https://discovery-v6-1.syncthing.net/v2/": {

      "error": "Post https://discovery-v6-1.syncthing.net/v2/: dial tcp [2001:470:28:4d6::5]:443: connect: no route to host"

    },

    "global@https://discovery-v6-2.syncthing.net/v2/": {

      "error": "Post https://discovery-v6-2.syncthing.net/v2/: dial tcp [2604:a880:800:10::182:a001]:443: connect: no route to host"

    },

    "global@https://discovery-v6-3.syncthing.net/v2/": {

      "error": "Post https://discovery-v6-3.syncthing.net/v2/: dial tcp [2400:6180:0:d0::d9:d001]:443: connect: no route to host"

    }

  },

  "discoveryMethods": 8,

  "goroutines": 49,

  "lastDialStatus": {

      "tcp://10.20.30.40": {

        "when": "2019-05-16T07:41:23Z",

        "error": "dial tcp 10.20.30.40:22000: i/o timeout"

      },

      "tcp://172.16.33.3:22000": {

        "when": "2019-05-16T07:40:43Z",

        "ok": true

      },

      "tcp://83.233.120.221:22000": {

        "when": "2019-05-16T07:41:13Z",

        "error": "dial tcp 83.233.120.221:22000: connect: connection refused"

      }

  },

  "myID": "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2",

  "pathSeparator": "/",

  "startTime": "2016-06-06T19:41:43.039284753+02:00",

  "sys": 42092792,

  "themes": [

    "default",

    "dark"

  ],

  "tilde": "/Users/jb",

  "uptime": 2635
}
```

Added in version 1.2.0: The lastDialStatus dictionary contains the last error (or null for success) for each peer address that Syncthing has attempted to contact. The connectionServiceStatus entries gained "error": null attributes where previously there would be no error attribute at all in the success case.

Added in version 1.18.0: The discoveryStatus dictionary lists all configured discovery methods, not only failed ones like the now deprecated discoveryErrors. Each entry is an object itself (for consistency with other fields), where a null value for the error attribute means the method is running successfully. This effectively deprecates the discoveryMethods attribute as well, which now always matches the number of entries in discoveryStatus.

### GET /rest/system/upgrade
Checks for a possible upgrade and returns an object describing the newest version and upgrade possibility.

```
{

  "latest": "v0.14.47",

  "majorNewer": false,

  "newer": true,

  "running": "v0.14.46"
}
```

### POST /rest/system/upgrade
Perform an upgrade to the newest released version and restart. Does nothing if there is no newer version than currently running.

### GET /rest/system/version
Returns the current Syncthing version information.

```
{

  "arch": "amd64",

  "longVersion": "syncthing v0.10.27+3-gea8c3de (go1.4 darwin-amd64 default) jb@syno 2015-03-16 11:01:29 UTC",

  "os": "darwin",

  "version": "v0.10.27+3-gea8c3de"
}
```

## CONFIG ENDPOINTS

### Config Endpoints
Added in version 1.12.0.

These endpoints facilitate access and modification of the configuration in a granular way. Config sent to the endpoints must be in the same format as returned by the corresponding GET request. When posting the configuration succeeds, the posted configuration is immediately applied, except for changes that require a restart. Query /rest/config/restart-required to check if a restart is required.

For all endpoints supporting PATCH, it takes the existing config and unmarshals the given JSON object on top of it. This means all child objects will replace the existing objects, not extend them. For example for RawListenAddresses in options, which is an array of strings, sending {RawListenAddresses: ["tcp://10.0.0.2"]} will replace all existing listen addresses.

### /rest/config
GET returns the entire config and PUT replaces it.

### /rest/config/restart-required
GET returns whether a restart of Syncthing is required for the current config to take effect.

### /rest/config/folders, /rest/config/devices
GET returns all folders respectively devices as an array. PUT takes an array and POST a single object. In both cases if a given folder/device already exists, it’s replaced, otherwise a new one is added.

### /rest/config/folders/*id*, /rest/config/devices/*id*
Put the desired folder- respectively device-ID in place of *id*. GET returns the folder/device for the given ID, PUT replaces the entire config, PATCH replaces only the given child objects and DELETE removes the folder/device.

### /rest/config/defaults/folder, /rest/config/defaults/device
GET returns a template folder / device configuration object with all default values, which only needs a unique ID to be applied. PUT replaces the default config (omitted values are reset to the hard-coded defaults), PATCH replaces only the given child objects.

### /rest/config/defaults/ignores
Added in version 1.19.0.

GET returns an object with a single lines attribute listing ignore patterns to be used by default on folders, as an array of single-line strings. PUT replaces the default ignore patterns from an object of the same format.

### /rest/config/options, /rest/config/ldap, /rest/config/gui
GET returns the respective object, PUT replaces the entire object and PATCH replaces only the given child objects.

NOTE:

The gui.password configuration option has special handling to accept already hashed passwords. Any valid bcrypt hash is stored verbatim, while a plaintext password is first hashed.

## CLUSTER ENDPOINTS
Concerns the mesh network structure.

### DELETE /rest/cluster/pending/devices
Added in version 1.18.0.

Remove records about a pending remote device which tried to connect. Valid values for the device parameter are those from the corresponding GET /rest/cluster/pending/devices endpoint.

```
$ curl -X DELETE -H "X-API-Key: abc123" http://localhost:8384/rest/cluster/pending/devices?device=P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2
```

Returns status 200 and no content upon success, or status 500 and a plain text error on failure. A PendingDevicesChanged event will be generated in response.

For a more permanent effect, also for future connections from the same device ID, the device should be ignored in the configuration instead.

### GET /rest/cluster/pending/devices
Added in version 1.13.0.

Lists remote devices which have tried to connect, but are not yet configured in our instance.

```
{

  "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2": {

    "time": "2020-03-18T11:43:07Z",

    "name": "Friend Joe",

    "address": "192.168.1.2:22000"

  }
}
```

### DELETE /rest/cluster/pending/folders
Added in version 1.18.0.

Remove records about a pending folder announced from a remote device. Valid values for the folder and device parameters are those from the corresponding GET /rest/cluster/pending/folders endpoint. The device parameter is optional and affects announcements of this folder from the given device, or from any device if omitted.

```
$ curl -X DELETE -H "X-API-Key: abc123" http://localhost:8384/rest/cluster/pending/folders?folder=cpkn4-57ysy&device=P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2
```

Returns status 200 and no content upon success, or status 500 and a plain text error on failure. A PendingFoldersChanged event will be generated in response.

For a more permanent effect, also for future announcements of the same folder ID, the folder should be ignored in the configuration instead.

### GET /rest/cluster/pending/folders
Added in version 1.13.0.

Lists folders which remote devices have offered to us, but are not yet shared from our instance to them. Takes the optional device parameter to only return folders offered by a specific remote device. Other offering devices are also omitted from the result.

```
{

  "cpkn4-57ysy": {

    "offeredBy": {

      "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2": {

        "time": "2020-03-18T11:43:07Z",

        "label": "Joe's folder",

        "receiveEncrypted": true,

        "remoteEncrypted": false

      },

      "DOVII4U-SQEEESM-VZ2CVTC-CJM4YN5-QNV7DCU-5U3ASRL-YVFG6TH-W5DV5AA": {

        "time": "2020-03-01T10:12:13Z",

        "label": "Jane's and Joe's folder",

        "receiveEncrypted": false,

        "remoteEncrypted": false

      }

    }

  },

  "abcde-fghij": {

    "offeredBy": {

      "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2": {

        "time": "2020-03-18T11:43:07Z",

        "label": "MyPics",

        "receiveEncrypted": false,

        "remoteEncrypted": false

      }

    }

  }
}
```

## FOLDER ENDPOINTS
Runtime state of the individual shared folders.

### GET /rest/folder/errors
Added in version 0.14.53.

Takes one mandatory parameter, folder, and returns the list of errors encountered during scanning or pulling.

The results can be paginated using the common pagination parameters.

```
{

  "folder": "nnhic-sxuae",

  "errors": [

    {

      "path": "noperm.txt",

      "error": "hashing: open /path/to/folder/noperm.txt: permission denied"

    }

  ],

  "page": 1,

  "perpage": 100
}
```

### GET /rest/folder/pullerrors (DEPRECATED)
Deprecated since version v0.14.53: This endpoint still works as before but is deprecated. Use GET /rest/folder/errors instead, which returns the same information.

See https://github.com/syncthing/syncthing/commit/d510e3cca3d5caae42121fa206b3decc981ae59e

### GET /rest/folder/versions
Added in version 0.14.44.

Takes one mandatory parameter, folder, and returns the list of archived files that could be recovered. How many versions are available depends on the File Versioning configuration. Each entry specifies when the file version was archived as the versionTime, the modTime when it was last modified before being archived, and the size in bytes.

```
{

  "dir1/dir2/bar": [

    {

      "versionTime": "2022-02-06T20:44:12+01:00",

      "modTime": "2021-01-14T13:21:22+01:00",

      "size": 4

    }

  ],

  "baz": [

    {

      "versionTime": "2022-02-06T20:44:20+01:00",

      "modTime": "2021-01-14T13:23:49+01:00",

      "size": 4

    }

  ],

  "foo": [

    {

      "versionTime": "2022-02-06T20:55:31+01:00",

      "modTime": "2022-02-06T20:44:13+01:00",

      "size": 4

    },

    {

      "versionTime": "2022-02-06T20:44:20+01:00",

      "modTime": "2021-01-14T13:21:16+01:00",

      "size": 4

    }

  ]
}
```

### POST /rest/folder/versions
Restore archived versions of a given set of files. Expects an object with attributes named after the relative file paths, with timestamps as values matching valid versionTime entries in the corresponding GET /rest/folder/versions response object.

Takes the mandatory parameter folder (folder ID). Returns an object containing any error messages that occurred during restoration of the file, with the file path as attribute name.

```
curl -X POST -H X-API-key:... http://127.0.0.1:8384/rest/folder/versions?folder=default -d '{

  "dir1/dir2/bar": "2022-02-06T20:44:12+01:00",

  "baz": "2022-02-06T20:44:20+01:00"
}'
```

## DATABASE ENDPOINTS

### GET /rest/db/browse
Returns the directory tree of the global model. Directories are always JSON objects (map/dictionary), and files are always arrays of modification time and size. The first integer is the files modification time, and the second integer is the file size.

The call takes one mandatory folder parameter and two optional parameters. Optional parameter levels defines how deep within the tree we want to dwell down (0 based, defaults to unlimited depth) Optional parameter prefix defines a prefix within the tree where to start building the structure.

```
$ curl -s http://localhost:8384/rest/db/browse?folder=j663y-3ct3e&prefix=DCIM&levels=2
[

    {

        "modTime" : "2020-10-02T23:48:52.076996974+02:00",

        "name" : "100ANDRO",

        "size" : 128,

        "type" : "FILE_INFO_TYPE_DIRECTORY"

    },

    {

        "children" : [

            {

                "modTime" : "2020-12-16T23:31:34.5009668+01:00",

                "name" : "IMG_20201114_124821.jpg",

                "size" : 10682189,

                "type" : "FILE_INFO_TYPE_FILE"

            },

            {

                "modTime" : "2020-12-16T23:31:35.0106367+01:00",

                "name" : "IMG_20201213_122451.jpg",

                "size" : 7936351,

                "type" : "FILE_INFO_TYPE_FILE"

            },

            {

                "modTime" : "2020-12-13T12:25:05.017097469+01:00",

                "name" : "IMG_20201213_122504.jpg",

                "size" : 8406507,

                "type" : "FILE_INFO_TYPE_FILE"

            },

            {

                "modTime" : "2020-12-13T12:25:06.127097469+01:00",

                "name" : "IMG_20201213_122505.jpg",

                "size" : 8381931,

                "type" : "FILE_INFO_TYPE_FILE"

            },

            {

                "modTime" : "2020-12-13T12:53:29.707298401+01:00",

                "name" : "IMG_20201213_125329.jpg",

                "size" : 4388331,

                "type" : "FILE_INFO_TYPE_FILE"

            },

        ],

        "modTime" : "2020-10-09T13:04:42.4410738+02:00",

        "name" : "Camera",

        "size" : 128,

        "type" : "FILE_INFO_TYPE_DIRECTORY"

    },
]
```

NOTE:

This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly.

### GET /rest/db/completion
Returns the completion percentage (0 to 100) and byte / item counts. Takes optional device and folder parameters:

- folder specifies the folder ID to calculate completion for. An empty or absent folder parameter means all folders as an aggregate.

- device specifies the device ID to calculate completion for. An empty or absent device parameter means the local device.

If a device is specified but no folder, completion is calculated for all folders shared with that device.

### Example Queries
Completion status for folder abcd-1234 on device I6KAH76-...-3PSROAU:

```
/rest/db/completion?folder=abcd-1234&device=I6KAH76-...-3PSROAU
```

Aggregated completion status for device I6KAH76-...-3PSROAU (all folders shared with them):

```
/rest/db/completion?device=I6KAH76-...-3PSROAU
```

Completion status for folder abcd-1234 on the local device:

```
/rest/db/completion?folder=abcd-1234
```

Aggregated completion status for all folders on the local device:

```
/rest/db/completion
```

### Example Response

```
{

  "completion": 99.9937565835,

  "globalBytes": 156793013575,

  "needBytes": 9789241,

  "globalItems": 7823,

  "needItems": 412,

  "needDeletes": 0,

  "remoteState": "valid",

  "sequence": 12
}
```

Added in version 1.8.0: The ability to aggregate multiple folders by leaving out the folder ID. Querying data for the local device by leaving out the device ID. Returning the globalItems counter in the response.

Added in version 1.20.0: Indication whether the remote device has accepted the folder (shares it with us) as well, and whether it is paused. The remoteState field is meaningless for aggregated responses, unknown when the remote device is not connected. Otherwise it can be either paused, notSharing, or valid if the remote is sharing back.

### GET /rest/db/file
Returns most data about a given file, including version and availability.

### Request
The call requires parameters for folder and file in the query string:

- folder is the folder ID which you can find in the Syncthing Web GUI, e.g. 5camp-slpa8.

- file is the relative path of the file starting from the folder root to the file you are interested in. The path and filename must be correctly URL-encoded.

### Example Request

```
$ curl -s -H X-API-KEY:... "http://localhost:8384/rest/db/file?folder=YOUR_FOLDER_ID" --url-query "file=path/to/file.pdf"
```

### Response

```
{

  "availability": [

    {

      "id": "ITZRNXE-YNROGBZ-HXTH5P7-VK5NYE5-QHRQGE2-7JQ6VNJ-KZUEDIU-5PPR5AM",

      "fromTemporary": false

    }

  ],

  "global": { /* a file entry */ },

  "local": { /* a file entry */ },

  "mtime": {

    "err": null,

    "value": {

      "real": "0001-01-01T00:00:00Z",

      "virtual": "0001-01-01T00:00:00Z"

  }
}
```

local and global refer to the current file on the local device and the globally newest file, respectively. A file entry looks like this:

```
{

  "deleted": false,

  "ignored": false,

  "inodeChange": "1970-01-01T01:00:00+01:00",

  "invalid": false,

  "localFlags": 0,

  "modified": "2022-09-28T08:07:19.979723+02:00",

  "modifiedBy": "523ITIE",

  "mustRescan": false,

  "name": "img",

  "noPermissions": false,

  "numBlocks": 0,

  "permissions": "0755",

  "platform": { /* platform specific data */ },

  "sequence": 914,

  "size": 128,

  "type": "FILE_INFO_TYPE_DIRECTORY",

  "version": [

    "523ITIE:1664345275"

  ]
}
```

Platform specific data may be ownership, extended attributes, etc. and is divided into entries per operating system / platform. An example platform entry containing ownership information for Unix systems and an extended attribute for macOS (“darwin”) looks as follows:

```
{

  "darwin": {

    "xattrs": [

      {

        "name": "net.kastelo.xattrtest",

        "value": "aGVsbG8="

      }

    ]

  },

  "freebsd": null,

  "linux": null,

  "netbsd": null,

  "unix": {

    "gid": 20,

    "groupName": "staff",

    "ownerName": "jb",

    "uid": 501

  },

  "windows": null
}
```

### GET /rest/db/ignores
Takes one parameter, folder, and returns the content of the .stignore as the ignore field. A second field, expanded, provides a list of strings which represent globbing patterns described by gobwas/glob (based on standard wildcards) that match the patterns in .stignore and all the includes. If appropriate these globs are prepended by the following modifiers: ! to negate the glob, (?i) to do case insensitive matching and (?d) to enable removing of ignored files in an otherwise empty directory.

```
{

  "ignore": [

    "(?i)/Backups"

  ],

  "expanded": [

    "(?i)Backups",

    "(?i)Backups/**"

  ]
}
```

### POST /rest/db/ignores
Expects a format similar to the output of GET /rest/db/ignores call, but only containing the ignore field (expanded field should be omitted). It takes one parameter, folder, and either updates the content of the .stignore echoing it back as a response, or returns an error.

### GET /rest/db/localchanged
Added in version 0.14.55.

Takes one mandatory parameter, folder, and returns the list of files which were changed locally in a receive-only folder. Thus they differ from the global state and could be reverted by pulling from remote devices again, see POST /rest/db/revert.

The results can be paginated using the common pagination parameters.

```
{

  "files": [

    {

      "flags": "0755",

      "sequence": 6,

      "modified": "2015-04-20T23:06:12+09:00",

      "name": "ls",

      "size": 34640,

      "version": [

        "5157751870738175669:1"

      ]

    }

  ],

  "page": 1,

  "perpage": 100
}
```

NOTE:

This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly.

### GET /rest/db/need
Takes one mandatory parameter, folder, and returns lists of files which are needed by this device in order for it to become in sync.

The results can be paginated using the common pagination parameters. Pagination happens, across the union of all needed files, that is - across all 3 sections of the response. For example, given the current need state is as follows:

1.

progress has 15 items

2.

queued has 3 items

3.

rest has 12 items

If you issue a query with page=1 and perpage=10, only the progress section in the response will have 10 items. If you issue a request query with page=2 and perpage=10, progress section will have the last 5 items, queued section will have all 3 items, and rest section will have first 2 items. If you issue a query for page=3 and perpage=10, you will only have the last 10 items of the rest section.

NOTE:

Return format changed in version 0.14.43, removing the total count attribute.

```
{

  # Files currently being downloaded

  "progress": [

    {

      "flags": "0755",

      "sequence": 6,

      "modified": "2015-04-20T23:06:12+09:00",

      "name": "ls",

      "size": 34640,

      "version": [

        "5157751870738175669:1"

      ]

    }

  ],

  # Files queued to be downloaded next (as per array order)

  "queued": [

      ...

  ],

  # Files to be downloaded after all queued files will be downloaded.

  # This happens when we start downloading files, and new files get added while we are downloading.

  "rest": [

      ...

  ],

  "page": 1,

  "perpage": 100
}
```

NOTE:

This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly.

### POST /rest/db/override
Request override of a send only folder. Override means to make the local version latest, overriding changes made on other devices. This API call does nothing if the folder is not a send only folder.

Takes the mandatory parameter folder (folder ID).

```
curl -X POST -H X-API-key:... http://127.0.0.1:8384/rest/db/override?folder=default
```

### POST /rest/db/prio
Moves the file to the top of the download queue.

```
curl -X POST http://127.0.0.1:8384/rest/db/prio?folder=default&file=foo/bar
```

Response contains the same output as GET /rest/db/need.

### GET /rest/db/remoteneed
Added in version 0.14.43.

Takes the mandatory parameters folder and device, and returns the list of files which are needed by that remote device in order for it to become in sync with the shared folder.

The results can be paginated using the common pagination parameters.

```
{

  "files": [

    {

      "flags": "0755",

      "sequence": 6,

      "modified": "2015-04-20T23:06:12+09:00",

      "name": "ls",

      "size": 34640,

      "version": [

        "5157751870738175669:1"

      ]

    }

  ],

  "page": 1,

  "perpage": 100
}
```

NOTE:

This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly.

### POST /rest/db/revert
Added in version 0.14.50.

Request revert of a receive only folder. Reverting a folder means to undo all local changes. This API call does nothing if the folder is not a receive only folder.

Takes the mandatory parameter folder (folder ID).

```
curl -X POST -H X-API-Key:... http://127.0.0.1:8384/rest/db/revert?folder=default
```

### POST /rest/db/scan
Request immediate scan. Takes the optional parameters folder (folder ID), sub (path relative to the folder root) and next (time in seconds). If folder is omitted or empty all folders are scanned. If sub is given, only this path (and children, in case it’s a directory) is scanned. The next argument delays Syncthing’s automated rescan interval for a given amount of seconds.

Requesting scan of a path that no longer exists, but previously did, is valid and will result in Syncthing noticing the deletion of the path in question.

Returns status 200 and no content upon success, or status 500 and a plain text error if an error occurred during scanning.

```
curl -X POST http://127.0.0.1:8384/rest/db/scan?folder=default&sub=foo/bar
```

### GET /rest/db/status
Returns information about the current status of a folder.

Parameters: folder, the ID of a folder.

```
{

  "globalBytes": 0,

  "globalDeleted": 0,

  "globalDirectories": 0,

  "globalFiles": 0,

  "globalSymlinks": 0,

  "globalTotalItems": 0,

  "ignorePatterns": false,

  "inSyncBytes": 0,

  "inSyncFiles": 0,

  "invalid": "",

  "localBytes": 0,

  "localDeleted": 0,

  "localDirectories": 0,

  "localFiles": 0,

  "localSymlinks": 0,

  "localTotalItems": 0,

  "needBytes": 0,

  "needDeletes": 0,

  "needDirectories": 0,

  "needFiles": 0,

  "needSymlinks": 0,

  "needTotalItems": 0,

  "pullErrors": 0,

  "receiveOnlyChangedBytes": 0,

  "receiveOnlyChangedDeletes": 0,

  "receiveOnlyChangedDirectories": 0,

  "receiveOnlyChangedFiles": 0,

  "receiveOnlyChangedSymlinks": 0,

  "receiveOnlyTotalItems": 0,

  "sequence": 0,

  "state": "idle",

  "stateChanged": "2018-08-08T07:04:57.301064781+02:00",

  "version": 0
}
```

The various fields have the following meaning:

global*:

Data in the cluster latest version.

inSync*:

Data that is locally the same as the cluster latest version.

local*:

Data that is locally present, regardless of whether it’s the same or different version as the cluster latest version.

need*:

Data that is needed to become up to date with the cluster latest version (i.e., data that is out of sync).

receiveOnlyChanged*:

Data that has changed locally in a receive only folder, and thus not been sent to the cluster.

invalid:

Deprecated, always empty.

pullErrors:

The number of files that failed to sync during the last sync operations.

sequence:

The current folder sequence number.

state:

The current folder state.

stateChanged:

When the folder state last changed.

version:

Deprecated, equivalent to the sequence number.

NOTE:

This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly.

## EVENT ENDPOINTS

### GET /rest/events
To receive events, perform a HTTP GET of /rest/events.

To filter the event list, in effect creating a specific subscription for only the desired event types, add a parameter events=EventTypeA,EventTypeB,... where the event types are any of the Event Types. If no filter is specified, all events except LocalChangeDetected and RemoteChangeDetected are included.

The optional parameter since=<lastSeenID> sets the ID of the last event you’ve already seen. Syncthing returns a JSON encoded array of event objects, starting at the event just after the one with this last seen ID. The default value is 0, which returns all events. There is a limit to the number of events buffered, so if the rate of events is high or the time between polling calls is long some events might be missed. This can be detected by noting a discontinuity in the event IDs.

If no new events are produced since <lastSeenID>, the HTTP call blocks and waits for new events to happen before returning. If <lastSeenID> is a future ID, the HTTP call blocks until such ID is reached or timeouts. By default it times out after 60 seconds returning an empty array. The time out duration can be customized with the optional parameter timeout=<seconds>.

To receive only a limited number of events, add the limit=<n> parameter with a suitable value for n and only the last n events will be returned. This can be used to catch up with the latest event ID after a disconnection for example: /rest/events?since=0&limit=1.

### GET /rest/events/disk
This convenience endpoint provides the same event stream, but pre-filtered to show only LocalChangeDetected and RemoteChangeDetected event types. The events parameter is not used.

## STATISTICS ENDPOINTS

### GET /rest/stats/device
Returns general statistics about devices. Currently, only contains the time the device was last seen and the last connection duration.

```
$ curl -s http://localhost:8384/rest/stats/device | json
{

  "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2": {

    "lastSeen" : "2015-04-18T11:21:31.3256277+01:00"

    "lastConnectionDurationS": 556335.421708141

  }
}
```

### GET /rest/stats/folder
Returns general statistics about folders. Currently contains the last scan time and the last synced file.

```
{

  "folderid": {

    "lastScan": "2016-06-02T13:28:01.288181412-04:00",

    "lastFile": {

      "filename": "file/name",

      "at": "2015-04-16T22:04:18.3066971+01:00"

    }

  }
}
```

## MISC SERVICES ENDPOINTS

### GET /rest/svc/deviceid
Verifies and formats a device ID. Accepts all currently valid formats (52 or 56 characters with or without separators, upper or lower case, with trivial substitutions). Takes one parameter, id, and returns either a valid device ID in modern format, or an error.

```
$ curl -s http://localhost:8384/rest/svc/deviceid?id=1234 | json
{

  "error": "device ID invalid: incorrect length"
}
$ curl -s http://localhost:8384/rest/svc/deviceid?id=p56ioi7m--zjnu2iq-gdr-eydm-2mgtmgl3bxnpq6w5btbbz4tjxzwicq | json
{

  "id": "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2"
}
```

### GET /rest/svc/lang
Returns a list of canonicalized localization codes, as picked up from the Accept-Language header sent by the browser.

```
["sv_sv","sv","en_us","en"]
```

### GET /rest/svc/random/string
Returns a strong random generated string (alphanumeric) of the specified length. Takes the length parameter.

```
{

  "random": "FdPaEaZQ56sXEKYNxpgF"
}
```

### GET /rest/svc/report
Returns the data sent in the anonymous usage report.

```
{

   "folderMaxMiB" : 0,

   "platform" : "linux-amd64",

   "totMiB" : 0,

   "longVersion" : "syncthing v0.12.2 \"Beryllium Bedbug\" (go1.4.3 linux-amd64 default) unknown-user@build2.syncthing.net 2015-11-09 13:23:26 UTC",

   "upgradeAllowedManual" : true,

   "totFiles" : 3,

   "folderUses" : {

      "ignorePerms" : 0,

      "autoNormalize" : 0,

      "sendonly" : 0,

      "ignoreDelete" : 0

   },

   "memoryUsageMiB" : 13,

   "version" : "v0.12.2",

   "sha256Perf" : 27.28,

   "numFolders" : 2,

   "memorySize" : 1992,

   "announce" : {

      "defaultServersIP" : 0,

      "otherServers" : 0,

      "globalEnabled" : false,

      "defaultServersDNS" : 1,

      "localEnabled" : false

   },

   "usesRateLimit" : false,

   "numCPU" : 2,

   "uniqueID" : "",

   "urVersion" : 2,

   "rescanIntvs" : [

      60,

      60

   ],

   "numDevices" : 2,

   "folderMaxFiles" : 3,

   "relays" : {

      "defaultServers" : 1,

      "enabled" : true,

      "otherServers" : 0

   },

   "deviceUses" : {

      "compressMetadata" : 1,

      "customCertName" : 0,

      "staticAddr" : 1,

      "compressAlways" : 0,

      "compressNever" : 1,

      "introducer" : 0,

      "dynamicAddr" : 1

   },

   "upgradeAllowedAuto" : false
}
```

## DEBUG ENDPOINTS

### GET /rest/debug/cpuprof
Used to capture a profile of what Syncthing is doing on the CPU. See Profiling.

### GET /rest/debug/heapprof
Used to capture a profile of what Syncthing is doing with the heap memory. See Profiling.

### GET /rest/debug/support
Collects information about the running instance for troubleshooting purposes. Returns a “support bundle” as a zipped archive, which should be sent to the developers after verifying it contains no sensitive personal information. Credentials for the web GUI and the API key are automatically redacted already.

### GET /rest/debug/file
Shows diagnostics about a certain file in a shared folder. Takes the folder (folder ID) and file (folder relative path) parameters.

```
$ curl -H X-API-Key:... "http://localhost:8384/rest/debug/file?folder=default&file=foo/bar"
```

The returned object contains the same info as GET /rest/db/file, plus a summary of globalVersions.

## NOAUTH ENDPOINTS
Calls that do not require authentication.

### GET /rest/noauth/health
Returns a {"status": "OK"} object.

```
{

  "status": "OK"
}
```

## AUTHOR
The Syncthing Authors

## COPYRIGHT
2014-2026, The Syncthing Authors


syncthing-security

title: syncthing-security(7)

summary: Security Principles

text:
# syncthing-security(7)

## NAME
syncthing-security - Security Principles

Security is one of the primary project goals. This means that it should not be possible for an attacker to join a cluster uninvited, and it should not be possible to extract private information from intercepted traffic. Currently this is implemented as follows.

All device to device traffic is protected by TLS. To prevent uninvited devices from joining a cluster, the certificate fingerprint of each device is compared to a preset list of acceptable devices at connection establishment. The fingerprint is computed as the SHA-256 hash of the certificate and displayed in a human-friendly encoding, called Device ID.

Incoming requests for file data are verified to the extent that the requested file name must exist in the local index and the global model.

For information about ensuring you are running the code you think you are and for reporting security vulnerabilities, please see the official security page <https://syncthing.net/security>.

## INFORMATION LEAKAGE

### Global Discovery
When global discovery is enabled, Syncthing sends an announcement every 30 minutes to the global discovery servers so that they can keep a mapping between your device ID and external IP. The announcement contain the device ID and listening port(s). Also, when connecting to other devices that have not been seen on the local network, a query is sent to the global discovery servers containing the device ID of the requested device. The connection to the discovery server is encrypted using TLS and the discovery server certificate is verified, so the contents of the query should be considered private between the device and the discovery server. The discovery servers are currently hosted by @calmh <https://github.com/calmh>. Global discovery defaults to on.

When turned off, devices with dynamic addresses not on the local network cannot be found and connected to.

An eavesdropper on the Internet can deduce which machines are running Syncthing with global discovery enabled, and what their device IDs are.

The operator of the discovery server can map arbitrary device addresses to IP addresses, and deduce which devices are connected to each other.

If a different global discovery server is configured, no data is sent to the default global discovery servers.

### Local Discovery
When local discovery is enabled, Syncthing sends broadcast (IPv4) and multicast (IPv6) packets to the local network every 30 seconds. The packets contain the device ID and listening port. Local discovery defaults to on.

An eavesdropper on the local network can deduce which machines are running Syncthing with local discovery enabled, and what their device IDs are.

When turned off, devices with dynamic addresses on the local network cannot be found and connected to.

### Upgrade Checks
When automatic upgrades are enabled, Syncthing checks for a new version at startup and then once every twelve hours. This is by an HTTPS request to the download site for releases, currently hosted by @calmh <https://github.com/calmh>. Automatic upgrades default to on (unless Syncthing was compiled with upgrades disabled).

Even when automatic upgrades are disabled in the configuration, an upgrade check as above is done when the GUI is loaded, in order to show the “Upgrade to …” button when necessary. This can be disabled only by compiling Syncthing with upgrades disabled.

The actual download, should an upgrade be available, is done from GitHub, thus exposing the user to them.

The upgrade check (or download) requests do not contain any identifiable information about the user or device.

### Usage Reporting
When usage reporting is enabled, Syncthing reports usage data at startup and then every 24 hours. The report is sent as an HTTPS POST to the usage reporting server, currently hosted by @calmh <https://github.com/calmh>. The contents of the usage report can be seen behind the “Preview” link in settings. Usage reporting defaults to off but the GUI will ask once about enabling it, shortly after the first install.

The reported data is protected from eavesdroppers, but the connection to the usage reporting server itself may expose the client as running Syncthing.

### Sync Connections (BEP)
Sync connections are attempted to all configured devices, when the address is possible to resolve. The sync connection is based on TLS 1.2 or TLS 1.3. The TLS certificates can be obtained by an eavesdropper, although it is more difficult to do so in TLS 1.3. This means that the contents of the certificate are visible, which includes certificate Common Name (by default syncthing).

An eavesdropper can deduce that this is a Syncthing connection and under certain circumstances calculate the device IDs involved based on the hashes of the sent certificates.

Likewise, if the sync port (default 22000) is accessible from the internet, a port scanner may discover it, attempt a TLS negotiation and thus obtain the device certificate. This provides the same information as in the eavesdropper case.

### Relay Connections
When relaying is enabled, Syncthing will look up the pool of public relays and establish a connection to one of them (the best, based on an internal heuristic). The selected relay server will learn the connecting device’s device ID. Relay servers can be run by anyone in the general public. Relaying defaults to on. Syncthing can be configured to disable relaying, or only use specific relays.

If a relay connections is required between two devices, the relay will learn the other device’s device ID as well.

Any data exchanged between the two devices is encrypted as usual and not subject to inspection by the relay.

### Web GUI
If the web GUI is accessible, it exposes the device as running Syncthing. The web GUI defaults to being reachable from the local host only.

## IN SHORT
Parties doing surveillance on your network (whether that be corporate IT, the NSA or someone else) will be able to see that you use Syncthing, and your device IDs are OK to share anyway <https://docs.syncthing.net/users/faq.html#should-i-keep-my-device-ids-secret>, but the actual transmitted data is protected as well as we can. Knowing your device ID can expose your IP address, using global discovery.

## PROTECTING YOUR SYNCTHING KEYS AND IDENTITY
Anyone who can access the Syncthing TLS keys and config file on your device can impersonate your device, connect to your peers, and then have access to your synced files. Here are some general principles to protect your files:

1.

If a device of yours is lost, make sure to revoke its access from your other devices.

2.

If you’re syncing confidential data on an encrypted disk to guard against device theft, put the Syncthing config folder on the same encrypted disk to avoid leaking keys and metadata. Or, use whole disk encryption.

## AUTHOR
The Syncthing Authors

## COPYRIGHT
2014-2026, The Syncthing Authors


syncthing-versioning

title: syncthing-versioning(7)

text:
# syncthing-versioning(7)

## NAME
syncthing-versioning - Keep automatic backups of deleted files by other nodes

Syncthing supports archiving the old version of a file when it is deleted or replaced with a newer version from the cluster. This is called “file versioning” and uses one of the available versioning strategies described below. File versioning is configured per folder, on a per-device basis, and defaults to “no file versioning”, i.e. no old copies of files are kept.

NOTE:

Versioning applies to changes received from other devices. That is, if Alice has versioning turned on and Bob changes a file, the old version will be archived on Alice’s computer when that change is synced from Bob. If Alice changes a file locally on her own computer Syncthing will not and can not archive the old version.

The applicable configuration options for each versioning strategy are described below. For most of them it’s possible to specify where the versions are stored, with the default being the .stversions folder inside the shared folder path. If you set a custom version path, please ensure that it’s on the same partition or filesystem as the regular folder path, as moving files there may otherwise fail.

## TRASH CAN FILE VERSIONING
This versioning strategy emulates the common “trash can” approach. When a file is deleted or replaced due to a change on a remote device, it is moved to the trash can in the .stversions folder. If a file with the same name was already in the trash can it is replaced.

A configuration option is available to clean the trash can from files older than a specified number of days. If this is set to a positive number of days, files will be removed when they have been in the trash can that long. Setting this to zero prevents any files from being removed from the trash can automatically.

## SIMPLE FILE VERSIONING
With “Simple File Versioning” files are moved to the .stversions folder when replaced or deleted on a remote device. In addition to the cleanoutDays option, this strategy also takes a value in an input titled “Keep Versions” which tells Syncthing how many old versions of the file it should keep. For example, if you set this value to 5, if a file is replaced 5 times on a remote device, you will see 5 time-stamped versions on that file in the .stversions folder on the other devices sharing the same folder.

## STAGGERED FILE VERSIONING
With “Staggered File Versioning” files are also moved to the .stversions folder when replaced or deleted on a remote device (just like “Simple File Versioning”), however, versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.

The following intervals are used and they each have a maximum number of files that will be kept for each.

1 Hour

For the first hour, the oldest version in every 30-seconds interval is kept.

1 Day

For the first day, the oldest version in every hour is kept.

30 Days

For the first 30 days, the oldest version in every day is kept.

Until Maximum Age

Until maximum age, the oldest version in every week is kept.

Maximum Age

The maximum time to keep a version in days. For example, to keep replaced or deleted files in the .stversions folder for an entire year, use 365. If only for 10 days, use 10. Corresponds to the maxAge option. Note: Set to 0 to keep versions forever.

This means that there is only one version in each interval and as files age they will be deleted unless when the interval they are entering is empty. By keeping the oldest versions this versioning scheme preserves the file if it is overwritten.

For more info, check the unit test file <https://github.com/syncthing/syncthing/blob/main/lib/versioner/staggered_test.go#L32> that shows which versions are deleted for a specific run.

## EXTERNAL FILE VERSIONING
This versioning strategy delegates the decision on what to do to an external command (e.g. a program or a command line script). Just prior to a file being replaced, the command will be executed. The file needs to be removed from the folder in the process, or otherwise Syncthing will report an error. The command can use the following templated arguments:

%FOLDER_PATH%

Path to the folder

%FILE_PATH%

Path to the file within the folder

Note that the former expands to the path of the actual Syncthing folder, and the latter to the path inside that folder. For instance, if you use the default Sync folder in Windows, and the full path to the file is C:\Users\User\Sync\Family photos\IMG_2021-03-01.jpg, then the %FOLDER_PATH% will be C:\Users\User\Sync, and the %FILE_PATH% will be Family photos\IMG_2021-03-01.jpg.

### Example for Unixes
Let’s say I want to keep the latest version of each file as they are replaced or removed; essentially I want a “trash can”-like behavior. For this, I create the following script and store it as /Users/jb/bin/onlylatest.sh (i.e. the bin directory in my home directory):

```
#!/bin/sh
set -eu
# Where I want my versions stored
versionspath=~/.trashcan
# The parameters we get from Syncthing
folderpath="$1"
filepath="$2"
# First ensure the dir where we need to store the file exists
outpath=$(dirname "$versionspath/$filepath")
mkdir -p "$outpath"
# Then move the file there
mv -f "$folderpath/$filepath" "$versionspath/$filepath"
```

I must ensure that the script has execute permissions (chmod 755 onlylatest.sh), then configure Syncthing with command /Users/jb/bin/onlylatest.sh %FOLDER_PATH% %FILE_PATH%

Let’s assume I have a folder “default” in ~/Sync, and that within that folder there is a file docs/letter.txt that is being replaced or deleted. The script will be called as if I ran this from the command line:

```
$ /Users/jb/bin/onlylatest.sh /Users/jb/Sync docs/letter.txt
```

The script will then move the file in question to ~/.trashcan/docs/letter.txt, replacing any previous version of that letter that may already have been there.

### Move to a given folder using the command prompt (CMD)
On Windows we can use a batch script to perform the same “trash can”-like behavior as mentioned above. I created the following script and saved it as C:\Users\mfrnd\Scripts\onlylatest.bat.

```
@echo off
rem Enable UTF-8 encoding to deal with multilingual folder and file names
chcp 65001
rem We need command extensions for md to create intermediate folders in one go
setlocal enableextensions
rem Where I want my versions stored
set "versions_path=%USERPROFILE%\.trashcan"
rem The parameters we get from Syncthing, '~' removes quotes if any
set "folder_path=%~1"
set "file_path=%~2"
rem First ensure the dir where we need to store the file exists
for %%f in ("%versions_path%\%file_path%") do set "output_path=%%~dpf"
if not exist "%output_path%" md "%output_path%" || exit /b
rem Finally move the file, overwrite existing file if any
move /y "%folder_path%\%file_path%" "%versions_path%\%file_path%"
```

Finally, I set "C:\Users\mfrnd\Scripts\onlylatest.bat" "%FOLDER_PATH%" "%FILE_PATH%" as the command name in Syncthing.

### Move to the Recycle Bin using PowerShell
We can use PowerShell to send files directly to the Recycle Bin, which mimics the behaviour of deleting them using the Windows Explorer. Firstly, create the following script and save it in your preferred location, e.g. C:\Users\User\Scripts\SendToRecycleBin.ps1.

```
# PowerShell has no native method to recycle files, so we use Visual
# Basic to perform the operation. If succeeded, we also include the
# recycled file in the Syncthing's DEBUG output.
Add-Type -AssemblyName Microsoft.VisualBasic
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($args,'OnlyErrorDialogs','SendToRecycleBin')
if ($?) {

  Write-Output ("Recycled " + $args + ".")
}
```

Alternatively, the script can be expanded to send only deleted files to the Recycle Bin, and permanently delete modified ones, which makes it more consistent with how the Explorer works.

```
# PowerShell has no native method to recycle files, so we use Visual
# Basic to perform the operation.
Add-Type -AssemblyName Microsoft.VisualBasic
# We need to test if a Syncthing .tmp file exists. If it does, we assume
# a modification and delete the existing file. If if does not, we assume
# a deletion and recycle the current file. If succeeded, we also include
# the deleted/recycled file in the Syncthing's DEBUG output.
if (Test-Path -LiteralPath ((Split-Path -Path $args) + "\~syncthing~" + (Split-Path -Path $args -Leaf) + ".tmp")) {

  [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($args,'OnlyErrorDialogs','DeletePermanently')

  if ($?) {

    Write-Output ("Deleted " + $args + ".")

  }
} else {

  [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($args,'OnlyErrorDialogs','SendToRecycleBin')

  if ($?) {

    Write-Output ("Recycled " + $args + ".")

  }
}
```

Finally, we set the command name in Syncthing to powershell.exe -ExecutionPolicy Bypass -File "C:\Users\User\Scripts\SendToRecycleBin.ps1" "%FOLDER_PATH%\%FILE_PATH%".

The only caveat that you should be aware of is that if your Syncthing folder is located on a portable storage, such as a USB stick, or if you have the Recycle Bin disabled, then the script will end up deleting all files permanently.

## CONFIGURATION PARAMETER REFERENCE
The versioning settings are grouped into their own section of each folder in the configuration file. The following shows an example of such a section in the XML:

```
<folder id="...">

    <versioning type="simple">

        <cleanupIntervalS>3600</cleanupIntervalS>

        <fsPath></fsPath>

        <fsType>basic</fsType>

        <param key="cleanoutDays" val="0"></param>

        <param key="keep" val="5"></param>

    </versioning>
</folder>
```

versioning.type

Selects one of the versioning strategies: trashcan, simple, staggered, external or leave empty to disable versioning completely.

versioning.fsPath

Overrides the path where old versions of files are stored and defaults to .stversions if left empty. An absolute or relative path can be specified. The latter is interpreted relative to the shared folder path, if the fsType is configured as basic. Ignored for the external versioning strategy. This option used to be stored under the keys fsPath or versionsPath in the params element.

versioning.fsType

The internal file system implementation used to access this versions folder. Only applies if fsPath is also set non-empty, otherwise the filesystemType from the folder element is used instead. Refer to that option description for possible values. Ignored for the external versioning strategy. This option used to be stored under the key fsType in the params element.

versioning.cleanupIntervalS

The interval, in seconds, for running cleanup in the versions folder. Zero to disable periodic cleaning. Limited to one year (31536000 seconds). Ignored for the external versioning strategy. This option used to be stored under the key cleanInterval in the params element.

versioning.params

Each versioning strategy can have configuration parameters specific to its implementation under this element.

versioning.params.cleanoutDays

The number of days to keep files in the versions folder. Zero means to keep forever. Older elements encountered during cleanup are removed.

versioning.params.keep

The number of old versions to keep, per file.

versioning.params.maxAge

The maximum time to keep a version, in seconds. Zero means to keep forever.

versioning.params.command

External command to execute for storing a file version about to be replaced or deleted. If the path to the application contains spaces, it should be quoted.

## AUTHOR
The Syncthing Authors

## COPYRIGHT
2014-2026, The Syncthing Authors