Wanted to write some docs for this now that it’s 2025! All of the previous discussion is from several years ago.
This uses syncthing cli
which is included in the package, but the concepts should apply to folks looking to integrate into the REST API.
I have two virtual machines, syncOne
and syncTwo
, and I want to make a shared folder on them. I want to only use the CLI. Maybe these are remote machines, maybe I need to automate this, or maybe I don’t have a web browser handy.
It’s much easier than I expected, but it’s also not clear from the docs. Here’s how to do this.
TL;DR
Install syncthing and run it in the background. Gather each machine’s IDs by running:
syncthing cli show system | jq -r .myID
Syncthing will generate device IDs for you, but it will not generate random folder IDs. Pick a cryptographically secure folder ID by hashing some random bytes:
dd if=/dev/random count=1024 | sha1sum
Then, on each machine, run the same commands to:
-
introduce this machine to the other:
syncthing cli config devices add --device-id $OTHER_DEVICE_ID
-
register the folder (syncthing will create it if it does not exist, and will add a
.stfolder
hidden file if it does exist):syncthing cli config folders add --id $FOLDER_ID --path /path/to/folder
-
share the folder to the other device:
syncthing cli config folders $FOLDER_ID devices add --device-id $OTHER_DEVICE_ID
Each of these three steps must be run on both machines.
Here’s a worked example.
Step zero: Setup
First, we install and run syncthing. On both machines, we install syncthing
, tmux
, and jq
:
kimmy@syncOne:~$ sudo apt install -y syncthing tmux jq
Then, pop open a tmux panel and run syncthing
in the foreground on both machines.
It looks like this:
I have two terminals open, one connected to each machine. Each terminal is running syncthing in the top and has a shell on the bottom.
Step one: Introduce devices to each other
Key idea: Two devices are introduced if they appear in each other’s device list.
There’s no key exchange, you just tell each device the other’s ID. The ID serves as both the username and the password, so keep it secret, keep it safe.
To start, we need to retrieve both device IDs. On syncOne
, we have:
kimmy@syncOne:~$ syncthing cli show system | jq -r .myID
DAP76ZM-QQCBKBH-FREZULQ-IVWCASB-EJZOJJS-QE2NRRN-YMN6UAI-UHHHIAV
And on syncTwo
, the ID is:
kimmy@syncTwo:~$ syncthing cli show system | jq -r .myID
TLLN7FX-W3IBVGI-GL22U7H-JNP3KOB-4LUSCDV-FZSWFFL-2RDYFFY-IOEXTAE
Now, add syncTwo
to syncOne
’s device list. On syncOne
, run
kimmy@syncOne:~$ syncthing cli config devices add --device-id TLLN7FX-W3IBVGI-GL22U7H-JNP3KOB-4LUSCDV-FZSWFFL-2RDYFFY-IOEXTAE --name syncTwo
Check: After five seconds, syncTwo
should show syncOne
’s pending invitation:
kimmy@syncTwo:~$ syncthing cli show pending devices
{
"DAP76ZM-QQCBKBH-FREZULQ-IVWCASB-EJZOJJS-QE2NRRN-YMN6UAI-UHHHIAV": {
"address": "198.19.249.77:22000",
"name": "syncOne",
"time": "2025-01-23T01:24:59Z"
}
}
Excellent. Accept this invitation by performing the reverse operation on syncTwo
(adding syncOne
’s ID to syncTwo
’s device list):
kimmy@syncTwo:~$ syncthing cli config devices add --device-id DAP76ZM-QQCBKBH-FREZULQ-IVWCASB-EJZOJJS-QE2NRRN-YMN6UAI-UHHHIAV --name syncOne
Step two: Make a folder
Let’s make a pre-existing folder starting on syncOne
.
kimmy@syncOne:~$ mkdir mydata
kimmy@syncOne:~$ cd mydata
kimmy@syncOne:~/mydata$ touch hello-world
kimmy@syncOne:~/mydata$ date > foobar
kimmy@syncOne:~/mydata$ ls
foobar hello-world
kimmy@syncOne:~/mydata$ cat foobar
Wed Jan 22 20:32:06 EST 2025
kimmy@syncOne:~/mydata$ cd
kimmy@syncOne:~$
We first register this folder on syncOne
. The two critical parameters are label and path.
kimmy@syncOne:~$ syncthing cli config folders add --label mydata --path ~/mydata
syncthing: error: unexpected HTTP status returned: 400 Bad Request
folder has empty ID
Unfortunately, we need to make up a random folder ID. This must not be guessable, so I’ll use the hash of some random bytes as my ID.
kimmy@syncOne:~$ dd if=/dev/urandom count=1024 | sha1sum
...
96feab5b3bd18ed3554abf04b1ce64c5ccee3a96 -
kimmy@syncOne:~$ syncthing cli config folders add --label mydata --path ~/mydata --id 96feab5b3bd18ed3554abf04b1ce64c5ccee3a96
After this, there are two changes on syncOne
. First, syncthing created a .stfolder
hidden folder inside our folder:
kimmy@syncOne:~$ ls -a mydata/
. .. .stfolder foobar hello-world
Second, syncOne
now lists its own device ID on the folder’s shared device list (in other words, it’s registered to share to itself):
kimmy@syncOne:~$ syncthing cli config folders 96feab5b3bd18ed3554abf04b1ce64c5ccee3a96 dump-json
{
"id": "96feab5b3bd18ed3554abf04b1ce64c5ccee3a96",
"label": "mydata",
"filesystemType": "basic",
"path": "/home/kimmy/mydata",
"type": "sendreceive",
"devices": [
{
"deviceID": "DAP76ZM-QQCBKBH-FREZULQ-IVWCASB-EJZOJJS-QE2NRRN-YMN6UAI-UHHHIAV",
"introducedBy": "",
"encryptionPassword": ""
}
],
... other stuff ...
}
Step three: Share the folder
On syncOne
, share the folder to syncTwo
:
kimmy@syncOne:~$ syncthing cli config folders 96feab5b3bd18ed3554abf04b1ce64c5ccee3a96 devices add --device-id TLLN7FX-W3IBVGI-GL22U7H-JNP3KOB-4LUSCDV-FZSWFFL-2RDYFFY-IOEXTAE
Check: syncTwo
should now show the pending folder invitation.
kimmy@syncTwo:~$ syncthing cli show pending folders
{
"96feab5b3bd18ed3554abf04b1ce64c5ccee3a96": {
"offeredBy": {
"DAP76ZM-QQCBKBH-FREZULQ-IVWCASB-EJZOJJS-QE2NRRN-YMN6UAI-UHHHIAV": {
"label": "mydata",
"receiveEncrypted": false,
"remoteEncrypted": false,
"time": "2025-01-23T01:41:11Z"
}
}
}
}
Step four: Share the folder back
Key idea: Both devices must share the folder to each other.
On syncTwo
, we need to add the folder and we also need to share the folder back to syncOne
.
kimmy@syncTwo:~$ syncthing cli config folders add --id 96feab5b3bd18ed3554abf04b1ce64c5ccee3a96 --label mydata --path ~/mydata
kimmy@syncTwo:~$ syncthing cli config folders 96feab5b3bd18ed3554abf04b1ce64c5ccee3a96 devices add --device-id DAP76ZM-QQCBKBH-FREZULQ-IVWCASB-EJZOJJS-QE2NRRN-YMN6UAI-UHHHIAV
That’s it! After a short sync period, syncTwo
can see the folder and can add to it:
kimmy@syncTwo:~$ ls mydata/
foobar hello-world
kimmy@syncTwo:~$ echo "Hello from syncTwo" > mydata/syncTwo
kimmy@syncTwo:~$ ls mydata/
foobar hello-world syncTwo
kimmy@syncTwo:~$ cat mydata/foobar mydata/syncTwo
Wed Jan 22 20:32:06 EST 2025
Hello from syncTwo
The sync is two-way, so syncOne
can also see these changes:
kimmy@syncOne:~$ ls mydata
foobar hello-world syncTwo
kimmy@syncOne:~$ cat mydata/foobar mydata/syncTwo
Wed Jan 22 20:32:06 EST 2025
Hello from syncTwo
```2