Skip to content

Latest commit

 

History

History
667 lines (371 loc) · 47.3 KB

File metadata and controls

667 lines (371 loc) · 47.3 KB

Changelog

All notable changes to tailscaled-rs are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Pre-1.0, experimental. This is a from-scratch Rust system daemon — tailnetd (the daemon) plus tnet (a thin CLI) — built on the tailscale-rs engine, adding the layer the embeddable engine omits: an IPN-style state machine, persisted preferences, and a LocalAPI over a Unix domain socket. The engine refuses to run unless TS_RS_EXPERIMENT=this_is_unstable_software is set, and so does this daemon. Interfaces (LocalAPI, prefs schema, CLI flags) are unstable and may change without notice while we are below 1.0. Not affiliated with, endorsed by, or sponsored by Tailscale Inc.; "Tailscale" and "WireGuard" are used nominatively only.

Versioning policy

Releases are driven by Conventional Commits:

  • feat:minor bump.
  • fix:patch bump.
  • chore: / docs: / style: / refactor: / test: → no release on their own.
  • Because the project is pre-1.0, breaking changes may land in a minor bump (and are called out under Changed) rather than forcing a major bump. The major version stays at 0 until the LocalAPI, prefs schema, and CLI are declared stable.

0.52.0 (2026-06-15)

Features

  • tailnetd: emit sd_notify(READY=1) + flip systemd units to Type=notify (#233) (2dd488d)
  • tnet: add switch --list --json (Go tailscale switch --list --json) (#235) (069d9eb)
  • tnet: exit-node suggest + bump engine v0.40.0 → v0.41.0 (ask #24) (#239) (024d85b)
  • tnet: is_ssh_over_tailscale reads /proc session-leader environ under sudo (#238) (cd1f0da)

Bug Fixes

  • tnet: reject file get <name> - clearly instead of writing a file named "-" (#236) (3bd97dc)

0.51.0 (2026-06-15)

Features

  • install: wire systemd ExecStopPost=--cleanup and operator EnvironmentFile (#225) (8a1bf4c)
  • tailnetd: add --port / PORT — consume Config.wireguard_listen_port (#231) (62ea1c9)
  • tnet: add debug local-creds + debug stat (Go tailscale debug parity) (#227) (2e09b7f)
  • tnet: add debug restun (Go tailscale debug restun) — consume Device::re_stun (#230) (b423e3a)
  • tnet: add ssh — host-key-pinned ssh over the tailnet (v0.40.0) (#232) (97e411a)
  • tnet: web UI login affordance — surface the auth URL (#223) (f07fbc3)

0.50.0 (2026-06-15)

Features

  • daemon: add --debug HTTP server exposing GET /debug/metrics (tsd-iqq.9) (#215) (11ec005)
  • localapi: WatchNotifications phase 1 — masked Notify stream (tsd-iqq.11) (#220) (16679dc)
  • localapi: WatchNotifications phase 2 — prefs-change broadcast (#221) (aba1a44)
  • tnet: add up --json (Go tailscale up --json) (#219) (539a99a)
  • tnet: netcheck --format json|json-line + --every (tsd-iqq.13) (#218) (6f31097)

Bug Fixes

  • tnet: ping <own-ip> prints "is local Tailscale IP" + exit 0 (tsd-rqa) (#222) (2843d65)

0.49.0 (2026-06-15)

Features

  • localapi: add check-ip-forwarding + check-prefs verbs (tsd-iqq.8) (#208) (68c48b3)
  • tnet: add ip --assert and whois --json (CLI-flag parity, tsd-iqq.13) (#209) (5ce4f65)
  • tnet: add reload-config LocalAPI verb + running-config adoption (tsd-iqq.7) (#214) (16b1fe5)

Bug Fixes

  • cli: neutralize U+2028/U+2029 + bidi overrides in terminal sanitizers (tsd-ct5) (#212) (a9783c8)
  • funnel: warn on non-loopback backend; correct stale CI runner comment (#211) (1bec62a)

0.48.0 (2026-06-15)

Features

  • daemon: add --cleanup, --no-logs-no-support, and rich --version (#201) (9140508)
  • tnet: add debug rebind (force engine UDP socket rebind) (#203) (f102544)
  • tnet: add debug via for 4via6 route encode/decode (#202) (badfd03)

Bug Fixes

  • conffile: match Go ConfigVAlpha wire keys; drop non-Go AdvertiseTags (#205) (1379087)
  • httpproxy: bound and harden the Tailscale-Connect-Error header (#204) (afb707f)
  • serve: atomic config save + funnel-off clears stale-host key (#207) (8ae6ea5)
  • status: Go-faithful exit-node ID, --watch json/filters, peer IPv6 (#206) (0d3eb90)
  • up: wipe key before --reset mutates in-memory prefs (#199) (497fd19)

0.47.1 (2026-06-14)

Bug Fixes

  • cert: write the private key atomically (temp+fsync+rename), guard the filename (#195) (fa0a045)
  • conffile: validate --config fields before persisting; warn on Locked (#198) (08a72da)
  • localapi: separate cap + timeouts for long-lived Watch/Nc streams (DoS) (#197) (cad049c)

0.47.0 (2026-06-14)

Features

  • daemon: outbound HTTP proxy over the tailnet (--outbound-http-proxy-listen) (#191) (848c1ca)
  • daemon: SOCKS5 proxy that dials over the tailnet (--socks5-server) (#189) (3fa605b)
  • debug: add tnet debug env and tnet debug metrics (Go parity) (#192) (e341b99)

Bug Fixes

  • proxy: bound the SOCKS5/HTTP handshake + dial with a timeout (slowloris) (#193) (c64402e)

0.46.0 (2026-06-14)

Features

  • switch: resolve tnet switch <target> by profile name, not just id (#184) (e9fb0cd)

Bug Fixes

  • funnel: resolve the splice backend from the Web map, not just tcp_forward (#181) (b5881d7)
  • revert-guard: correct ephemeral exemption docs + enforce UpOptions↔guard lockstep (#185) (c82e43a)
  • taildrop: set the quarantine attribute BEFORE copying received bytes (#183) (53f84a3)

0.45.0 (2026-06-13)

Features

  • cli: add tnet syspolicy list / reload (#175) (b390892)

Bug Fixes

  • control: allow plaintext /key bootstrap for http:// control servers (#178) (4fe80c8)

0.44.1 (2026-06-13)

Bug Fixes

  • update: harden the self-replace path (decompression-bomb cap, O_EXCL temp, explicit oversize) (#172) (6442a15)

0.44.0 (2026-06-13)

Features

  • cli: add tnet update (Go tailscale update) — version-check + verified self-install (#169) (b7adb4d)
  • cli: add tnet web (Go tailscale web) — read-only status web UI (#171) (e6132ca)

0.43.0 (2026-06-13)

Features

  • cli: add tnet login + reframe DESIGN scope to full Go parity (no "non-goals") (#165) (7914e8b)
  • cli: bump engine v0.32.0 → v0.33.0 + add tnet lock init (complete the lock surface) (#168) (bf0e58e)

0.42.0 (2026-06-13)

Features

  • cli: add tnet lock sign / tnet lock disable (Tailnet Lock write-ops) (#163) (fd9040a)

0.41.0 (2026-06-13)

Features

  • cli: add tnet cert (Go tailscale cert), consuming engine #16 (#127) (2dbb124)
  • cli: add tnet debug prefs (Go tailscale debug prefs) (#143) (367d0c5)
  • cli: add tnet dns query (Go tailscale dns query) (#158) (4735a8a)
  • cli: add tnet licenses (Go tailscale licenses) (#142) (87f2dfb)
  • cli: add tnet metrics print subcommand (Go tailscale metrics print) (#144) (3be6310)
  • cli: add workload-identity-federation up flags (--client-id/--client-secret/--id-token/--audience) (#154) (3df6101)
  • cli: tnet wait/up --timeout confirms the kernel TUN interface carries the IP (#155) (388df12)
  • cli: wire accept-dns (Go --accept-dns / CorpDNS), consuming engine #14 (#126) (06640b8)
  • file: Go cp colon-target syntax + variadic files + --targets (closes tsd-x4i) (#137) (1807be3)
  • file: Go-faithful file get <dir> inbox drain + --conflict (fixes silent-overwrite data loss) (#136) (77c037e)
  • get: add get --set-flags (Go tailscale get --set-flags) (#149) (6cbaa58)
  • get: surface hostname in tnet get (Go parity) + fix flaky conffile test temp path (#148) (1180b43)
  • install: TUN-relaxed systemd unit for tun-feature builds (tsd-9qm) (#138) (eb8e522)
  • ping: --until-direct + Go-faithful -c default + direct/DERP path reporting (#135) (9c83b56)
  • serve: model Go's top-level ServeConfig.Web map (tsd-6p4 stage A) (#129) (30d7522)
  • serve: serve + render the Go ServeConfig.Web map (tsd-6p4 stage B, read side) (#130) (0f1c7ca)
  • serve: serve TLS-terminated raw-TCP forwards (Go --tls-terminated-tcp) (#128) (6787ad8)
  • serve: tnet serve authors the Go Web map (tsd-6p4 stage B2, write side) (#131) (33ad18a)
  • tailnetd: --config declarative config file (Go ipn.ConfigVAlpha, closes tsd-bin) (#140) (8e72b0c)
  • tailnetd: Go-style CLI flags (--statedir/--socket/--verbose/--version) (#139) (f7c5d3c)

Bug Fixes

  • cli: align netcheck/serve/get diagnostic output with Go v1.100.0 (#122) (f0788e3)
  • cli: byte-match Go's lock-status wording + correct file-command doc miscites (#123) (1a65ea5)
  • cli: neutralize column/row injection from control-supplied names in terminal output (#152) (51b5d30)
  • cli: reset SIGPIPE to default so broken output pipes exit cleanly (not a panic) (#151) (2fb9130)
  • cli: sanitize control-supplied diagnostic output + correct netcheck JSON claims (#124) (19a4273)
  • exit-node: reject auto: selector instead of silently breaking exit routing (#119) (27cd239)
  • file: wire cp --name to the actual transfer (was silently a no-op) (#141) (2b178ba)
  • ipn: gate Stopped on a persisted node key (Go hasNodeKeyLocked) + honest dns-status --json doc (#121) (8556063)
  • ipn: persist has_logged_in on set-rebuild + clear it on logout (review follow-ups) (#161) (c29128d)
  • ipn: revert-guard fresh-node exemption keys on has-logged-in, not prefs-file existence (#156) (14dbde7)
  • release: ship a full-featured daemon (tun,ssh,acme) — released binaries were feature-less (#133) (b09fa10)
  • status: emit RFC3339 timestamps (Go-ipnstate-compatible), not chrono Display (#147) (6476b1b)
  • up: default to a persistent node + add --ephemeral/--no-ephemeral (Go parity, tsd-4qt) (#134) (d46bbee)

0.40.1 (2026-06-12)

Bug Fixes

  • taildrop: O_NOFOLLOW on file_cp/file_get opens + file engine asks #16/#17 (#112) (86e86c8)

0.40.0 (2026-06-12)

Features

  • set: apply hostname/accept-routes/advertise-* live (no reconnect) (#110) (9290e0c)

0.39.0 (2026-06-12)

Features

  • whois: surface flow-scoped cap-grants (WhoIsResponse.CapMap) (#108) (f5d7e11)

0.38.0 (2026-06-12)

Features

  • status: add --web (embedded HTML status server) (#105) (047a025)

0.37.0 (2026-06-12)

Features

  • status: surface Version + TUN (+HaveNodeKey) in status --json (#103) (0e4edb7)

0.36.0 (2026-06-12)

Features

  • risk: enforce lose-ssh on an SSH-server toggle over Tailscale SSH (completes the risk) (#101) (e9db2b8)

0.35.0 (2026-06-12)

Features

  • up: --accept-risk + enforce lose-ssh on force-reauth over Tailscale SSH (#99) (c30c1d8)

0.34.1 (2026-06-12)

Bug Fixes

  • auth: gate metrics as a write (Go PermitWrite), not a read (#97) (2dcd3e3)

0.34.0 (2026-06-12)

Features

  • id-token: tnet id-token <audience> — fetch an OIDC id-token for the node (#95) (2103a6f)

0.33.0 (2026-06-11)

Features

  • version: version.Meta JSON shape (incl. cap) + bugreport [note] + version --upstream stub (#93) (56b2236)

0.32.0 (2026-06-11)

Features

  • up: refuse a control-server change on a running node without --force-reauth (#91) (8035ba4)

0.31.0 (2026-06-11)

Features

  • wait: fail fast on a terminal registration error (#89) (e818397)

0.30.0 (2026-06-11)

Features

  • whois: surface online state and last-seen time (#87) (8a4156d)

0.29.0 (2026-06-11)

Features

  • whois: surface ACL tags and node-key expiry (#85) (a9a1426)

0.28.0 (2026-06-11)

Features

  • up: add --timeout to wait for the node to reach Running (#83) (e3c542f)

0.27.0 (2026-06-11)

Features

  • up: add --force-reauth to force a fresh re-registration (#81) (17e081c)

0.26.0 (2026-06-11)

Features

  • install: tnet install / uninstall — system-daemon bootstrap (#79) (9743f2c)

0.25.0 (2026-06-11)

Features

  • daemon: link-change monitor → Device::rebind() (laptop-grade re-homing) (#77) (ee8f82f)

0.24.0 (2026-06-11)

Features

  • prefs: --shields-up — block inbound peer connections (up + set) (#75) (a616e0b)

0.23.0 (2026-06-11)

Features

  • netcheck: tnet netcheck — DERP-latency net report (#73) (15396ba)

0.22.0 (2026-06-11)

Features

  • dns: tnet dns status — render the control-pushed MagicDNS config (#71) (280ff2b)

0.21.0 (2026-06-11)

Features

  • status: engine bump v0.21.2 + enrich tnet status (relay/direct, last-seen, allowed-routes, active-exit) (#69) (eb6c187)

0.20.0 (2026-06-11)

Features

  • debug: tnet debug capture — packet capture to a pcap file (#67) (e30ee03)

0.19.0 (2026-06-11)

Features

  • funnel: tnet funnel <port> {on|off} via engine listen_funnel (#65) (54803b2)

0.18.0 (2026-06-11)

Features

  • serve: rich web handlers — text, --set-path mounts, redirect (#63) (2f5f86c)

0.17.0 (2026-06-11)

Features

  • serve: HTTPS/HTTP web serve via engine delegation (#61) (7b458e6)

0.16.0 (2026-06-11)

Features

  • serve: ServeConfig model + persistence + tnet serve tcp/status/reset (#58) (63d8aa5)
  • serve: TCP-forward accept loops (serve --tcp now serves traffic) (#60) (0e65d1d)

0.15.0 (2026-06-10)

Features

  • nc: tnet nc <host> <port> — overlay netcat (Go parity) (#56) (3c5bc9e)

0.14.0 (2026-06-10)

Features

  • ping: tnet ping -c <count> (Go parity) (#53) (92cfdd6)

Bug Fixes

  • tags,ping: tighten tag validation to Go CheckTag + pace ping -c (#55) (61d3068)

0.13.0 (2026-06-10)

Features

  • bugreport: tnet bugreport — local diagnostic marker (Go parity, honest) (#52) (7348a16)
  • up,set: advertise-tags pref (Go --advertise-tags parity) (#50) (7b04fe7)

0.12.0 (2026-06-10)

Features

  • cli: tnet metrics, lock status, exit-node list (Go parity) (#48) (56d0ff9)

0.11.0 (2026-06-10)

Features

  • ip: tnet ip -4/-6/-1 and [peer] (Go parity) (#45) (1868ac7)
  • profiles: multi-profile state + tnet switch (Go parity) (#46) (fc5aa7e)
  • status: --active / --no-peers / --no-self filters (Go parity) (#44) (9fc2826)
  • status: tnet status --json (Go ipnstate.Status-shaped subset) (#42) (46447e3)

Bug Fixes

  • profiles: commit in-memory switch only after persisted writes succeed (#47) (12069a4)

0.10.0 (2026-06-10)

Features

  • get: tnet get — show current prefs (Go parity) (#39) (1b64d6b)
  • version: tnet version (+ --daemon, --json) — Go parity (#38) (8f3b711)
  • wait,whoami: tnet wait + whoami (Go parity) (#40) (407993c)

Bug Fixes

  • up,logout: drift-proof the revert guard + crash-safe logout key wipe (#36) (78c50a3)

0.9.0 (2026-06-10)

Features

  • logout: tnet logout — deregister + discard key (Go parity) (#33) (0d5170d)

0.8.0 (2026-06-10)

Features

  • up: Go-faithful REPLACE semantics via accidental-revert guard + --reset (#32) (aa2b3b4)

Bug Fixes

  • localapi: run read/file engine calls off-lock + harden Taildrop paths (#28) (10432e8)
  • nits: consistent clear-flag naming, tun resolver helper, comment cleanup (#31) (63fa6c7)

0.7.0 (2026-06-10)

Features

  • security: use engine Device::new_with_secret + bump to v0.8.0 (tsd-tnv) (#26) (9a8a703)

0.6.0 (2026-06-10)

Features

  • diag: tnet ip / whois / ping diagnostics (tsd-iqq.2 part 1) (#23) (9903696)
  • file: Taildrop send/receive (tsd-qw8) (#24) (016e072)
  • status: surface configured posture in status (tsd-iqq.4 part 1) (#21) (34ca3a5)

0.5.0 (2026-06-09)

Features

  • exit: leak-safety guard + documented invariant (tsd-iqq.3) (#19) (53bbd22)
  • set: tnet set — live pref mutation without up/down (tsd-iqq.1) (#15) (c918cc1)
  • ssh: Tailscale SSH server (tsd-46c) (#17) (3b17972)

Bug Fixes

  • set: preflight rebuilt config before tearing down the live device (#18) (ee1f6b0)

0.4.0 (2026-06-09)

Features

  • routing: exit-node + advertise-exit-node + advertise-routes (tsd-hob, tsd-cmi) (#13) (ceb8ec3)

0.3.1 (2026-06-09)

Miscellaneous Chores

0.3.0 (2026-06-09)

Features

  • status: surface terminal registration failure distinctly (tsd-bml) (#7) (46fe77d)

Bug Fixes

  • gate terminal-failure on is_permanent + harden installer symlinks (#9) (8cef416)

0.2.2 (2026-06-09)

Bug Fixes

  • release: auto-dispatch the binary build when a release is cut (#5) (b3b1cb5)

0.2.1 (2026-06-09)

Bug Fixes

  • release: emit clean v* tags so the binary build fires (#3) (03f0cd9)

0.2.0 (2026-06-09)

Features

  • login: interactive/browser login — surface the control auth URL (tsd-8j2) (862a708)
  • status: 'tnet status --watch' streaming over LocalAPI (tsd-c3p) (c93e492)
  • tun: Phase-3 TUN-mode plumbing (daemon-ready; blocked on one engine export) (9275693)
  • tun: wire kernel-TUN transport on engine v0.6.7 (tsd-tth) (a86fb3b)

Bug Fixes

  • bump engine to v0.6.9 + correct macOS-TUN engine ask #6 (3f9675c)
  • ci: gate the utun-name test to macOS so Linux+tun compiles (eb6fee3)
  • review: bound status() netmap query + drop vestigial param (a85c324)
  • review: off-lock SIGHUP reload, --no-tun, boot-attempt guard, hardening (814ccef)
  • tun: default macOS TUN name to utun (engine default tailscale0 is rejected) (7b4c41d)

Nothing yet.

0.1.0 - 2026-06-08

The initial MVP of the standalone daemon, hardened and reviewed.

Added

  • MVP daemon (tailnetd) + CLI (tnet). A from-scratch, BSD-3-Clause Rust system daemon built on the tailscale-rs engine, supplying the daemon layer the embeddable engine omits: an IPN-style state machine, persisted preferences, and a LocalAPI over a Unix domain socket. In MVP (Phase 1, userspace networking) it joins a tailnet with a pre-auth key, reaches Running, and answers status/up/down. Verified end-to-end against a live tailnet.
  • Pre-auth-key handling that keeps keys out of argv/history. tnet gains --authkey-file and $TS_AUTH_KEY (precedence: file > flag > env).
  • SO_PEERCRED authorization on the LocalAPI socket. Reads are open; writes (up/down) require root or the daemon's owner, and authorization fails closed if the peer-credential lookup errors. Computed per-connection before the stream split.
  • Control-plane URL override. A control_url from prefs/CLI is parsed and applied to the engine config on up(); a malformed URL fails loudly instead of silently falling back to the default control plane.
  • Extended IPN state parity. NeedsMachineAuth / InUseOtherUser added to the state model with honest LIMITATION docs (the engine surfaces no machine-auth signal — documented, not fabricated); derive_state extracted as a pure, unit-tested helper.
  • Secure state directory. ensure_state_dir_secure enforces 0700 on the state directory before any key file is written.
  • Tests. LocalAPI integration tests over a real UDS plus a state-machine matrix; 39 tests pass, clippy -D warnings clean.

Changed

  • Crate renamed to tailscaled-rs (imported as tailscaled_rs); the installed binaries deliberately stay tailnetd + tnet so they never collide on PATH with a real Tailscale install. Added repository metadata. No behavior change.

    Publishing to crates.io remains blocked until the tailscale-rs engine is published there (cargo publish rejects the git dependency).

  • control_url precedence made explicit. The engine config is now built from Config::default_from_env() so TS_CONTROL_URL is honored, with prefs.control_url overriding last (precedence: prefs > env > default). HTTP/HTTPS scheme validation added; a control_url.rs test pins the parse + scheme contract.
  • Authorization model simplified. Collapsed Permissions{read,write} (a dead field) into a 2-variant Access enum; introduced an AuthPolicy built once at startup (the operator-GID seam) instead of a per-call euid lookup; made current_euid private. A pure authorize(&Request, Access) -> Result<(), Denied> was extracted so the security-critical deny path is unit-tested directly.
  • Prefs are forward/backward compatible via a container-level #[serde(default)] on prefs.json.
  • LICENSE made canonically detectable. LICENSE is now the verbatim BSD-3-Clause template (GitHub auto-detect); the upstream-derivation explanation and trademark notices moved to NOTICE, with both copyright holders in the copyright line and Tailscale Inc. attribution retained per clause 1.

Fixed

  • ever_configured survives restart. It now derives from prefs-file existence, so the NoState vs Stopped distinction holds across a restart (previously an up→down→restart wrongly reported NoState; now reports Stopped).
  • Engine status errors are logged, not swallowed. status() errors are logged instead of silently downgrading RunningStarting.
  • Empty pre-auth keys are ignored consistently. An empty TS_AUTH_KEY is filtered in the daemon auto-start path, matching the CLI.

Security

  • Peercred-gated writes (fail-closed). up/down require root or the daemon owner via SO_PEERCRED; a credential-lookup error denies the request.
  • Secrets handled as secrecy::SecretString end-to-end (CLI, daemon, auto-start), exposed only at serialization and the engine call, never logged; a Debug-redaction test pins this.
  • 0700 enforced on the socket's parent directory inside serve() rather than trusting the caller.
  • LocalAPI server hardening: request line length capped at 64 KiB (anti-OOM); in-flight connections drained via a JoinSet with a 2 s bound on shutdown; concurrent connections capped with a Semaphore.