[workspace]
resolver = "3"
members = [
    "checks",
    "ts_array256",
    "ts_bart",
    "ts_bart_packetfilter",
    "ts_bitset",
    "ts_capabilityversion",
    "ts_cli_util",
    "ts_control",
    "ts_control_noise",
    "ts_control_serde",
    "ts_dataplane",
    "ts_derp",
    "ts_devtools",
    "ts_disco_protocol",
    "ts_dns_wire",
    "ts_dynbitset",
    "ts_elixir/native/ts_elixir",
    "ts_ffi",
    "ts_forwarder",
    "ts_hexdump",
    "ts_host_net",
    "ts_http_util",
    "ts_keys",
    "ts_magicsock",
    "ts_metrics",
    "ts_netcheck",
    "ts_netstack_smoltcp",
    "ts_netstack_smoltcp_core",
    "ts_netstack_smoltcp_socket",
    "ts_nodecapability",
    "ts_overlay_router",
    "ts_packet",
    "ts_packetfilter",
    "ts_packetfilter_serde",
    "ts_packetfilter_state",
    "ts_peercapability",
    "ts_python",
    "ts_runtime",
    "ts_test_util",
    "ts_time",
    "ts_tka",
    "ts_tls_util",
    "ts_transport",
    "ts_transport_tun",
    "ts_underlay_router",
    "ts_tunnel",
]

[workspace.package]
edition = "2024"
license = "BSD-3-Clause"
publish = true
version = "0.34.1"
repository = "https://github.com/GeiserX/tailscale-rs"

# This disagrees with the README's stated MSRV. That's intentional: this field causes cargo to error
# if the toolchain is too old, unconditionally failing the compile, while our policy is just that we
# only actively support the last two stables -- older compilers should still work as long as they
# can still compile the project. This field is the de facto MSRV (the oldest compiler that works).
rust-version = "1.94.1"

[workspace.dependencies]
aead = { version = "0.5", features = ["std"] }
# Argon2i KDF for TKA disablement values (Go tka.DisablementKDF = argon2.Key, Argon2i). Stable 0.5
# pulls blake2 ^0.10.6 (matches the workspace blake2 — no second blake2) and is pure-Rust (base64ct/
# cpufeatures + password-hash, NO aws-lc/openssl/ring) so it is ring-only-clean. Used by ts_tka
# (consensus-critical), default-features off (drops rand/std; `alloc` still pulls password-hash/alloc
# transitively — pure-Rust, ring-clean. The code calls the low-level hash_password_into, not the
# PasswordHasher trait).
argon2 = { version = "0.5", default-features = false, features = ["alloc"] }
base64 = "0.22"
blake2 = "0.10"
bytes = "1"
cfg-if = "1.0"
chacha20poly1305 = "0.10"
chrono = { version = "0.4", default-features = false }
clap = { version = "4", features = ["derive"] }
crypto_box = "0.9"
divan = "0.1"
flume = "0.12"
futures = { version = "0.3", default-features = false }
futures-util = { version = "0.3", default-features = false }
gethostname = "1.1"
hashbrown = "0.17"
heapless = "0.9"
hex = "0.4"
hkdf = { version = "0.12", features = ["std"] }
http = "1.4"
httparse = "1"
http-body-util = "0.1"
hyper = { version = "1", default-features = false, features = ["http2"] }
hyper-util = { version = "0.1", default-features = false, features = ["http1"] }
ipnet = { version = "2.12", default-features = false }
lazy_static = "1.5"
pin-project-lite = "0.2"
proptest = "1"
itertools = "0.14"
rand = "0.10"
reqwest = "0.13"
# Pure-Rust secret wrapper (zeroize-on-drop, redacted Debug). Used by the facade's
# `Device::new_with_secret` so callers can pass the registration auth key as `SecretString`
# without materializing a plain `String` at the engine boundary. Ring-clean: pulls only `zeroize`
# (already pervasive in this tree), NO aws-lc/openssl/ring — safe for the ring-only invariant.
secrecy = "0.10"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_with = "3"
smallvec = "1.15"
static_assertions = "1.1"
smoltcp = { version = "0.13", default-features = false }
thiserror = { version = "2", default-features = false }
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros"] }
tokio-util = { version = "0.7", default-features = false }
# The standalone `Service` trait crate (NOT full `tower`) — used only by the facade's optional
# `hyper` feature so the tailnet HTTP connector can implement `Service<Uri>` (hyper-util's sealed
# `Connect` is granted via its blanket impl over `Service<Uri>`). Pure-Rust, ring-clean.
tower-service = "0.3"
tracing = { version = "0.1", default-features = false, features = ["attributes"] }
tracing-test = "0.2"
url = { version = "2", default-features = false }
x25519-dalek = { version = "3.0.0-rc.0", features = ["getrandom", "reusable_secrets", "static_secrets"] }
# Project Wycheproof adversarial primitive test vectors (dev-only). Ring-clean: pulls only
# serde / serde_json / data-encoding, NO aws-lc/openssl/ring — safe for the ring-only invariant.
wycheproof = "0.6"
yoke = { version = "0.8", default-features = false }
zerocopy = { version = "0.8", features = ["derive"] }
# zeroize wipes secret byte buffers on drop. ts_keys / ts_control_noise pin "1.8" directly; this
# workspace entry lets ts_tunnel (WireGuard symmetric key material) share the same pin. Ring-clean.
zeroize = { version = "1.8", default-features = false, features = ["derive"] }

# local workspace deps
tailscale = { path = ".", package = "geiserx_tailscale", version = "0.34.1" } # x-release-please-version
ts_array256 = { path = "ts_array256", package = "geiserx_ts_array256", version = "0.34.1" } # x-release-please-version
ts_bart = { path = "ts_bart", package = "geiserx_ts_bart", version = "0.34.1" } # x-release-please-version
ts_bart_packetfilter = { path = "ts_bart_packetfilter", package = "geiserx_ts_bart_packetfilter", version = "0.34.1" } # x-release-please-version
ts_bitset = { path = "ts_bitset", package = "geiserx_ts_bitset", default-features = false, version = "0.34.1" } # x-release-please-version
ts_capabilityversion = { path = "ts_capabilityversion", package = "geiserx_ts_capabilityversion", version = "0.34.1" } # x-release-please-version
ts_cli_util = { path = "ts_cli_util" }
ts_control = { path = "ts_control", package = "geiserx_ts_control", version = "0.34.1" } # x-release-please-version
ts_control_noise = { path = "ts_control_noise", package = "geiserx_ts_control_noise", version = "0.34.1" } # x-release-please-version
ts_control_serde = { path = "ts_control_serde", package = "geiserx_ts_control_serde", version = "0.34.1" } # x-release-please-version
ts_dataplane = { path = "ts_dataplane", package = "geiserx_ts_dataplane", version = "0.34.1" } # x-release-please-version
ts_derp = { path = "ts_derp", package = "geiserx_ts_derp", version = "0.34.1" } # x-release-please-version
ts_disco_protocol = { path = "ts_disco_protocol", package = "geiserx_ts_disco_protocol", version = "0.34.1" } # x-release-please-version
ts_dns_wire = { path = "ts_dns_wire", package = "geiserx_ts_dns_wire", version = "0.34.1" } # x-release-please-version
ts_dynbitset = { path = "ts_dynbitset", package = "geiserx_ts_dynbitset", version = "0.34.1" } # x-release-please-version
ts_forwarder = { path = "ts_forwarder", package = "geiserx_ts_forwarder", version = "0.34.1" } # x-release-please-version
ts_hexdump = { path = "ts_hexdump", package = "geiserx_ts_hexdump", version = "0.34.1" } # x-release-please-version
ts_host_net = { path = "ts_host_net", package = "geiserx_ts_host_net", version = "0.34.1" } # x-release-please-version
ts_keys = { path = "ts_keys", package = "geiserx_ts_keys", version = "0.34.1" } # x-release-please-version
ts_magicsock = { path = "ts_magicsock", package = "geiserx_ts_magicsock", version = "0.34.1" } # x-release-please-version
ts_metrics = { path = "ts_metrics", package = "geiserx_ts_metrics", version = "0.34.1" } # x-release-please-version
ts_netcheck = { path = "ts_netcheck", package = "geiserx_ts_netcheck", version = "0.34.1" } # x-release-please-version
ts_netstack_smoltcp = { path = "ts_netstack_smoltcp", package = "geiserx_ts_netstack_smoltcp", version = "0.34.1" } # x-release-please-version
ts_netstack_smoltcp_core = { path = "ts_netstack_smoltcp_core", package = "geiserx_ts_netstack_smoltcp_core", version = "0.34.1" } # x-release-please-version
ts_netstack_smoltcp_socket = { path = "ts_netstack_smoltcp_socket", package = "geiserx_ts_netstack_smoltcp_socket", version = "0.34.1" } # x-release-please-version
ts_nodecapability = { path = "ts_nodecapability", package = "geiserx_ts_nodecapability", version = "0.34.1" } # x-release-please-version
ts_overlay_router = { path = "ts_overlay_router", package = "geiserx_ts_overlay_router", version = "0.34.1" } # x-release-please-version
ts_packet = { path = "ts_packet", package = "geiserx_ts_packet", version = "0.34.1" } # x-release-please-version
ts_packetfilter = { path = "ts_packetfilter", package = "geiserx_ts_packetfilter", version = "0.34.1" } # x-release-please-version
ts_packetfilter_serde = { path = "ts_packetfilter_serde", package = "geiserx_ts_packetfilter_serde", version = "0.34.1" } # x-release-please-version
ts_packetfilter_state = { path = "ts_packetfilter_state", package = "geiserx_ts_packetfilter_state", version = "0.34.1" } # x-release-please-version
ts_peercapability = { path = "ts_peercapability", package = "geiserx_ts_peercapability", version = "0.34.1" } # x-release-please-version
ts_http_util = { path = "ts_http_util", package = "geiserx_ts_http_util", version = "0.34.1" } # x-release-please-version
ts_tka = { path = "ts_tka", package = "geiserx_ts_tka", version = "0.34.1" } # x-release-please-version
ts_tls_util = { path = "ts_tls_util", package = "geiserx_ts_tls_util", version = "0.34.1" } # x-release-please-version
ts_runtime = { path = "ts_runtime", package = "geiserx_ts_runtime", version = "0.34.1" } # x-release-please-version
ts_test_util = { path = "ts_test_util" }
ts_time = { path = "ts_time", package = "geiserx_ts_time", version = "0.34.1" } # x-release-please-version
ts_transport = { path = "ts_transport", package = "geiserx_ts_transport", version = "0.34.1" } # x-release-please-version
ts_transport_tun = { path = "ts_transport_tun", package = "geiserx_ts_transport_tun", version = "0.34.1" } # x-release-please-version
ts_underlay_router = { path = "ts_underlay_router", package = "geiserx_ts_underlay_router", version = "0.34.1" } # x-release-please-version
ts_tunnel = { path = "ts_tunnel", package = "geiserx_ts_tunnel", version = "0.34.1" } # x-release-please-version

