tailscale-rs: a Rust rewrite of Tailscale that is honest about what it does not do yet
Rust implementation of Tailscale (preview, experimental)
At a glance
- What is it?
- tailscale-rs is a work-in-progress Rust implementation of Tailscale with bindings for C, Elixir, and Python, where the core networking works and the perimeter features such as MagicDNS, exit nodes, Tailscale SSH, and Serve do not. The project publishes a feature status list, a compatibility caveats section, and a rolling MSRV policy.
- Who is it for?
- tailscale-rs fits a Rust developer who wants to embed a tailnet in their own application, a systems programmer who wants to read a serious Rust networking stack, or a team experimenting with a WireGuard mesh without running the Go client. It is the wrong choice for production workloads, and the wrong choice if you need exit nodes, MagicDNS, or Tailscale SSH today.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The library is a layer below the bindings you probably want
tailscale-rs is a work-in-progress Tailscale library written in Rust, with language bindings to C, Elixir, and Python. The project is at tailscale.com, the crate is published as `tailscale` on crates.io with docs on docs.rs, and the licence is BSD-3-Clause.
The structure matters. What is on crates.io is the Rust API, and the C, Elixir, and Python bindings sit in separate directories, each with its own README, and the getting started instructions point you at them directly. So the question of whether you want this library is a question of which language you are writing in and how much of the Tailscale stack you are replacing.
The Rust dependency line is simple:
[dependencies]
tailscale = { version = "0.6" }And the code sample shows what the API feels like. It opens a `Device` with a `Config` and an auth key, binds a UDP socket on the tailnet IP, and sends packets to a peer:
use std::{
time::Duration,
net::Ipv4Addr,
error::Error,
};
use tailscale::{Config, Device};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Open a new connection to tailscale
let dev = Device::new(
&Config::default_with_key_file("tsrs_keys.json").await?,
Some("YOUR_AUTH_KEY_HERE".to_owned()),
).await?;
// Bind a UDP socket on our tailnet IP, port 1234
let sock = dev.udp_bind((dev.ipv4().await?, 1234).into()).await?;
// Send a packet containing "ping" to 100.64.0.1:5678 once per second
loop {
sock.send_to((Ipv4Addr::new(100, 64, 0, 1), 5678).into(), b"ping").await?;
tokio::time::sleep(Duration::from_secs(1)).await;
}
}The shape to notice is that the API is a `Device` you create and a socket you bind on the tailnet, with an async interface throughout. The key file, `tsrs_keys.json`, holds the node's persistent identity, and the auth key is passed separately, which is the same split the Go client uses between node state and authentication.
The examples directory has four of them, `axum/`, `peer_ping/`, `ssh_peer_lookup/`, and `tcp_echo/`, so the intended uses are an HTTP service on the tailnet, a peer ping, looking up a peer, and a TCP echo server.
What works, what is coming, and what is not going to happen yet
The status section is the most useful part of the project summary, because it is unusually specific about the gaps.
Under Implemented, the list is: create TCP and UDP sockets on the tailnet, communicate with peers via public DERP relays, communicate with peers via direct connections in many cases, and communicate with the Tailscale Go client, `tsnet`, and `libtailscale`. Then language support, with the Rust API and the C, Elixir, and Python bindings.
That last line is the strategically interesting one. The Rust implementation can talk to nodes running the Go client, to `tsnet` embedded instances, and to `libtailscale`. So a mixed fleet is possible, with some nodes on the mature Go implementation and some on the new Rust one, and that is what makes the project worth evaluating before it is complete.
Under Coming Soon, with no guarantees on timeline or completion, there is DNS forwarding, integrations with more HTTP client and server libraries, peer lookups so you can address peers by hostname, and port mapping.
Under Unsupported, the list is long and specific, and it is framed as an incomplete list of features in the Go client, `tsnet`, and `libtailscale` that are not currently supported. The networking group covers peer relays, exit nodes both being one and using one, MagicDNS, private DERP relays, split DNS, and subnet routers. The platform group covers AIX, Android, the BSDs, iOS, Plan9, QNAP, and Synology DSM. The observability group covers client metrics, endpoint collection, device posture collection, log streaming, and network flow logs. And the other features group covers application capabilities, automatic key rotation, HTTPS certificates, Kubernetes, Mullvad VPN, node sharing, Taildrive, Taildrop, Tailnet Lock, Tailscale Funnel, Tailscale Serve, Tailscale SSH, Tailscale Services, and webhooks.
Read that list as the boundary of the project. The mesh works, the services around the mesh do not. A tailnet without MagicDNS, without exit nodes, without subnet routers, and without SSH is a private network between your own nodes, and that is a real product, just a different one from the full Tailscale.
The project invites issue reports on anything missing, and says if something is not on the list and you are not sure about it, please open an issue.
The caveats section says the quiet part out loud
Before the status list there is a caveats section, and it is the most credible thing in the project summary.
The software is still a work-in-progress, and the project says it is providing it in the open at this stage out of a belief in open source and to see where the community runs with it, but warns about three specific things.
There are no compatibility guarantees at the moment. The project may break dependent code in order to get things right, although they will try to avoid doing so gratuitously.
Direct connections via NAT traversal are a work in progress. Communication may fall back to DERP relaying in more cases, with a corresponding hit to latency and throughput.
And core networking functionality is implemented, while many other features are still either in progress or not implemented yet.
That second point is the one that will change your measurements, so it is worth being concrete about. DERP is the relay that carries your traffic when a direct path cannot be established, and it means your packets go to a relay server and back out. A direct connection is a WireGuard path between your two nodes. When the Rust implementation falls back more often, your latency and throughput change, and for a latency-sensitive application that is the difference between the tailnet being useful and being a disappointment.
The project pairs this with the version policy. It follows semver and aims to make a point release roughly monthly, and since it is pre-1.0 it makes no strong backwards-compatibility guarantees. So the honest reading of the versioning is that the 0.x version number is not a promise, and the monthly point releases are a schedule for a moving target.
The invitation at the end of the caveats is the right one for a project in this state. It encourages you to experiment with the library and build things with it, as long as you are okay with these caveats.
A caveat worth stating for anyone evaluating: a DERP relay in the middle of your traffic is a different trust and performance story from a direct WireGuard path, and if the thing you are building depends on the properties of the direct path, you should measure the fallback rate rather than assume it.
The Rust policy is a rolling MSRV, which is unusual and deliberate
The MSRV section is short and it tells you exactly what to expect.
The current MSRV is 1.97 and the current edition is Rust 2024. The project has a rolling MSRV, Minimum Supported Rust Version, policy to support the current and previous Rust compiler versions, and the latest edition of Rust. It may periodically lag the MSRV behind this latest version or edition in rare cases, for the dependencies to catch up and for the project to perform any necessary fixes.
The rolling policy is unusual, and the reason is the one the previous section gives: pre-1.0 with no compatibility guarantees and a monthly point release cadence. A rolling MSRV means your Cargo.toml does not need to be revisited when Rust moves, and it means the library is always built against something recent.
The manifest contains a detail that is worth knowing, and it is the kind of thing that saves a confusing error message. The `rust-version` field in the Cargo.toml disagrees with the README's stated MSRV, and the comment in the file says that is intentional, because the field causes cargo to error if the toolchain is too old, unconditionally.
So there are two numbers, and they are not accidentally different. The manifest's `rust-version` is the hard floor that Cargo enforces, and the README's MSRV is the moving policy statement. The manifest value is the one that will refuse to build your project.
The platform support list is short: Linux on x86_64 and ARM64, macOS on ARM64, and Windows on x86_64. Notably absent are the mobile platforms, and the unsupported list repeats that Android and iOS are not supported. So a mobile application cannot use this library, which is consistent with the Go client situation but worth stating plainly, since Tailscale on a phone is a common use.
The Windows entry is x86_64 only, and macOS is ARM64 only, which for a cross-platform library is a narrowing that will affect anyone with an Intel Mac.
The workspace is a serious networking stack, split by layer
The Cargo workspace lists around forty members, and the directory names describe the architecture of a mesh networking implementation more than they describe a library.
There are separate groups. The bindings are `crates/bindings/ts_ffi`, `ts_elixir`, and `ts_python`. The control plane is `crates/control_plane/ts_control`, with `ts_control_noise` for the encrypted control channel and `ts_control_serde` for its serialisation. The data structures are `ts_array256`, `ts_bart`, `ts_bitset`, and `ts_dynbitset`, and the name BART shows up again in the network group as `ts_bart_packetfilter`, which is the packet filter.
The key-value store is `crates/kv_store/ts_kv_store` and `ts_kv_store_tokio`, and the network overlay is `ts_bart_packetfilter`, `ts_netstack_smoltcp`, `ts_packetfilter`, `ts_packetfilter_serde`, and `ts_packetfilter_state`. The `smoltcp` in that name is the embedded TCP/IP stack, so this crate is what lets the library create real TCP and UDP sockets on the tailnet rather than emulating them.
The net group is `ts_dataplane`, `ts_transport`, and underlay crates `ts_derp`, `ts_netcheck`, and `ts_netmon`. The protocol group is `ts_disco_protocol`, `ts_noise`, and `ts_tunnel`. The capabilities group is `ts_capabilityversion`, `ts_nodecapability`, and `ts_peercapability`, which is the mechanism by which nodes negotiate which features they can use, and it maps directly onto the unsupported features list. The utils group is `ts_cli_util`, `ts_http_util`, `ts_keys`, `ts_packet`, `ts_test_util`, `ts_time`, `ts_tls_util`, and `ts_util`, and there is `crates/ts_runtime` at the top.
So the shape is a layered mesh stack: capabilities negotiated at the top, a control plane over Noise, a data plane with a packet filter and an embedded network stack, a transport layer, and DERP, netcheck, and network monitoring underneath for the underlay.
The supporting tooling is what a project like this needs. There is a `Cross.toml` for cross-compilation, a `rust-toolchain.toml` pinning the toolchain, a `deny.toml` for cargo-deny licence and advisory checking, a `.clippy.toml` and a `.rustfmt.toml` for lints and formatting, a `flake.nix` with a `flake.lock` and a `nix/` directory, a `supply-chain/` directory, `bin/`, `tools/` with a `checks` crate and `ts_devtools`, and `tools/checks` as a workspace member. There is also an `ARCHITECTURE.md`, an `AGENTS.md`, a `CLAUDE.md`, a `PATENTS` file, and a `CHANGELOG.md`.
What the legal notice tells you about the dependency
The legal section is one line, and it is the one line in the entire project summary that is a legal statement rather than a description.
WireGuard is a registered trademark of Jason A. Donenfeld.
That is there because Tailscale is a WireGuard mesh, and the project is being explicit that the trademark belongs to someone else. A reader adopting this library is embedding a WireGuard implementation, and the trademark note is the acknowledgement that the underlying technology carries someone else's brand.
It is also a reminder that the value of this library is not the cryptography. The Noise protocol crate in the workspace, the packet filter, the DERP client, the netcheck, the capability negotiation, the embedded TCP/IP stack, and the control plane are all in the repository, and they are the work. The wire format underneath is a given.
The other legal and governance signals in the tree are worth noting alongside it. There is a `LICENSE` file and a `PATENTS` file, which is a common combination in a project that wants to grant patent rights alongside the copyright grant, and the BSD-3-Clause licence is the permissive base. There is a `deny.toml` for cargo-deny, which is how a Rust project checks its dependencies' licences and advisories automatically, and a `supply-chain/` directory which suggests the project is treating dependency provenance as a first-class concern rather than an afterthought.
For a project that sits in the network path of your application, those two things matter as much as the code. A mesh networking library that pulls in a hundred transitive dependencies is a supply chain question, and the fact that this one has a supply-chain directory and a deny configuration says the maintainers know that.
The last release is v0.6.1 from 2026-09-18, with v0.6.0 the day before, and v0.5.0 in August 2026, and the last push was on 2026-10-15.
Editorial conclusion
tailscale-rs fits a Rust developer who wants to embed a tailnet in their own application, a systems programmer who wants to read a serious Rust networking stack, or a team experimenting with a WireGuard mesh without running the Go client. It is the wrong choice for production workloads, and the wrong choice if you need exit nodes, MagicDNS, or Tailscale SSH today. Check three things before you adopt it: that the features you need are in the Implemented list rather than the Coming Soon or Unsupported ones, since the differences are large and the project is explicit about them, that you can accept a pre-1.0 library with no compatibility guarantees, and that the direct connection path is not the one you depend on, because NAT traversal is a work in progress and traffic falls back to DERP relaying more often than in the Go client.
Frequently asked questions
What is tailscale-rust and what does it currently support?
It is a work-in-progress Rust implementation of Tailscale with bindings for C, Elixir, and Python. The implemented features are creating TCP and UDP sockets on the tailnet, communicating with peers via public DERP relays, communicating via direct connections in many cases, and communicating with the Tailscale Go client, tsnet, and libtailscale. MagicDNS, exit nodes, subnet routers, Tailscale SSH, Serve, Funnel, and Taildrop are not supported.
How do I add tailscale-rust to a Cargo project?
Add `tailscale = { version = "0.6" }` under `[dependencies]` in your Cargo.toml. The API is built around a `Device` created from a `Config` and an auth key, with async methods such as `udp_bind` to bind a socket on the tailnet, and the examples directory contains axum, peer_ping, ssh_peer_lookup, and tcp_echo examples.
What are the compatibility caveats of tailscale-rust?
The project states there are no compatibility guarantees at the moment and that dependent code may be broken to get things right, that direct connections via NAT traversal are a work in progress so communication may fall back to DERP relaying in more cases with a corresponding hit to latency and throughput, and that while core networking is implemented many other features are still in progress or not implemented. The project follows semver with a roughly monthly point release, but since it is pre-1.0 it makes no strong backwards-compatibility guarantees.
Which platforms and Rust versions does tailscale-rust support?
Linux on x86_64 and ARM64, macOS on ARM64, and Windows on x86_64. The current MSRV is 1.97 with edition Rust 2024, and the project runs a rolling MSRV policy supporting the current and previous compiler versions, with the Cargo.toml rust-version field as the hard floor that Cargo enforces.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tailscale-tailscale-rs)Was this useful?