cd /news/developer-tools/i-reimplemented-the-rustdesk-wire-pr… · home topics developer-tools article
[ARTICLE · art-98939] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

I reimplemented the RustDesk wire protocol in pure Go — here's what it took

A developer has built rdcli, a pure-Go CLI that implements the RustDesk wire protocol, enabling terminal-based remote access without the official GUI. The tool supports file transfer, remote shells, and TCP tunnels, and can import credentials from the official RustDesk app. The project is released under AGPL-3.0.

read3 min views1 publishedAug 16, 2026

RustDesk's public server now requires login, and the only official client is a desktop GUI. If you wanted to script a remote machine, pull a file, or set up a tunnel from your terminal — you were out of luck. So I built rdcli

: a pure-Go CLI that speaks the RustDesk wire protocol directly. No GUI, no official binary, no Electron.

It can list remote directories, copy files both ways, open interactive or one-shot remote shells, and set up TCP tunnels through NAT — all from a terminal, with output designed for both humans and AI agents.

RustDesk's wire protocol is a combination of protobuf messages and raw framed streams, all wrapped in NaCl crypto. Here's the pipeline I had to port:

1. Framing (BytesCodec). Every message is length-prefixed with a variable-length little-endian header: 1–4 bytes, where the bottom 2 bits encode the header length and the remaining bits encode the payload length. Simple, but easy to get subtly wrong.

2. Key exchange (NaCl box). Client and server exchange X25519 public keys, derive a shared secret, and then everything flows through secretbox

with sequential nonces. The server's identity is an Ed25519-signed blob — signature-first layout, like sodiumoxide's sign::sign

. Getting the signature ordering wrong means silent handshake failures.

3. Rendezvous (hbbs). The client sends a PunchHoleRequest

with the peer ID, NAT type, connection type, login token, and licence key. The rendezvous server replies with relay info and the peer's socket address. This is also how online checks work (OnlineRequest

/OnlineResponse

).

4. Hole punching. TCP simultaneous-open: bind the same local address used for the rendezvous connection, try the peer address, time out according to NAT type — asymmetric gets a generous timeout, symmetric fails fast to relay. --relay

forces the hbbr relay path: request a relay with a UUID, connect, re-key, proceed as a secure stream.

5. Login. After the secure stream is up, the client sends a Login

message with flags for the connection type (port-forward, file-transfer, or terminal) plus the password. The peer replies with PeerInfo

or LoginError

. Passwords are hashed as sha256(sha256(pw+salt)+challenge)

.

6. File transfer (fs.proto). FileAction

messages drive everything: ReadDir, ReadAllFiles, send/receive file lists with job IDs, directory creation, deletion. Data flows in 64KB FileTransferBlock

chunks, with digest checks and resume support.

7. Terminal. TerminalAction

messages with an ID, data, resize, close. The server replies with TerminalResponse

output frames. Interactive mode uses a raw-mode PTY with SIGWINCH handling.

Protobuf without the toolchain. I vendored the .proto

files from hbb_common and generated Go bindings. The generated code is committed, so builds are reproducible.

Crypto fidelity. NaCl box/secretbox with sequential nonces — the nonce scheme is where ports usually break. I ported it from crypt.rs

exactly, then wrote round-trip tests against known vectors.

The desktop app's secrets. The nicest feature: rdcli import-gui

reads the official app's stored login and its encrypted 00...

password blobs, decrypts them the same way the GUI does, and stores everything in ~/.config/rdcli/config.toml

with 0600 permissions. Your existing RustDesk setup just works.

The protocol details are ported from the AGPL-licensed RustDesk sources, so the project is AGPL-3.0. That's the honest license for this kind of work.

brew tap 4nkitd/tap
brew install rustdesk-cli

rdcli -c home-pc ls C:/Users/jane
rdcli -c home-pc cp ./backup.zip C:/Users/jane/Desktop/
rdcli -c home-pc sh "ipconfig /all"
rdcli -c home-pc tunnel -L 3389:localhost:3389

Prebuilt binaries for linux/darwin (amd64+arm64) are on the Releases page. If you use RustDesk and ever wanted to script it — this is for you. Issues, PRs, and security reviews welcome.

── more in #developer-tools 4 stories · sorted by recency
── more on @rustdesk 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/i-reimplemented-the-…] indexed:0 read:3min 2026-08-16 ·