pub struct Device { /* private fields */ }Expand description
Tailscale client.
Implementations§
Source§impl Device
impl Device
Sourcepub fn udp_bind<'p>(
&self,
py: Python<'p>,
addr: (IpRepr, u16),
) -> PyResult<Bound<'p, PyAny>>
pub fn udp_bind<'p>( &self, py: Python<'p>, addr: (IpRepr, u16), ) -> PyResult<Bound<'p, PyAny>>
Bind a new UDP socket on the given addr.
addr must be given as (host, port). Presently, host must be an IP.
Sourcepub fn tcp_listen<'p>(
&self,
py: Python<'p>,
addr: (IpRepr, u16),
) -> PyResult<Bound<'p, PyAny>>
pub fn tcp_listen<'p>( &self, py: Python<'p>, addr: (IpRepr, u16), ) -> PyResult<Bound<'p, PyAny>>
Bind a new TCP listen socket on the given addr and port.
addr must be given as (host, port). Presently, host must be an IP.
Sourcepub fn tcp_connect<'p>(
&self,
py: Python<'p>,
addr: (IpRepr, u16),
) -> PyResult<Bound<'p, PyAny>>
pub fn tcp_connect<'p>( &self, py: Python<'p>, addr: (IpRepr, u16), ) -> PyResult<Bound<'p, PyAny>>
Create a new TCP connection to the given addr.
addr must be given as (host, port). Presently, host must be an IP.
Sourcepub fn ipv4_addr<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
pub fn ipv4_addr<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
Get the device’s IPv4 tailnet address.
Sourcepub fn ipv6_addr<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
pub fn ipv6_addr<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
Get the device’s IPv6 tailnet address.
Sourcepub fn peer_by_name<'p>(
&self,
py: Python<'p>,
name: String,
) -> PyResult<Bound<'p, PyAny>>
pub fn peer_by_name<'p>( &self, py: Python<'p>, name: String, ) -> PyResult<Bound<'p, PyAny>>
Look up info about a peer by its name.
name may be an unqualified hostname or a fully-qualified name.
Sourcepub fn self_node<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
pub fn self_node<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
Get this device’s node info.
Sourcepub fn peer_by_tailnet_ip<'p>(
&self,
py: Python<'p>,
ip: IpRepr,
) -> PyResult<Bound<'p, PyAny>>
pub fn peer_by_tailnet_ip<'p>( &self, py: Python<'p>, ip: IpRepr, ) -> PyResult<Bound<'p, PyAny>>
Look up a peer by its tailnet IP address.
Sourcepub fn peers_with_route<'p>(
&self,
py: Python<'p>,
ip: IpRepr,
) -> PyResult<Bound<'p, PyAny>>
pub fn peers_with_route<'p>( &self, py: Python<'p>, ip: IpRepr, ) -> PyResult<Bound<'p, PyAny>>
Look up peer(s) with the most specific route match for the given address.
If more than one peer has the same route covering the same address, more than one result may be returned.
Sourcepub fn status<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
pub fn status<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
Snapshot of this device and its tailnet peers (like tailscale status).
Returns a dict {"self_node": <node>|None, "peers": [<node>, ...]} where each node carries
stable_id, display_name, ipv4, ipv6, online, allowed_routes, and is_exit_node.
Sourcepub fn whois<'p>(
&self,
py: Python<'p>,
addr: String,
) -> PyResult<Bound<'p, PyAny>>
pub fn whois<'p>( &self, py: Python<'p>, addr: String, ) -> PyResult<Bound<'p, PyAny>>
Map a tailnet source addr to the node that owns its IP (like tsnet’s WhoIs).
addr may be an ip or host:port string; only the IP is used. Returns None if no
tailnet node owns that address.
Sourcepub fn netmap<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
pub fn netmap<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
One-shot snapshot of the current netmap peers (the current value of the netmap watch).
Returns the list of peer nodes as of now, in the same shape as status()["peers"]. Mirrors
reading the current value off tsnet’s WatchIPNBus subscription.
Sourcepub fn resolve<'p>(
&self,
py: Python<'p>,
name: String,
) -> PyResult<Bound<'p, PyAny>>
pub fn resolve<'p>( &self, py: Python<'p>, name: String, ) -> PyResult<Bound<'p, PyAny>>
Resolve a tailnet peer (or this node) by MagicDNS name to its tailnet IPv4 address.
Returns the IPv4 address as a string, or None if no tailnet node has that name. This is an
in-process netmap lookup — it does not query any DNS server. IPv6 is not resolved (this fork
is IPv4-only on the tailnet).
Sourcepub fn connect_by_name<'p>(
&self,
py: Python<'p>,
name: String,
port: u16,
) -> PyResult<Bound<'p, PyAny>>
pub fn connect_by_name<'p>( &self, py: Python<'p>, name: String, port: u16, ) -> PyResult<Bound<'p, PyAny>>
Connect to a tailnet peer by MagicDNS name and port over TCP.
Resolves name via Device::resolve (an in-process netmap lookup, no DNS server), then
dials the resulting tailnet IPv4 address. Raises if the name does not resolve to a tailnet
node. Returns the same TcpStream as tcp_connect.
Sourcepub fn ping<'p>(
&self,
py: Python<'p>,
addr: IpRepr,
timeout_ms: u64,
) -> PyResult<Bound<'p, PyAny>>
pub fn ping<'p>( &self, py: Python<'p>, addr: IpRepr, timeout_ms: u64, ) -> PyResult<Bound<'p, PyAny>>
Ping a tailnet peer over the overlay with an ICMPv4 echo (like tailscale ping).
addr is the peer’s tailnet IP; timeout_ms is the timeout in milliseconds. Returns the
round-trip time in milliseconds (a float), or raises on timeout / unsupported IPv6
destination. The echo is sent from this device’s own tailnet IPv4 over the overlay netstack
— never a host socket.
Sourcepub fn get_certificate<'p>(
&self,
py: Python<'p>,
name: String,
) -> PyResult<Bound<'p, PyAny>>
pub fn get_certificate<'p>( &self, py: Python<'p>, name: String, ) -> PyResult<Bound<'p, PyAny>>
Obtain a TLS certificate for a node’s MagicDNS name (like tsnet’s GetCertificate).
Fail-closed. This fork has no client-side ACME engine and no set-dns RPC, so this
ALWAYS raises a Python exception carrying the underlying CertError (issuance is
unimplemented). It NEVER self-signs and NEVER returns a placeholder certificate. When ACME
issuance lands upstream, this starts succeeding with no API change.
Sourcepub fn listen_tls<'p>(
&self,
py: Python<'p>,
serve_config: ServeConfigArg,
) -> PyResult<Bound<'p, PyAny>>
pub fn listen_tls<'p>( &self, py: Python<'p>, serve_config: ServeConfigArg, ) -> PyResult<Bound<'p, PyAny>>
Build a TLS listener config for serve_config on the overlay (like tsnet’s ListenTLS).
serve_config is a mapping {"name": str, "port": int, "target": <target>} where target
is "accept" or {"proxy": "host:port"}.
Fail-closed. Delegates to Device::get_certificate; because no real certificate can be
issued in this fork, this ALWAYS raises the same CertError rather than ever serving a
self-signed cert or downgrading to plaintext. The serve config is validated first, so an
off-tailnet name / zero port / empty proxy target raises a distinct error.
Sourcepub fn fetch_id_token<'p>(
&self,
py: Python<'p>,
audience: String,
) -> PyResult<Bound<'p, PyAny>>
pub fn fetch_id_token<'p>( &self, py: Python<'p>, audience: String, ) -> PyResult<Bound<'p, PyAny>>
Fetch an OIDC ID token from control scoped to audience (like tailscale id-token).
Returns the signed JWT as a string. The sub claim is this node’s MagicDNS name and the
aud claim is audience, suitable for workload-identity federation (AWS/GCP). Raises if
control does not support id-token issuance.
Sourcepub fn metrics(&self) -> String
pub fn metrics(&self) -> String
Snapshot this process’s client metrics in Prometheus text exposition format.
The metric registry is process-global, so the returned text covers every Device in the
process. Synchronous — no overlay round-trip is involved.
Sourcepub fn self_key_expiry_unix<'p>(
&self,
py: Python<'p>,
) -> PyResult<Bound<'p, PyAny>>
pub fn self_key_expiry_unix<'p>( &self, py: Python<'p>, ) -> PyResult<Bound<'p, PyAny>>
This node’s key-expiry instant as Unix seconds, or None if the key never expires.
This fork is reactive about key expiry (it reports rather than rotating in the background); schedule re-authentication around this time.
Sourcepub fn self_key_expired<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
pub fn self_key_expired<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
Whether this node’s key has expired as of now. A key with no expiry is never expired.
Sourcepub fn taildrop_waiting_files(&self) -> PyResult<Vec<(String, u64)>>
pub fn taildrop_waiting_files(&self) -> PyResult<Vec<(String, u64)>>
List the Taildrop files this device has fully received and not yet consumed.
Returns a list of dicts {"name": str, "size": int}, sorted by name. Returns an empty list
when Taildrop is disabled (fail-closed, never an error). Synchronous (a local filesystem
listing).
Sourcepub fn taildrop_delete_file(&self, name: String) -> PyResult<()>
pub fn taildrop_delete_file(&self, name: String) -> PyResult<()>
Delete a received Taildrop file by name (path-traversal-safe; validated in the store).
Raises when Taildrop is disabled, the name is invalid, or the file does not exist. Synchronous (a local filesystem delete).
Sourcepub fn taildrop_save_file(
&self,
name: String,
dst_path: String,
) -> PyResult<u64>
pub fn taildrop_save_file( &self, name: String, dst_path: String, ) -> PyResult<u64>
Save a received Taildrop file by name to dst_path on the local filesystem.
Opens the received file via the store (path-traversal-safe) and copies its bytes to
dst_path, returning the number of bytes written. Pyo3 cannot hand back a raw file handle,
so this save-to-path shape is the Pythonic equivalent of Go’s OpenFile. Synchronous (local
filesystem I/O). Raises when Taildrop is disabled, the name is invalid, the source file does
not exist, or dst_path cannot be written.
Sourcepub fn send_file<'p>(
&self,
py: Python<'p>,
peer_name: String,
file_name: String,
src_path: String,
) -> PyResult<Bound<'p, PyAny>>
pub fn send_file<'p>( &self, py: Python<'p>, peer_name: String, file_name: String, src_path: String, ) -> PyResult<Bound<'p, PyAny>>
Send a local file at src_path to tailnet peer peer_name via Taildrop (Go PushFile).
Resolves peer_name via peer_by_name, opens src_path as a tokio
file, and streams it to the peer’s peerAPI over the encrypted overlay (never a host socket).
file_name is the base name the receiver sees. Raises when the peer is unknown, the peer
advertises no IPv4 peerAPI, or the transfer fails.
Sourcepub fn capture_pcap<'p>(
&self,
py: Python<'p>,
dst_path: String,
) -> PyResult<Bound<'p, PyAny>>
pub fn capture_pcap<'p>( &self, py: Python<'p>, dst_path: String, ) -> PyResult<Bound<'p, PyAny>>
Begin a debug packet capture, writing a pcap of every dataplane packet to dst_path.
Opens dst_path and streams a classic pcap (Tailscale LINKTYPE_USER0) of every plaintext
IP packet — outbound (pre-encrypt) and inbound (post-decrypt) — until
stop_capture is called. Records are buffered and flushed on stop.
Opens in Wireshark with Tailscale’s ts-dissector.lua.
Sourcepub fn stop_capture<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
pub fn stop_capture<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
Stop a packet capture started by capture_pcap.
Clears the dataplane capture hook; the writer is dropped and its buffered bytes flushed. Idempotent — stopping when no capture is installed is a no-op.
Sourcepub fn loopback<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
pub fn loopback<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
Start a host-loopback SOCKS5 proxy that dials into the tailnet (Go tsnet.Loopback).
Returns a tuple (addr, proxy_cred, handle) where addr is the bound 127.0.0.1:port
string, proxy_cred is the SOCKS5 password (username is tsnet), and handle is a
LoopbackHandle whose .stop() (or garbage collection) stops the proxy. Hold the handle
for exactly as long as you want the proxy alive. Raises in TUN transport mode.
Sourcepub fn tka_status<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
pub fn tka_status<'p>(&self, py: Python<'p>) -> PyResult<Bound<'p, PyAny>>
Fetch the current Tailnet Lock (TKA) status pushed by control, if any.
Returns None when control has sent no TKAInfo, else a dict {"head": str, "disabled": bool} where head is the base32 (no-pad) AUMHash of the latest applied
Authority Update Message.
Sourcepub fn listen_funnel<'p>(
&self,
py: Python<'p>,
serve_config: ServeConfigArg,
funnel_only: bool,
) -> PyResult<Bound<'p, PyAny>>
pub fn listen_funnel<'p>( &self, py: Python<'p>, serve_config: ServeConfigArg, funnel_only: bool, ) -> PyResult<Bound<'p, PyAny>>
Build a Funnel TLS listener config for serve_config (like tsnet’s ListenFunnel).
serve_config has the same shape as listen_tls. funnel_only (default
False) rejects tailnet-internal connections, serving only public Funnel ingress.
Fail-closed. Enforces the node-attribute / port gates first, then obtains the node’s
*.ts.net cert via the ACME-aware path (raising FunnelError on cert failure — never
plaintext or a self-signed cert). On success the funnel ingress listener is registered; the
returned FunnelAcceptedReceiver is dropped here (Python holds no Rust receiver), so this
surfaces only the gate/cert outcome. The public ingress relay that feeds it is Tailscale
infrastructure, present only against real Tailscale SaaS.
Sourcepub fn listen_service<'p>(
&self,
py: Python<'p>,
service_name: String,
mode: ServiceModeArg,
) -> PyResult<Bound<'p, PyAny>>
pub fn listen_service<'p>( &self, py: Python<'p>, service_name: String, mode: ServiceModeArg, ) -> PyResult<Bound<'p, PyAny>>
Host a Tailscale VIP service (svc:<label>) by service_name (like ListenService).
mode is a dict {"mode": "tcp"|"http", "port": int}. Returns a [TcpListener] bound on the
service’s control-assigned VIP over the overlay netstack.
Fail-closed. The service_name must be a valid svc:<dns-label>, this node must be
tagged, and control must have assigned the service a VIP on this node; any unmet precondition
raises before binding.
Trait Implementations§
impl DerefToPyAny for Device
impl ExtractPyClassWithClone for Device
Source§impl<'py> IntoPyObject<'py> for Device
impl<'py> IntoPyObject<'py> for Device
Source§type Output = Bound<'py, <Device as IntoPyObject<'py>>::Target>
type Output = Bound<'py, <Device as IntoPyObject<'py>>::Target>
Source§fn into_pyobject(
self,
py: Python<'py>,
) -> Result<<Self as IntoPyObject<'_>>::Output, <Self as IntoPyObject<'_>>::Error>
fn into_pyobject( self, py: Python<'py>, ) -> Result<<Self as IntoPyObject<'_>>::Output, <Self as IntoPyObject<'_>>::Error>
Source§impl PyClassImpl for Device
impl PyClassImpl for Device
Source§const IS_BASETYPE: bool = false
const IS_BASETYPE: bool = false
Source§const IS_SUBCLASS: bool = false
const IS_SUBCLASS: bool = false
Source§const IS_MAPPING: bool = false
const IS_MAPPING: bool = false
Source§const IS_SEQUENCE: bool = false
const IS_SEQUENCE: bool = false
Source§const IS_IMMUTABLE_TYPE: bool = false
const IS_IMMUTABLE_TYPE: bool = false
Source§const RAW_DOC: &'static CStr = /// Tailscale client.
const RAW_DOC: &'static CStr = /// Tailscale client.
Source§const DOC: &'static CStr
const DOC: &'static CStr
text_signature if a constructor is defined. Read moreSource§type Layout = <<Device as PyClassImpl>::BaseNativeType as PyClassBaseType>::Layout<Device>
type Layout = <<Device as PyClassImpl>::BaseNativeType as PyClassBaseType>::Layout<Device>
Source§type ThreadChecker = NoopThreadChecker
type ThreadChecker = NoopThreadChecker
Source§type PyClassMutability = <<PyAny as PyClassBaseType>::PyClassMutability as PyClassMutability>::ImmutableChild
type PyClassMutability = <<PyAny as PyClassBaseType>::PyClassMutability as PyClassMutability>::ImmutableChild
Source§type BaseNativeType = PyAny
type BaseNativeType = PyAny
PyAny by default, and when you declare
#[pyclass(extends=PyDict)], it’s PyDict.fn items_iter() -> PyClassItemsIter
fn lazy_type_object() -> &'static LazyTypeObject<Self>
Source§fn dict_offset() -> Option<PyObjectOffset>
fn dict_offset() -> Option<PyObjectOffset>
Source§fn weaklist_offset() -> Option<PyObjectOffset>
fn weaklist_offset() -> Option<PyObjectOffset>
Source§impl PyMethods<Device> for PyClassImplCollector<Device>
impl PyMethods<Device> for PyClassImplCollector<Device>
fn py_methods(self) -> &'static PyClassItems
Source§impl PyTypeInfo for Device
impl PyTypeInfo for Device
Source§const NAME: &str = <Self as ::pyo3::PyClass>::NAME
const NAME: &str = <Self as ::pyo3::PyClass>::NAME
prefer using ::type_object(py).name() to get the correct runtime value
Source§const MODULE: Option<&str> = <Self as ::pyo3::impl_::pyclass::PyClassImpl>::MODULE
const MODULE: Option<&str> = <Self as ::pyo3::impl_::pyclass::PyClassImpl>::MODULE
prefer using ::type_object(py).module() to get the correct runtime value
Source§fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject
fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject
Source§fn type_object(py: Python<'_>) -> Bound<'_, PyType>
fn type_object(py: Python<'_>) -> Bound<'_, PyType>
Auto Trait Implementations§
impl !RefUnwindSafe for Device
impl !UnwindSafe for Device
impl Freeze for Device
impl Send for Device
impl Sync for Device
impl Unpin for Device
impl UnsafeUnpin for Device
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<A, T> DynMessage<A> for T
impl<A, T> DynMessage<A> for T
Source§fn handle_dyn<'a>(
self: Box<T>,
state: &'a mut A,
actor_ref: ActorRef<A>,
tx: Option<Sender<Result<Box<dyn Any + Send>, SendError<Box<dyn Any + Send>, Box<dyn Any + Send>>>>>,
stop: &'a mut bool,
) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn ReplyError>>> + Send + 'a>>
fn handle_dyn<'a>( self: Box<T>, state: &'a mut A, actor_ref: ActorRef<A>, tx: Option<Sender<Result<Box<dyn Any + Send>, SendError<Box<dyn Any + Send>, Box<dyn Any + Send>>>>>, stop: &'a mut bool, ) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn ReplyError>>> + Send + 'a>>
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<'py, T> IntoPyObjectExt<'py> for Twhere
T: IntoPyObject<'py>,
impl<'py, T> IntoPyObjectExt<'py> for Twhere
T: IntoPyObject<'py>,
Source§fn into_bound_py_any(self, py: Python<'py>) -> Result<Bound<'py, PyAny>, PyErr>
fn into_bound_py_any(self, py: Python<'py>) -> Result<Bound<'py, PyAny>, PyErr>
self into an owned Python object, dropping type information.Source§impl<T> PyErrArguments for T
impl<T> PyErrArguments for T
Source§impl<T> PyTypeCheck for Twhere
T: PyTypeInfo,
impl<T> PyTypeCheck for Twhere
T: PyTypeInfo,
Source§const NAME: &'static str = T::NAME
const NAME: &'static str = T::NAME
Use ::classinfo_object() instead and format the type name at runtime. Note that using built-in cast features is often better than manual PyTypeCheck usage.