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