- P2P NAT Traversal: Automatically attempts to punch holes through NATs using WebRTC ICE DataChannels. Seamlessly falls back to Syncthing's global public relay network if a direct connection is impossible.
- End-to-End Encryption: Automatically establishes a secure end-to-end tunnel using TLS 1.3.
- Cryptographic Device Verification: Identifies and authenticates endpoints by hashing their TLS certificates to generate unique Syncthing Device IDs, preventing man-in-the-middle attacks.
- Global Discovery: Server registers itself dynamically in the Syncthing global announce directory (
discovery.syncthing.net), allowing clients to look up and connect to the server using only its Device ID. - Interactive Shell: Built-in pseudo-terminal (PTY) shell mode (
-shell) natively replaces SSH, perfectly forwarding terminal sizes for interactive tools likevimorhtop. - Remote Command Execution: Execute an arbitrary command for each connection and pipe its input/output over the tunnel (
--command). - SOCKS5 Proxy: Built-in multiplexed remote SOCKS proxy (
-socks). - Netcat-style: Pipes standard input (
stdin) and standard output (stdout) bi-directionally between endpoints.
You can build the binary using:
go build -o syncthing-socket main.goStart the server listening on the Syncthing relay network. By default, it will dynamically choose a low-latency public relay from the pool, print the connection string, and announce itself to the Syncthing Global Discovery servers:
./syncthing-socket serverExample Output:
==================================================
Server Device ID: YGOV2KW-QIGHJYZ-BCA52RN-7EQTGY3-CQGARMC-3JVUWAA-BXHSKI5-ZRJSBQT
==================================================
Connected to Relay: relay://195.22.153.71:22067/?id=SPXEVID...
To connect, run:
./syncthing-socket client --relay "relay://195.22.153.71:22067/?id=SPXEVID..." YGOV2KW-QIGHJYZ-BCA52RN-7EQTGY3-CQGARMC-3JVUWAA-BXHSKI5-ZRJSBQT
Or simply:
./syncthing-socket client YGOV2KW-QIGHJYZ-BCA52RN-7EQTGY3-CQGARMC-3JVUWAA-BXHSKI5-ZRJSBQT@relay://195.22.153.71:22067/?id=SPXEVID...
==================================================
2026/07/11 13:16:33 Announcing availability to https://discovery-announce-v4.syncthing.net/v2/?nolookup...
2026/07/11 13:16:33 Successfully announced to https://discovery-announce-v4.syncthing.net/v2/?nolookup
--shell: Start a fully interactive PTY shell daemon (replaces SSH).--command "<cmd>": Spawn an arbitrary command (e.g.bash -c "<cmd>") for each incoming connection and pipe its standard input and output over the tunnel.--socks: Start a remote SOCKS5 proxy server.--passphrase <secret>: Deterministically generates a secure TLS certificate and Device ID from a secret password, so you don't need to copy-paste long Device IDs.--cert <path>: Custom TLS certificate path (default: empty, runs in-memory).--key <path>: Custom TLS key path (default: empty, runs in-memory).--relay <uri>: Specific relay URI, or a dynamic pool URL (default:dynamic+https://relays.syncthing.net/endpoint).--discovery <urls>: Comma-separated list of global announce directories.--direct-port <port>: Enable direct TCP connection listening on this port (0 to disable, default: 0).--authorized-clients <ids>: Comma-separated list of authorized client Syncthing Device IDs. When specified, only clients with matching Device IDs are allowed to connect; unauthorized clients are rejected post-handshake.--totp: Require Time-based One-Time Password (TOTP, RFC 6238) authentication for incoming connections. If--totp-secretis omitted, a secure base32 secret is automatically generated and an ASCII QR code is printed to stderr.--totp-secret <secret>: Base32-encoded TOTP secret key for authentication. Explicitly setting this flag automatically enables TOTP requirement.--log-level <level>: Logging level: trace, debug, info, warn, error (default: info).--log-format <format>: Logging format: auto, text, json, journald (default: auto).
You can connect to the server in three ways:
If you only know the Server's Device ID, the client will automatically query the global directory, resolve which relay the server is currently on, and connect to it:
./syncthing-socket client <SERVER_DEVICE_ID>Connect directly using the connection string printed by the server:
./syncthing-socket client <SERVER_DEVICE_ID>@<RELAY_URI>Specify the relay address manually via the --relay flag:
./syncthing-socket client --relay "<RELAY_URI>" <SERVER_DEVICE_ID>--shell: Put the local terminal in raw mode and connect to a remote--shellserver.--socks <address>: Spin up a local SOCKS5 proxy on this address (e.g.127.0.0.1:10800).--passphrase <secret>: Use the same passphrase as the server to auto-discover and connect without needing the Server Device ID!--cert <path>/--key <path>: Use a persistent certificate (default: generates a secure in-memory certificate).--discovery <url>: Custom discovery server URL for lookups (default:https://discovery-lookup.syncthing.net/v2/).--direct: Attempt direct P2P connections via WebRTC ICE (UDP NAT Hole Punching) and direct TCP before seamlessly falling back to relay. Set--direct=falseto disable ICE and force relay connections (default: true).--totp <code>: 6-digit TOTP passcode for server authentication. If omitted when connecting to a TOTP-enabled server, the client will interactively prompt for the code on standard input.--log-level <level>: Logging level: trace, debug, info, warn, error (default: info).--log-format <format>: Logging format: auto, text, json, journald (default: auto).
You can completely bypass the need for an external OpenSSH server by using the built-in PTY shell!
- On the Server:
./syncthing-socket server --passphrase "my-secret" --shell- On the Client:
./syncthing-socket client --passphrase "my-secret" --shellThis spawns a remote bash session and pipes your raw local terminal directly into it. It natively supports tab-completion, vim, htop, and even transmits window resizing events (SIGWINCH) over a dedicated lightweight control stream so the remote UI always perfectly fits your screen.
If you want to just trigger a script, pipe data into a command, or build a simple authenticated remote execution API, you can use the --command flag.
On the Server:
./syncthing-socket server --passphrase "my-secret" --command "echo 'Processing data...'; cat - | grep error; echo 'Done!'"On the Client:
echo "log data with error" | ./syncthing-socket client --passphrase "my-secret"Note: By default, standard error (stderr) generated by the command is cleanly printed to the server's structured logs so it does not corrupt the data stream. If you wish to send stderr back to the client along with standard output, use standard shell redirection at the end of your command string: --command "your command 2>&1".
If you are using the --passphrase flag instead of explicit TLS certificates, syncthing-socket mathematically derives unique cryptographic keys from your secret phrase. To prevent reflection attacks (where a malicious actor routes your client back to itself), the client and server intentionally use different cryptographic suffixes during derivation.
If you are curious what your Syncthing Device IDs will be for a given passphrase, you can use the id subcommand:
$ ./syncthing-socket id --passphrase "my-secret"
Server ID: DJOJMPR-MCPBCMH-GVVOJEX-HXWI7JU-L7OSTDV-TBD4ZJP-YSOFXQZ-PPFV5QD
Client ID: F34H6S5-62G6AIP-2G3VHTB-I5D35S6-6T4K5E6-RFL52E4-37C5T3S-7FLHFAQYou can front your local SSH daemon (or any other TCP service) using syncthing-socket. This allows you to securely SSH into a machine behind a NAT.
Start the server with the --forward option pointing to your SSH port (usually 127.0.0.1:22). You should also specify --cert and --key so that your server keeps a persistent Device ID:
./syncthing-socket server --cert cert.pem --key key.pem --forward 127.0.0.1:22The server will print its Device ID (e.g. SERVER_DEVICE_ID). It runs as a persistent daemon and forwards incoming connections to the local SSH port in separate goroutines, handling multiple concurrent sessions.
You can forward traffic to TCP addresses or local UNIX domain sockets (e.g. /var/run/docker.sock or unix:///tmp/app.sock). If you are forwarding to a reverse proxy (like Nginx, Traefik, or HAProxy), you can enable the --proxy-protocol flag:
./syncthing-socket server --forward /var/run/docker.sock --proxy-protocolThis injects an HAProxy PROXY Protocol V2 header at the start of the forwarded stream, allowing compatible backend software to read the Client's original address. Furthermore, syncthing-socket embeds the client's cryptographically authenticated Syncthing Device ID into a custom TLV field (Type 0xEA), enabling native peer authentication at the reverse proxy layer!
On the client machine, connect using standard SSH with the -o ProxyCommand flag:
ssh -o ProxyCommand="./syncthing-socket client <SERVER_DEVICE_ID>" user@ignored_hostAlternatively, add an entry to your local ~/.ssh/config file:
Host my-nat-server
User your_username
ProxyCommand /path/to/syncthing-socket client <SERVER_DEVICE_ID>
Then connect simply by running:
ssh my-nat-serverYou can expose a local service (e.g., a development web server) to the internet by routing traffic backward through a syncthing-socket server running on a public VPS. This acts exactly like SSH -R. You can use TCP addresses or UNIX domain sockets on either side!
1. On the Server (Public VPS):
# Listen on TCP port 8080 or a UNIX socket
./syncthing-socket server --passphrase my-secret --reverse-forward 0.0.0.0:8080
# Or: ./syncthing-socket server --passphrase my-secret --reverse-forward /tmp/remote.sock2. On the Client (Local Machine):
# Forward incoming reverse tunnel traffic to local port 8000 or a UNIX socket
./syncthing-socket client --passphrase my-secret --reverse-forward 127.0.0.1:8000
# Or: ./syncthing-socket client --passphrase my-secret --reverse-forward /var/run/docker.sockNow, anyone accessing http://<VPS-IP>:8080 (or connecting to the remote UNIX socket) will securely hit your local service. Under the hood, this uses yamux multiplexing over the P2P tunnel to handle multiple concurrent requests seamlessly!
Both the server and client natively respect standard proxy environment variables to help bypass restrictive firewalls.
Supported Variables:
SOCKS_PROXY(orsocks_proxy): e.g.socks5://127.0.0.1:1080HTTP_PROXY(orhttp_proxy): e.g.http://proxy.corp.com:8080HTTPS_PROXY(orhttps_proxy)
How it works:
- Discovery (HTTPS): Uses standard Go networking logic to route queries to
discovery.syncthing.netvia your specifiedHTTP_PROXY/HTTPS_PROXY. - Syncthing Relay Network (Raw TCP): Transparently dials out to the Syncthing Relay endpoints (
relay://...) by tunneling the raw TCP traffic through either your specifiedSOCKS_PROXYor via anHTTP CONNECTtunnel if anHTTP_PROXYis provided. - Direct Connections (WebRTC ICE): STUN/TURN discovery queries are handled by Pion and may also route over supported proxies if configured in the environment. Direct P2P TCP hole punching attempts to establish a connection directly between peers (bypassing proxies if local).
Note: If both SOCKS and HTTP proxies are provided in the environment, SOCKS takes precedence for raw TCP routing to the Relay network.
syncthing-socket natively supports generating auto-completions for Bash, Zsh, Fish, and PowerShell.
To load completions in your current shell session (e.g., Bash):
source <(syncthing-socket completion bash)To permanently install them, read the help instructions for your specific shell:
syncthing-socket completion bash --help
syncthing-socket completion fish --help- Relay Blindness: The relay server acts purely as a TCP proxy. It cannot decrypt or read any data sent through it.
- E2E TLS 1.3: Once the relay session is joined by both client and server, they negotiate a direct TLS 1.3 handshake.
- Peer Verification & Authentication:
- The Client extracts the leaf certificate from the TLS handshake, hashes it to generate the server's Device ID, and compares it against the expected
<SERVER_DEVICE_ID>. The connection is terminated immediately if they do not match. - The Server requests the client's certificate to verify and log the client's Device ID. If
--authorized-clientsis specified, the server validates the client Device ID post-handshake and immediately closes unauthorized connections. - If
--totpis enabled on the server, an orthogonal challenge-response protocol requires the client to supply a valid Time-based One-Time Password (RFC 6238) before any application stream or tunnel is opened.
- The Client extracts the leaf certificate from the TLS handshake, hashes it to generate the server's Device ID, and compares it against the expected