[workspace.lints.rust]
closure_returning_async_block = "warn"
let-underscore = "warn"
missing_docs = "warn"

# useful reference: https://rust-lang.github.io/rust-clippy/master/index.html
[workspace.lints.clippy]
cargo = { level = "warn", priority = -1 }
too_many_arguments = "allow"
# too annoying to manage dependencies
multiple_crate_versions = "allow"

[workspace.lints.rustdoc]
bare_urls = "deny"
broken_intra_doc_links = "deny"
invalid_html_tags = "deny"
private_intra_doc_links = "deny"

[profile.release]
lto = true
opt-level = "s"
split-debuginfo = "packed"
strip = true

[profile.bench]
debug = true
opt-level = 3
split-debuginfo = "off"
codegen-units = 1
strip = false

[profile.faststrip]
debug = false
lto = true
opt-level = 3
split-debuginfo = "off"
codegen-units = 1
incremental = false
strip = true
inherits = "release"

[package]
# Published crate name (the crate `tailscale` is already taken on crates.io). The library name
# stays `tailscale` (see `[lib]` below) so all `use tailscale::…` code is unchanged.
name = "geiserx_tailscale"
version.workspace = true
description = "A work-in-progress pure-Rust Tailscale implementation (fork of tailscale/tailscale-rs)"
categories = ["network-programming", "api-bindings", "authentication", "asynchronous"]
keywords = ["tailscale", "networking"]

