Skip to content

Instantly share code, notes, and snippets.

@gcr
Created January 23, 2025 02:10
Show Gist options
  • Select an option

  • Save gcr/dee9a4b18f52fa2ad949d50b66fea213 to your computer and use it in GitHub Desktop.

Select an option

Save gcr/dee9a4b18f52fa2ad949d50b66fea213 to your computer and use it in GitHub Desktop.
* Programatically setting up SyncThing via the bulit-in CLI command: A worked example
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.
** TL;DR
Install syncthing and run it in the background. Gather each machine's IDs by running:
#+begin_src shell
syncthing cli show system | jq -r .myID
#+end_src
Syncthing will generate device IDs, but it will not generate random folder IDs. Pick a cryptographically secure folder ID by hashing some random bytes:
#+begin_src shell
dd if=/dev/random count=1024 | sha1sum
#+end_src
Then, on each machine, run:
#+begin_src shell
# Introduce this device to the other device
syncthing cli config devices add --device-id $OTHER_DEVICE_ID
# Register the folder (syncthing will create
# it if it does not exist. If it does exist,
# syncthing will add a `.stfolder`
# hidden file to it.)
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
#+end_src
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=:
#+begin_src shell
kimmy@syncOne:~$ sudo apt install -y syncthing tmux jq
#+end_src
Then, pop open a tmux panel and run =syncthing= in the foreground on both machines.
It looks like this:
[[https://pasteboard.co/KdXNgI8CsVv9.png]]
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
#+begin_quote
๐Ÿ‘‰ *Key idea:* Two devices are *introduced* if they appear in each other's device list.
#+end_quote
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:
#+begin_src shell
kimmy@syncOne:~$ syncthing cli show system | jq -r .myID
DAP76ZM-QQCBKBH-FREZULQ-IVWCASB-EJZOJJS-QE2NRRN-YMN6UAI-UHHHIAV
#+end_src
And on =syncTwo=, the ID is:
#+begin_src shell
kimmy@syncTwo:~$ syncthing cli show system | jq -r .myID
TLLN7FX-W3IBVGI-GL22U7H-JNP3KOB-4LUSCDV-FZSWFFL-2RDYFFY-IOEXTAE
#+end_src
Now, *add =syncTwo= to =syncOne='s device list.* On =syncOne=, run
#+begin_src shell
kimmy@syncOne:~$ syncthing cli config devices add --device-id TLLN7FX-W3IBVGI-GL22U7H-JNP3KOB-4LUSCDV-FZSWFFL-2RDYFFY-IOEXTAE --name syncTwo
#+end_src
*Check:* After five seconds, =syncTwo= should show =syncOne='s pending invitation:
#+begin_src shell
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"
}
}
#+end_src
Excellent. Accept this invitation by performing the reverse operation on =syncTwo= (adding =syncOne='s ID to =syncTwo='s device list):
#+begin_src shell
kimmy@syncTwo:~$ syncthing cli config devices add --device-id DAP76ZM-QQCBKBH-FREZULQ-IVWCASB-EJZOJJS-QE2NRRN-YMN6UAI-UHHHIAV --name syncOne
#+end_src
** Make a folder
Let's make a pre-existing folder starting on =syncOne=.
#+begin_src shell
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:~$
#+end_src
We first register this folder on =syncOne=. The two critical parameters are *label* and *path*.
#+begin_src shell
kimmy@syncOne:~$ syncthing cli config folders add --label mydata --path ~/mydata
syncthing: error: unexpected HTTP status returned: 400 Bad Request
folder has empty ID
#+end_src
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.
#+begin_src shell
kimmy@syncOne:~$ dd if=/dev/urandom count=1024 | sha1sum
...
96feab5b3bd18ed3554abf04b1ce64c5ccee3a96 -
kimmy@syncOne:~$ syncthing cli config folders add --label mydata --path ~/mydata --id 96feab5b3bd18ed3554abf04b1ce64c5ccee3a96
#+end_src
After this, there are two changes on =syncOne=. First, syncthing created a =.stfolder= hidden folder inside our folder:
#+begin_src shell
kimmy@syncOne:~$ ls -a mydata/
. .. .stfolder foobar hello-world
#+end_src
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):
#+begin_src shell
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 ...
}
#+end_src
** Share the folder
On =syncOne=, share the folder to =syncTwo=:
#+begin_src shell
kimmy@syncOne:~$ syncthing cli config folders 96feab5b3bd18ed3554abf04b1ce64c5ccee3a96 devices add --device-id TLLN7FX-W3IBVGI-GL22U7H-JNP3KOB-4LUSCDV-FZSWFFL-2RDYFFY-IOEXTAE
#+end_src
*Check:* =syncTwo= should now show the pending folder invitation.
#+begin_src shell
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"
}
}
}
}
#+end_src
** Share the folder back
#+begin_quote
๐Ÿ‘‰ *Key idea:* Two devices are *introduced* if they appear in each other's device list.
#+end_quote
On =syncTwo=, we need to *add* the folder and we also need to *share* the folder back to =syncOne=.
#+begin_src shell
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
#+end_src
That's it! After a short sync period, =syncTwo= can see the folder and can add to it:
#+begin_src shell
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
#+end_src
The sync is two-way, so =syncOne= can also see these changes:
#+begin_src shell
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
#+end_src
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment