Skip to main content

Node

Struct Node 

Source
pub struct Node<N: NetworkProvider + 'static> { /* private fields */ }
Expand description

The main truffle node — single public entry point for all functionality.

Generic over N: NetworkProvider so that tests can inject a mock provider without Tailscale. In production, use the concrete type Node<TailscaleProvider> (created via NodeBuilder).

§Lifecycle

  1. Create via Node::builder() + .build().await
  2. Use peers(), send(), subscribe(), open_tcp(), etc.
  3. Call stop() to shut down

Implementations§

Source§

impl<N: NetworkProvider + 'static> Node<N>

Source

pub fn builder() -> NodeBuilder

Create a new NodeBuilder for configuring and constructing a node.

Source

pub fn file_transfer(&self) -> FileTransfer<'_, N>

Access the file transfer subsystem.

Returns a FileTransfer handle that provides methods for sending, receiving, and pulling files.

Source

pub fn proxy(&self) -> Proxy<'_, N>

Access the reverse proxy subsystem.

Returns a Proxy handle that provides methods for adding, removing, and listing reverse proxies.

Source

pub fn synced_store<T>(self: &Arc<Self>, store_id: &str) -> Arc<SyncedStore<T>>
where T: Serialize + DeserializeOwned + Clone + Send + Sync + 'static,

Create a synchronized store for device-owned state.

Returns an Arc<SyncedStore<T>> that syncs data across the mesh on namespace "ss:{store_id}". The caller owns the returned Arc; the background sync task also holds one.

Requires self to be wrapped in an Arc because the sync task needs to outlive this call.

Source

pub fn synced_store_with_backend<T>( self: &Arc<Self>, store_id: &str, backend: Arc<dyn StoreBackend>, ) -> Arc<SyncedStore<T>>
where T: Serialize + DeserializeOwned + Clone + Send + Sync + 'static,

Create a synchronized store with a custom persistence backend.

Same as synced_store but restores persisted data on startup and writes through to the backend on every change.

Source

pub fn state_dir(&self) -> &Path

The state directory for persistence backends.

Returns an empty path for test nodes created via from_parts.

Source

pub async fn stop(&self)

Stop the node and all underlying layers.

Closes every active WebSocket connection (Layer 5) and shuts down the network provider (Layer 3 — sidecar + bridge). After stop() returns, send and send_typed fail with NodeError::Stopped, and broadcast / broadcast_typed become no-ops.

Idempotent: calling stop() more than once is safe — subsequent calls return immediately without repeating teardown.

The envelope-router and peer-event background tasks are not aborted here; they exit on their own once the Node is dropped and their broadcast channels close. They sit idle after stop().

Source

pub fn local_info(&self) -> NodeIdentity

Return the local node’s identity (stable ID, hostname, name).

Source

pub async fn peers(&self) -> Vec<Peer>

Return all known peers.

Includes peers that are online but not yet connected (no active WS). This information comes from Layer 3 peer discovery.

Source

pub fn on_peer_change(&self) -> Receiver<PeerEvent>

Subscribe to peer change events (joined, left, connected, etc.).

Source

pub async fn peer( &self, query: &str, wait_ms: Option<u64>, ) -> Result<Option<Peer>, NodeError>

Resolve a query to a public Peer handle view (RFC 022).

wait_ms: when set, block until the query becomes resolvable or the timeout elapses (then Ok(None)). Useful for rehydrating a saved ULID whose owner has not completed hello yet.

Source

pub async fn resolve_peer_ip(&self, peer_ref: &str) -> Result<IpAddr, NodeError>

Resolve a peer identifier to the peer’s Tailscale IP address.

Accepts the same identifier forms as resolve_peer_id. Used by the FFI layers to address datagram sends by peer name.

Source

pub async fn resolve_peer_id(&self, peer_id: &str) -> Result<String, NodeError>

Resolve a peer query to a string safe to pass to send and other peer-addressed methods.

Prefer the published durable ULID when known and not suppressed; otherwise return the Tailscale stable id (routing key). This is the legacy string path — RFC 022 Phase B replaces it with peer() handles.

Accepts any of:

  • the stable device_id (full ULID)
  • a unique prefix of the device_id (at least 4 characters; must match exactly one known peer)
  • the human-readable device_name from the hello
  • the bare device name of a peer that has not helloed yet (matched via the truffle-{app_id}-{slug} hostname convention)
  • the Layer 3 Tailscale hostname (the sanitised slug)
  • the Tailscale stable ID
  • the Tailscale IP address (e.g. 100.x.x.x)
Source

pub async fn ping(&self, peer_id: &str) -> Result<PingResult, NodeError>

Ping a peer via the network layer.

Resolves the peer ID to an IP address and pings via Layer 3. Accepts the same identifier forms as resolve_peer_id.

Source

pub async fn health(&self) -> HealthInfo

Return health information from the network layer.

Source

pub async fn send( &self, peer_id: &str, namespace: &str, data: &[u8], ) -> Result<(), NodeError>

Send a namespaced message to a specific peer.

The data is wrapped in a Layer 6 Envelope with the given namespace and a "message" type, then serialized and sent via the session layer. If no WebSocket connection exists, one is lazily established.

Returns NodeError::Stopped if stop has been called.

Source

pub async fn send_typed( &self, peer_id: &str, namespace: &str, msg_type: &str, payload: &Value, ) -> Result<(), NodeError>

Send a namespaced message with an explicit msg_type and JSON payload.

Unlike send, this method takes a pre-built serde_json::Value payload and a caller-chosen msg_type instead of raw bytes with a hardcoded "message" type. Used by subsystems (file transfer, synced store, request/reply) that define their own wire protocol message types.

Source

pub async fn broadcast_typed( &self, namespace: &str, msg_type: &str, payload: &Value, )

Broadcast a namespaced message with an explicit msg_type and JSON payload to all connected peers.

Source

pub async fn broadcast(&self, namespace: &str, data: &[u8])

Broadcast a namespaced message to all connected peers.

Only peers with active WebSocket connections receive the broadcast. No lazy connections are established.

Source

pub fn subscribe(&self, namespace: &str) -> Receiver<NamespacedMessage>

Subscribe to messages in a specific namespace.

Returns a broadcast receiver that yields NamespacedMessages matching the given namespace. Multiple subscribers to the same namespace share the same underlying channel.

Source

pub async fn open_tcp( &self, peer_id: &str, port: u16, ) -> Result<TcpStream, NodeError>

Open a raw TCP stream to a peer on the given port.

Resolves the peer ID to an IP address via the session’s peer list, then dials via the network layer. Accepts the same identifier forms as resolve_peer_id. Returns a plain TcpStream for byte-oriented I/O.

The stream is raw even on port 443 — the sidecar’s legacy auto-TLS wrap for 443 dials is disabled on this path.

Source

pub async fn listen_tcp(&self, port: u16) -> Result<RawListener, NodeError>

Listen for incoming TCP connections on a port.

Returns a RawListener that yields raw TcpStreams. The caller is responsible for accepting connections in a loop. Port 0 binds an ephemeral port (advertise the resolved RawListener::port in-band, like the file transfer subsystem does). Ports 443 and 9417 are reserved by truffle’s own listeners.

Source

pub async fn unlisten_tcp(&self, port: u16) -> Result<(), NodeError>

Stop listening on a previously opened TCP port.

Dropping the RawListener alone stops local delivery but leaves the tsnet port bound in the sidecar; this releases it.

Source

pub async fn connect_quic( &self, peer_id: &str, port: u16, ) -> Result<QuicConnection, NodeError>

Open a raw QUIC connection to a peer on the given port.

The connection carries multiple concurrent bidirectional byte streams (QuicConnection::open_stream) with no head-of-line blocking between them. App scoping is enforced at the TLS layer via ALPN (truffle-raw.{app_id}) — peers from a different app fail the handshake. Accepts the same identifier forms as resolve_peer_id.

Source

pub async fn listen_quic(&self, port: u16) -> Result<QuicListener, NodeError>

Listen for raw QUIC connections on a port.

Returns a QuicListener that yields QuicConnections. Only same-app peers can complete the handshake (ALPN scoping). Ports 443 and 9417 are reserved, and port 0 is not yet supported over the tsnet relay (the relay cannot report the actual ephemeral port back).

Source

pub async fn bind_udp(&self, port: u16) -> Result<DatagramSocket, NodeError>

Bind a UDP datagram socket on a port.

Datagrams are relayed through the network provider (tsnet) with boundaries preserved; the transport falls back to a direct host socket only when the provider has no UDP support (tests). Returns a DatagramSocket supporting send_to / recv_from with tailnet addresses. IPv4 (100.x) peers only; keep payloads ≤ ~1200 bytes to stay under the tailnet MTU. Port 0 binds an ephemeral relay port — suitable for client-style sockets that send first.

Auto Trait Implementations§

§

impl<N> !Freeze for Node<N>

§

impl<N> !RefUnwindSafe for Node<N>

§

impl<N> !UnwindSafe for Node<N>

§

impl<N> Send for Node<N>

§

impl<N> Sync for Node<N>

§

impl<N> Unpin for Node<N>

§

impl<N> UnsafeUnpin for Node<N>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more