repository.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true

[lib]
# Keep the importable crate name `tailscale` regardless of the published package name, so
# `use tailscale::Device` etc. continues to work across the workspace and for downstream users.
name = "tailscale"

[package.metadata.docs.rs]
all-features = true

[dependencies]
# Our crates.
ts_runtime.workspace = true
ts_control.workspace = true
ts_metrics.workspace = true
ts_tka.workspace = true
ts_packet.workspace = true
ts_netstack_smoltcp = { workspace = true, features = ["tokio"] }
ts_keys.workspace = true

axum = { version = "0.8", optional = true }
bytes = { workspace = true, optional = true }
# `hyper` feature: a ready-made HTTP connector that dials over the tailnet (the analog of Go
# `tsnet.Server.HTTPClient`'s `http.Transport{DialContext: s.Dial}`). Off by default — the default
# facade build stays HTTP-stack-free. `hyper` provides the `rt::{Read,Write}` IO traits, `hyper-util`
# the `client-legacy` `Connect`/`Connection` glue + `TokioIo`, and `tower-service` the `Service<Uri>`
# trait the connector implements (the blanket impl grants the sealed `Connect`).
hyper = { workspace = true, optional = true }
hyper-util = { workspace = true, optional = true, features = ["client-legacy", "tokio"] }
tower-service = { workspace = true, optional = true }
ipnet.workspace = true
nix = { version = "0.30", optional = true, features = ["user", "process", "term", "signal"] }
pty-process = { version = "0.5", optional = true, features = ["async"] }
rand.workspace = true
ratatui = { version = "0.30", optional = true }
# exclude rsa to avoid https://rustsec.org/advisories/RUSTSEC-2023-0071
russh = { version = "0.61.2", optional = true, default-features = false, features = ["flate2", "aws-lc-rs"] }
secrecy.workspace = true
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["fs", "net", "io-util", "sync"] }
tracing.workspace = true
url.workspace = true

[dev-dependencies]
# Dependencies for examples
clap = { workspace = true, features = ["derive", "env", "string"] }
chrono.workspace = true
include_dir = "0.7"
itertools.workspace = true
mime_guess = "2.0"
tokio = { workspace = true, features = ["full"] }
tracing = { workspace = true, features = ["release_max_level_info"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-test = "0.2"

ts_keys.workspace = true
ts_test_util.workspace = true

[lints]
workspace = true

[features]
# Enable the `axum` module, which enables you to run an `axum` HTTP server on top of a tailscale TCP
# listener.
axum = ["dep:axum"]
# Enable the `http` module: a ready-made hyper connector (`Device::http_connector`) that routes
# outbound HTTP requests over the tailnet, the analog of Go `tsnet.Server.HTTPClient`. Off by default
# so the default build pulls no HTTP client stack.
hyper = ["dep:hyper", "dep:hyper-util", "dep:tower-service"]
ssh = ["dep:russh", "dep:bytes", "dep:ratatui", "dep:pty-process", "dep:nix"]
tun = ["ts_runtime/tun"]
# Workload-identity federation (SaaS-only): enables the `ts_control` exchange subsystem that
# trades an OAuth client secret or IdP-issued OIDC token for a Tailscale auth key. Off by default.
identity-federation = ["ts_control/identity-federation"]
# Client-side ACME (Let's Encrypt) cert issuance (SaaS-only, off by default): makes
# `Device::get_certificate` issue a real cert for the node's `*.ts.net` name via the node's set-dns
# RPC, instead of the fail-closed `CertError::Unimplemented` stub. A self-hosted control plane may 501 on set-dns,
# so issuance is non-functional there (built for full tsnet parity, like identity-federation).
acme = ["ts_control/acme", "ts_runtime/acme"]

[[example]]
name = "axum"
required-features = ["axum"]

[[example]]
name = "peer_ping"

[[example]]
name = "tcp_echo"

[[example]]
name = "ssh_peer_lookup"
required-features = ["ssh"]
