Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Record breaking or significant changes here. All dates are UTC.

Put changes for the upcoming release here!

- Changed (Rust API, lang bindings): the `TS_RS_EXPERIMENT` environment variable is no longer required to use the
library. The library logs a warning during initialization, as a reminder that it's still work-in-progress software.

## [0.5.0](https://github.com/tailscale/tailscale-rs/releases/tag/v0.5.0) - 2026-08-14

- **Breaking** (Rust API, lang bindings, ts_control): Support for `ephemeral`
Expand Down
46 changes: 17 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,8 @@ https://tailscale.com

`tailscale-rs` is a work-in-progress Tailscale library written in Rust, with language bindings to C, Elixir, and Python.

> [!CAUTION]
> This software is unstable and insecure.
>
> We welcome enthusiasm and interest, but please **do not** build production software using these
> libraries or rely on it for data privacy until we have a chance to batten down some hatches
> and complete a third-party audit.
>
> See [Caveats](#caveats) for more details.
> [!NOTE]
> This software is under active development. See [Caveats](#caveats) for more details.

## Getting Started

Expand Down Expand Up @@ -72,32 +66,28 @@ async fn main() -> Result<(), Box<dyn Error>> {
This software is still a work-in-progress! We are 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 &mdash; we may break dependent code
in order to get things right.
- We currently rely on DERP relays for all communication. Direct connections via NAT holepunching will be a seamless
upgrade in the future, but for now, this puts a cap on data 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. We'll remove this
requirement after a third-party code/cryptography audit and any necessary fixes.
- There are no compatibility guarantees at the moment. We may break dependent code in order to get things right,
although we will try to avoid doing so gratuitously.
- Direct connections via NAT traversal is a work in progress. Communication may fall back to DERP relaying in more cases,
with a corresponding hit to latency and throughput.
- Core networking functionality is implemented. Many other features are still either in progress or not implemented yet.
See [Status](#status) for details.

## Versioning, Releases, and Compatability
We encourage you to experiment with this library and build things with it, as long as you're okay with these caveats.

We follow semver and aim to make a point release roughly monthly. Since we are pre-1.0, we make no
backwards-compatability guarantees. We are aiming to have a stable 1.0 release as soon as we can, but we currently don't
have a timeline.
## Versioning, Releases, and Compatibility

We follow semver and aim to make a point release roughly monthly. Since we are pre-1.0, we make no strong
backwards-compatibility guarantees.

## 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).
compiler versions, and the latest [edition of Rust](https://doc.rust-lang.org/edition-guide/editions/index.html).

We may lag the latest version/edition in rare cases for our dependencies to catch up and for us to perform any necessary
We may periodically lag the MSRV behind this latest version/edition in rare cases for our dependencies to catch up and for us to perform any necessary
fixes.

## Platform Support
Expand All @@ -110,9 +100,8 @@ We support the following platforms and architectures:

## Status

`tailscale-rs` is a work-in-progress - we're still rapidly iterating, fixing bugs, and adding new features. We aim to
keep this section up-to-date, but our [issue tracker](https://github.com/tailscale/tailscale-rs/issues)
is the best way to see the latest updates.
`tailscale-rs` is a work-in-progress. We're still rapidly iterating, fixing bugs, and adding new features.
We aim to keep this section up-to-date, but our [issue tracker](https://github.com/tailscale/tailscale-rs/issues) is the best way to see the latest updates.

### Implemented

Expand All @@ -133,7 +122,6 @@ timeline or completion:

- Direct connections (NAT traversal, STUN, and Disco)
- Peer lookups (addressing peers by hostname)
- Third-party code and cryptography audit

### Unsupported

Expand Down
3 changes: 0 additions & 3 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ tailnet, referred to as `$AUTH_KEY` below
examples take care of that for you
- A tailnet policy configured to allow access between your local machine and the example code

Also note the `TS_RS_EXPERIMENT=this_is_unstable_software` environment variable in all the examples
below; for an explanation, see [the Caveats section of the README](../README.md#caveats).

## Overview

Brief descriptions and links to each example.
Expand Down
2 changes: 1 addition & 1 deletion examples/axum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ An `axum`-based HTTP server that serves a simple webpage over the tailnet. This
`tailscale-rs` to be compiled with the `axum` feature:

```sh
$ TS_RS_EXPERIMENT=this_is_unstable_software cargo run --example axum --features axum -- --auth-key $AUTH_KEY --key-file tsrs_keys.json
$ cargo run --example axum --features axum -- --auth-key $AUTH_KEY --key-file tsrs_keys.json
...
INFO axum: http server listening url=http://<tailnet IP>:80/index.html
```
Expand Down
2 changes: 1 addition & 1 deletion examples/peer_ping/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Then, in another terminal, run the example:

```sh
# Terminal 2
$ TS_RS_EXPERIMENT=this_is_unstable_software cargo run --example peer_ping -- --auth-key $AUTH_KEY --key-file tsrs_keys.json --peer <tailnet IP>:5678
$ cargo run --example peer_ping -- --auth-key $AUTH_KEY --key-file tsrs_keys.json --peer <tailnet IP>:5678
...
INFO ts_runtime::multiderp: new home derp region selected region_id=1 latency_ms=12.223305702209473
...
Expand Down
2 changes: 1 addition & 1 deletion examples/tcp_echo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ For this example, you can use netcat (`nc`) to test the server. First, start the

```sh
# Terminal 1
$ TS_RS_EXPERIMENT=this_is_unstable_software cargo run --example tcp_echo -- --auth-key $AUTH_KEY --key-file tsrs_keys.json
$ cargo run --example tcp_echo -- --auth-key $AUTH_KEY --key-file tsrs_keys.json
...
INFO tcp_echo: listening_addr=<tailnet IP>:1234
...
Expand Down
8 changes: 0 additions & 8 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ pub enum Error {
#[error("an error writing out the key file")]
KeyFileWrite,

/// The environment variable `TS_RS_EXPERIMENT` was not set.
///
/// The end-user must set `TS_RS_EXPERIMENT=this_is_unstable_software` to acknowledge that tailscale-rs
/// is early-days experimental software containing bugs, unvalidated cryptography, and no stability
/// or compatibility guarantees.
#[error("the environment variable `{}` was not set", crate::ENV_MAGIC_VAR)]
UnstableEnvVar,

/// An error occurred which can not be anticipated or handled by a library user.
///
/// This is likely due to a bug in our code or a rare and unexpected error.
Expand Down
29 changes: 3 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@
//!
//! ## Using `tailscale`
//!
//! To use this crate or the language bindings, you will need to set the `TS_RS_EXPERIMENT` env var
//! to `this_is_unstable_software`. We'll remove this requirement after a third-party code/cryptography
//! audit and any necessary fixes.
//!
//! Under the hood, we use Tokio for our async runtime. You must also use Tokio, any kind and most
//! configurations of Tokio runtimes should work, but there must be one available when you call any
//! async API functions. The easiest way to do this is to use `#[tokio::main]`, see the
Expand Down Expand Up @@ -189,7 +185,9 @@ impl Device {
/// # Ok(()) }
/// ```
pub async fn new(config: &Config, auth_key: Option<String>) -> Result<Self, Error> {
check_magic_env()?;
tracing::warn!(
"tailscale-rs is early software in active development, expect more bugs and missing features than usual."
);

let keys = (&config.key_state).into();
let rt = ts_runtime::Runtime::spawn(ts_runtime::Config {
Expand Down Expand Up @@ -376,24 +374,3 @@ pub mod keys {
NodeKeyPair, NodePrivateKey, NodePublicKey, NodeState, PersistState,
};
}

const ENV_MAGIC_VAR: &str = "TS_RS_EXPERIMENT";
const ENV_MAGIC_VALUE: &str = "this_is_unstable_software";

fn check_magic_env() -> Result<(), Error> {
if std::env::var(ENV_MAGIC_VAR).as_deref() != Ok(ENV_MAGIC_VALUE) {
let warning = format!(
"
check failed: set {ENV_MAGIC_VAR}={ENV_MAGIC_VALUE} to acknowledge that tailscale-rs is early-days
experimental software containing bugs, unvalidated cryptography, and no stability or compatibility
guarantees.
"
);

eprintln!("{}", warning.trim());

return Err(Error::UnstableEnvVar);
};

Ok(())
}
2 changes: 0 additions & 2 deletions tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,5 @@ async fn test_udp_unidir(tx: &UdpSocket, rx: &UdpSocket) {
}

async fn make_ts_device() -> Result<Device, Error> {
unsafe { std::env::set_var("TS_RS_EXPERIMENT", "this_is_unstable_software") };

Device::new(&Config::default(), Some(ts_test_util::auth_key().unwrap())).await
}
2 changes: 1 addition & 1 deletion ts_python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ if __name__ == "__main__":
To run this demo:

```console
$ TS_RS_EXPERIMENT=this_is_unstable_software python demo.py
$ python demo.py
```

## Building and Usage
Expand Down
3 changes: 0 additions & 3 deletions ts_python/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ For all the examples, you'll need:
- A tailnet policy configured to allow access between your local machine and the example code, and
between the examples themselves

Also note the `TS_RS_EXPERIMENT=this_is_unstable_software` environment variable is required for all
the examples below; for an explanation, see [the Caveats section of the README](../../README.md#caveats).

## [UDP](udp)

A UDP sender and receiver. The sender sends UDP datagrams to the receiver over the tailnet.
4 changes: 2 additions & 2 deletions ts_python/examples/udp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ First, start the receiver:

```sh
# Terminal 1
$ TS_RS_EXPERIMENT=this_is_unstable_software ./recv.py $AUTH_KEY_1 5678
$ ./recv.py $AUTH_KEY_1 5678
...
[<recv IPv4>:5678] udp bound, local endpoint: ('<recv IPv4>', 5678)
...
Expand All @@ -20,7 +20,7 @@ Then, in another terminal, start the sender:

```sh
# Terminal 2
$ TS_RS_EXPERIMENT=this_is_unstable_software ./send.py $AUTH_KEY_2 <recv IPv4> 5678
$ ./send.py $AUTH_KEY_2 <recv IPv4> 5678
...
[<send IPv4>:1234] udp bound, local endpoint: ('<send IPv4>', 1234)
[<send IPv4>:1234-><recv IPv4>:5678|0001] sent message: b'HELLO'
Expand Down
2 changes: 1 addition & 1 deletion ts_python/examples/udp/recv.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async def main(auth_key: str, bind_port: int) -> None:
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="UDP receiver built with `tailscale-py`",
usage="TS_RS_EXPERIMENT=this_is_unstable_software %(prog)s [options]",
usage="%(prog)s [options]",
)
parser.add_argument("auth_key", help="auth key to register with tailnet")
parser.add_argument("bind_port", help="local UDP port to bind", type=int)
Expand Down
2 changes: 1 addition & 1 deletion ts_python/examples/udp/send.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async def main(auth_key: str, peer_ip: str, peer_port: int) -> None:
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="UDP sender built with `tailscale-py`",
usage="TS_RS_EXPERIMENT=this_is_unstable_software %(prog)s [options]",
usage="%(prog)s [options]",
)
parser.add_argument("auth_key", help="auth key to register with tailnet")
parser.add_argument("peer_ip", help="peer's tailnet IP address")
Expand Down
4 changes: 2 additions & 2 deletions ts_tunnel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ tests).

## Security limitations

This crate has not yet been subjected to a code audit by expert cryptography engineers. Conservatively, assume that
there could be a critical security hole that exposes your traffic to attackers.
The code in this crate has been reviewed by third-party security experts. However, it's still relatively young code
that's still being worked on, so depending on your comfort level you may want to conduct your own evaluation.

As stated above, this crate by itself is NOT a complete implementation of WireGuard, and should not be used as one.

Expand Down