Skip to content

Instantly share code, notes, and snippets.

@gcr
Created January 23, 2025 02:10
Show Gist options
  • Save gcr/dee9a4b18f52fa2ad949d50b66fea213 to your computer and use it in GitHub Desktop.
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:

syncthing cli show system | jq -r .myID

Syncthing will generate device IDs, 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:

# 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

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:

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

👉 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

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 ...
}

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"
      }
    }
  }
}

Share the folder back

👉 Key idea: Two devices are introduced if they appear in each other’s device list.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment