<p align="center">
<img src="docs/assets/banner.svg" alt="tailscale-rs" width="100%">
</p>
<h1 align="center">tailscale-rs</h1>
<p align="center">
<a href="https://github.com/GeiserX/tailscale-rs/actions/workflows/ci.yml"><img src="https://github.com/GeiserX/tailscale-rs/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-BSD--3--Clause-blue.svg" alt="License: BSD-3-Clause"></a>
<img src="https://img.shields.io/badge/MSRV-1.94.1-orange.svg" alt="MSRV 1.94.1">
<img src="https://img.shields.io/badge/edition-2024-blue.svg" alt="Rust edition 2024">
<a href="https://github.com/tailscale/tailscale-rs"><img src="https://img.shields.io/badge/fork%20of-tailscale%2Ftailscale--rs-purple" alt="fork of tailscale/tailscale-rs"></a>
</p>
A fork of [tailscale/tailscale-rs](https://github.com/tailscale/tailscale-rs).
`tailscale-rs` is a work-in-progress Tailscale library written in Rust, with language bindings to
C, Elixir, and Python.
> [!NOTE]
> This project is **not associated with Tailscale Inc.** — it is an independent, unofficial fork.
> See [Legal](#legal).
> [!CAUTION]
> This software is unstable and insecure.
>
> I welcome enthusiasm and interest, but please **do not** build production software using these
> libraries or rely on it for data privacy until I've had a chance to batten down some hatches
> and complete a third-party audit.
>
> See [Caveats](#caveats) for more details.
## Getting Started
The following instructions are for Rust! For other languages, see the language-specific README:
- [C](ts_ffi/README.md)
- [Elixir](ts_elixir/README.md)
- [Python](ts_python/README.md)
Add this dependency line to your `Cargo.toml`:
```toml
[dependencies]
# Published as `geiserx_tailscale`; imported as `tailscale`.
tailscale = { package = "geiserx_tailscale", version = "0.6" }
```
> Or depend on the latest from git:
>
> ```toml
> [dependencies]
> tailscale = { package = "geiserx_tailscale", git = "https://github.com/GeiserX/tailscale-rs" }
> ```
Either way, you import it as `tailscale` (e.g. `use tailscale::Device;`) — the crate name on
crates.io is `geiserx_tailscale`, but the library name is `tailscale`.
Examples of using the `tailscale` crate can be found in [`examples/`](examples/README.md).
For instructions on how to run tests, lints, etc., see [CONTRIBUTING.md](CONTRIBUTING.md). For the high-level architecture and
repository layout, see [ARCHITECTURE.md](ARCHITECTURE.md).
### Code sample
A simple UDP client that periodically sends messages to a tailnet peer at `100.64.0.1:5678`:
```rust
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 this node's 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;
}
}
```
## How it works
A `Device` joins the tailnet by authenticating to the **control plane** over Tailscale's
TS2021 (Noise) channel, then moves data over an in-process **WireGuard** data plane — connecting
peer-to-peer where NAT traversal allows, and relaying through **DERP** otherwise.
```mermaid
flowchart LR
App["Your app<br/>(Rust / C / Elixir / Python)"] --> Device["tailscale::Device"]
Device -->|"TS2021 / Noise"| Control["Control plane<br/>(login, MapResponse)"]
Device --> WG["WireGuard data plane<br/>(userspace netstack)"]
WG -->|"direct (STUN + Disco)"| Peer["Tailnet peer"]
WG -.->|"fallback relay"| DERP["DERP relay"]
DERP -.-> Peer
```
For the full module layout and design notes, see [ARCHITECTURE.md](ARCHITECTURE.md).
## Caveats
This software is still a work-in-progress! I'm 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 please be aware of a few
important considerations:
- This implementation contains unaudited cryptography and hasn't undergone a comprehensive security
analysis. Conservatively, assume there could be a critical security hole meaning anything you send
or receive could be in the clear on the public Internet.
- There are no compatibility guarantees at the moment. This is early-days software — I may
break dependent code in order to get things right.
- Direct peer-to-peer connections via NAT traversal are implemented (STUN-discovered endpoints and
Disco, with `CallMeMaybe` hole-punching over DERP), and traffic falls back to DERP relays when no
direct path is available. Symmetric-NAT birthday-paradox hole-punching is not yet implemented,
so behind some NATs a flow stays relayed through DERP, which caps its throughput.
- The `TS_RS_EXPERIMENT` environment variable is required to be set to `this_is_unstable_software`
for all code linked against `tailscale-rs`; this includes Rust, C, Elixir, and Python code. I'll
remove this requirement after a third-party code/cryptography audit and any necessary fixes.
For the full security posture — unaudited cryptography, the Tailnet Lock enforcement gap, peerAPI
capability limitations, at-rest key handling, and how to report a vulnerability — see
[SECURITY.md](SECURITY.md).
## Versioning, Releases, and Compatability
I follow semver and aim to make a point release roughly monthly. Since this is pre-1.0, there are no
backwards-compatability guarantees. I'm aiming for a stable 1.0 release as soon as I can, but there's
currently no timeline.
## MSRV and Edition
The current MSRV is 1.94.1. The current edition is Rust 2024.
`tailscale-rs` has a rolling MSRV (Minimum Supported Rust Version) policy to support the current
and previous Rust compiler versions, and the latest
[edition of Rust](https://doc.rust-lang.org/edition-guide/editions/index.html).
I may lag the latest version/edition in rare cases to let dependencies catch up and to
perform any necessary fixes.
## Platform Support
The following platforms and architectures are supported:
- Linux (`x86_64`/`ARM64`)
- macOS (`ARM64`)
- Windows (`x86_64`)
## Status
`tailscale-rs` is a work-in-progress - I'm still rapidly iterating, fixing bugs, and adding new
features. I aim to keep this section up-to-date, but the [issue tracker](https://github.com/GeiserX/tailscale-rs/issues)
is the best way to see the latest updates.
### Implemented
These are features that are currently implemented:
- Basics
- Create TCP and UDP sockets on the tailnet
- Direct connections via NAT traversal (STUN-discovered endpoints and Disco, with `CallMeMaybe`
hole-punching over DERP); traffic falls back to public DERP relays only when no direct path is
available
- Peer lookups (addressing peers by MagicDNS name, in-process)
- MagicDNS (an in-netstack resolver on `100.100.100.100:53` answering A/AAAA/PTR for tailnet
peers and control-pushed static records (`ExtraRecords`); by default fail-closed — with no
`fallback_resolvers` configured, non-tailnet names get NXDOMAIN and are never forwarded
upstream. Configuring `fallback_resolvers` opts in to forwarding non-tailnet query names to
those upstream resolvers.)
- Split DNS (per-domain `routes` from control: a query matching a route's suffix is forwarded to
that route's resolvers by longest-suffix match; a route with an empty resolver list is a
negative route answering NXDOMAIN. Recursive forwarding to `fallback_resolvers`/`resolvers`
applies only when no route matches.)
- Using a subnet router (accepting peer-advertised subnet routes via `accept_routes`; opt-in,
fail-closed off by default)
- Using an exit node (routing internet-bound traffic through a chosen peer via `exit_node`,
selectable by Tailscale stable ID, tailnet IP, or MagicDNS name; opt-in, fail-closed off by
default). The exit node can also be changed at runtime with `Device::set_exit_node` — the
equivalent of Go `tsnet`'s `LocalClient.EditPrefs(ExitNodeID/ExitNodeIP)` — without recreating
the device.
- Recursive MagicDNS forwarding in TUN mode (the `100.100.100.100:53` resolver now forwards
non-tailnet names recursively in TUN mode, matching the netstack-mode behavior; the same
fail-closed default applies — without `fallback_resolvers`, non-tailnet names get NXDOMAIN)
- Tailscale Serve: `Proxy`, `Text`, `TcpForward`, plus HTTP `Path` (path-prefix mux) and
`Redirect` (HTTP 3xx) handlers; all TLS-terminating targets are validated and dispatched
fail-closed (unmatched path → 404, backend dial failure → drop)
- Tailnet Lock (TKA), **partial**: per-peer node-key signature verification is wired and
unit-tested at the peer-trust chokepoint and fails closed when a trusted-key `Authority` is
supplied. Live enforcement is **currently inert** — the AUM-chain sync RPC that would supply the
`Authority` is not yet implemented, so a compromised control plane can still inject peer keys.
See [SECURITY.md](SECURITY.md) before relying on it.
- Communicate with the Tailscale Go client, `tsnet`, and `libtailscale`
- Language support
- Rust API
- C, Elixir, and Python bindings
### Coming Soon
These are features or efforts I have in the pipeline and am actively working towards, but with
no guarantees on timeline or completion:
- Third-party code and cryptography audit
### Unsupported
This is an incomplete list of features in the Tailscale Go client, `tsnet`, and/or `libtailscale`
that are *not* currently supported. I'd like to add all of these eventually! If there's something
on this list you'd like to see supported, or something _not_ on this list you're not sure about,
please open an issue!
<details>
<summary>
Unsupported features
</summary>
- Networking
- Peer relays
- Exit Nodes (being one — advertising a default route; *using* one is supported)
- Exit node DNS (`ExitNodeDNSResolvers` — routing DNS through the exit node)
- Private DERP relays
- Subnet Routers (being one — advertising routes; *using* one is supported)
- Platforms
- AIX
- Android
- BSDs
- iOS
- Plan9
- QNAP
- Synology DSM
- Observability
- Client Metrics
- Endpoint Collection
- Device Posture Collection
- Log Streaming
- Network Flow Logs
- Other Features
- Application Capabilities
- Automatic Key Rotation
- HTTPS Certificates
- Kubernetes
- Mullvad VPN
- Node Sharing
- Taildrive
- Taildrop
- Tailnet Lock — full enforcement (signature verification is wired, but the AUM-sync RPC that
supplies the trusted-key `Authority` is not yet built, so enforcement is inert; see
[SECURITY.md](SECURITY.md))
- Tailscale Funnel
- Tailscale Serve — the stored serve-config runtime and accept-loop (the `Path`/`Redirect`/`Proxy`/
`Text`/`TcpForward` handlers themselves are implemented)
- Tailscale SSH
- Tailscale Services
- Webhooks
- Any other features not listed in "Implemented" or "Coming Soon"
</details>
## Legal
**This project is not associated with Tailscale Inc.** It is an independent, unofficial fork.
"Tailscale" is a trademark of Tailscale Inc.; it is used here only to describe interoperability and
the upstream project this is forked from.
WireGuard is a registered trademark of Jason A. Donenfeld.