Skip to content

tailscale-rs

tailscale-rs: Embed a Tailscale node in your Rust app

crates.io crates.io downloads GitHub Stars License: BSD-3-Clause


tailscale-rs is a work-in-progress Tailscale library written in Rust. Your program joins a tailnet as its own node, from inside its own process, and opens TCP and UDP sockets to the other nodes: direct peer to peer where NAT traversal allows, through DERP relays where it does not. Go programs get this from Go's tsnet; tailscale-rs brings it to Rust, with an optional tsnet::Server facade shaped like Go's, and to C, Elixir and Python through bindings. It talks to the Go client, tsnet and libtailscale. Start with Getting started, and read Caveats before you depend on it.

Unstable and unaudited

The cryptography has not been audited and the API has no compatibility guarantees yet. Do not build production software on it or rely on it for data privacy. Every program linked against it must set TS_RS_EXPERIMENT=this_is_unstable_software. See Caveats.

  • Getting started


    Add the crate, published as geiserx_tailscale and imported as tailscale, and set TS_RS_EXPERIMENT.

  • A first program


    A UDP client that joins the tailnet with an auth key and sends a packet to a peer every second.

  • Coming from Go tsnet


    The tsnet feature's Server: settable fields, lazy start, listen, dial, and a Go-to-Rust mapping table.

  • API reference


    Every type and method of the tailscale crate on docs.rs, for the published version.

What it looks like

A node that joins the tailnet as web and accepts TCP connections on port 80, with the tsnet feature, next to the Go it replaces:

use tailscale::tsnet::Server;

let mut srv = Server::new();
srv.hostname = Some("web".into());
srv.auth_key = Some("YOUR_AUTH_KEY_HERE".into());
srv.dir = Some("web_state".into());

// The node starts on the first method call.
let listener = srv.listen("tcp", ":80").await?;
srv := &tsnet.Server{Hostname: "web", AuthKey: key, Dir: "web_state"}
defer srv.Close()
ln, _ := srv.Listen("tcp", ":80")

The full program, the dependency lines and the plain Device API without the facade are on Getting started. The examples/ directory has runnable programs: an axum web page, a UDP ping, a TCP echo server, the same server on the facade, and an SSH-served peer lookup.

What works today

  • TCP and UDP sockets on the tailnet from an in-process WireGuard data plane.
  • Direct connections through NAT traversal (STUN, Disco, CallMeMaybe), falling back to DERP relays.
  • MagicDNS and split DNS, fail-closed by default.
  • Using subnet routers and exit nodes (opt-in), with the exit node changeable at runtime.
  • Tailscale Serve handlers, and Tailnet Lock peer signature enforcement (partial).

The full list, with what is coming and what is not supported, is on Status. How a node reaches the control plane and its peers is on How it works.

What it does not do yet

  • Act as an exit node or a subnet router for other nodes. Using one works.
  • Peer relays, private DERP relays, Taildrop, Taildrive and automatic key rotation.
  • Hole-punch symmetric NATs; behind some NATs a flow stays relayed through DERP, which caps its throughput.
  • Run on Android, iOS or the BSDs. It runs on Linux (x86_64, ARM64), macOS (ARM64) and Windows (x86_64), with Rust 1.94.1 or newer. See Caveats.

Other languages

The C, Elixir and Python bindings live in this repository and build from it: C, Elixir, Python. Each README has a code sample. The same TS_RS_EXPERIMENT rule applies.

Getting help

License

tailscale-rs is released under the BSD-3-Clause license. It is a fork of tailscale/tailscale-rs and is not associated with Tailscale Inc. "Tailscale" is a trademark of Tailscale Inc., used here only to describe interoperability and the upstream project. WireGuard is a registered trademark of Jason A. Donenfeld.