Skip to main content

Node

Struct Node 

Source
pub struct Node { /* private fields */ }
Expand description

Zero-configuration P2P node

This is the primary API for ant-quic. Create a node with zero configuration and it will automatically handle NAT traversal, post-quantum cryptography, and peer discovery.

§Symmetric P2P

All nodes are equal - every node can:

  • Connect to other nodes
  • Accept incoming connections
  • Act as coordinator for NAT traversal
  • Act as relay for peers behind restrictive NATs

§Post-Quantum Security

v0.2: Every connection uses pure post-quantum cryptography:

  • Key Exchange: ML-KEM-768 (FIPS 203)
  • Authentication: ML-DSA-65 (FIPS 204)
  • Ed25519 is used ONLY for the 32-byte PeerId compact identifier

There is no classical crypto fallback - security is quantum-resistant by default.

§Example

use ant_quic::Node;

// Zero configuration
let node = Node::new().await?;

// Or with known peers
let node = Node::with_peers(vec!["quic.saorsalabs.com:9000".parse()?]).await?;

// Or with persistent identity
let keypair = load_keypair()?;
let node = Node::with_keypair(keypair).await?;

Implementations§

Source§

impl Node

Source

pub async fn new() -> Result<Self, NodeError>

Create a node with automatic configuration

This is the recommended way to create a node. It will:

  • Bind to a random port on all interfaces (0.0.0.0:0)
  • Generate a fresh Ed25519 keypair
  • Enable all NAT traversal capabilities
  • Use 100% post-quantum cryptography
§Example
let node = Node::new().await?;
Source

pub async fn bind(addr: SocketAddr) -> Result<Self, NodeError>

Create a node with a specific bind address

Use this when you need a specific port for firewall rules or port forwarding.

§Example
let node = Node::bind("0.0.0.0:9000".parse()?).await?;
Source

pub async fn with_peers(peers: Vec<SocketAddr>) -> Result<Self, NodeError>

Create a node with known peers

Use this when you have a list of known peers to connect to initially. These can be any nodes in the network - they’ll help with NAT traversal.

§Example
let node = Node::with_peers(vec![
    "quic.saorsalabs.com:9000".parse()?,
    "peer2.example.com:9000".parse()?,
]).await?;
Source

pub async fn with_keypair( public_key: MlDsaPublicKey, secret_key: MlDsaSecretKey, ) -> Result<Self, NodeError>

Create a node with an existing keypair

Use this for persistent identity across restarts. The peer ID is derived from the public key, so using the same keypair gives you the same peer ID.

§Example
let (public_key, secret_key) = load_keypair_from_file("~/.ant-quic/identity.key")?;
let node = Node::with_keypair(public_key, secret_key).await?;
Source

pub async fn with_host_identity( host: &HostIdentity, network_id: &[u8], storage_dir: impl AsRef<Path>, ) -> Result<Self, NodeError>

Create a node with a HostIdentity for persistent encrypted identity

This is the recommended way to create a node with persistent identity. The keypair is encrypted at rest using a key derived from the HostIdentity.

§Arguments
  • host - The HostIdentity for key derivation
  • network_id - Network identifier for per-network keypair isolation
  • storage_dir - Directory to store the encrypted keypair
§Example
use ant_quic::{Node, HostIdentity};

let host = HostIdentity::generate();
let node = Node::with_host_identity(
    &host,
    b"my-network",
    "/var/lib/ant-quic",
).await?;
Source

pub async fn with_config(config: NodeConfig) -> Result<Self, NodeError>

Create a node with full configuration

For power users who need specific settings. Most applications should use Node::new() or one of the convenience methods.

§Example
let config = NodeConfig::builder()
    .bind_addr("0.0.0.0:9000".parse()?)
    .known_peer("quic.saorsalabs.com:9000".parse()?)
    .keypair(load_keypair()?)
    .build();

let node = Node::with_config(config).await?;
Source

pub fn peer_id(&self) -> PeerId

Get this node’s peer ID

The peer ID is derived from the Ed25519 public key and is the unique identifier for this node on the network.

Source

pub fn local_addr(&self) -> Option<SocketAddr>

Get the local bind address

Returns None if the endpoint hasn’t bound yet.

Source

pub fn external_addr(&self) -> Option<SocketAddr>

Get the observed external address

This is the address as seen by other peers on the network. Returns None if no external address has been discovered yet.

Source

pub fn direct_path_status(&self, peer_id: PeerId) -> Option<DirectPathStatus>

Return the latest best-effort direct-path status for a peer, when known.

Source

pub fn public_key_bytes(&self) -> &[u8]

Get the ML-DSA-65 public key bytes (1952 bytes)

Source

pub fn inner_endpoint(&self) -> &Arc<P2pEndpoint>

Get access to the underlying P2pEndpoint for advanced operations.

Source

pub fn transport_registry(&self) -> &TransportRegistry

Get the transport registry for this node

The transport registry contains all registered transport providers (UDP, BLE, etc.) that this node can use for connectivity.

Source

pub async fn connect_addr( &self, addr: SocketAddr, ) -> Result<PeerConnection, NodeError>

Connect to a peer by address.

Thin facade over P2pEndpoint::connect_addr, which uses the unified outbound connectivity orchestrator.

Source

pub async fn connect_peer( &self, peer_id: PeerId, ) -> Result<PeerConnection, NodeError>

Connect to a peer by durable peer ID.

Thin facade over the unified peer-oriented P2pEndpoint connect path. Strategy selection remains internal to the endpoint.

Source

pub async fn connect( &self, peer_id: PeerId, ) -> Result<PeerConnection, NodeError>

👎Deprecated:

use connect_peer(peer_id) for the canonical peer-oriented API

Connect to a peer by durable peer ID.

Compatibility-oriented alias retained for older callers. Prefer Self::connect_peer as the canonical peer-oriented public surface.

Source

pub async fn connect_peer_with_addrs( &self, peer_id: PeerId, addrs: Vec<SocketAddr>, ) -> Result<PeerConnection, NodeError>

Connect to a peer by durable peer ID plus explicit address hints.

Use this when the caller has candidate addresses for the peer and wants the transport to combine those hints with peer-authenticated fallback orchestration.

Source

pub async fn upsert_peer_hints( &self, peer_id: PeerId, addrs: Vec<SocketAddr>, capabilities: Option<PeerCapabilities>, )

Merge externally discovered peer hints into the node’s transport view.

This is the advanced discovery bridge for callers that learn peer addresses or assist-role capability hints from higher layers.

Source

pub async fn accept(&self) -> Option<PeerConnection>

Accept an incoming connection

Waits for and accepts the next incoming connection. Returns None if the node is shutting down.

§Example
while let Some(conn) = node.accept().await {
    println!("Accepted connection from: {:?}", conn.peer_id);
    // Handle connection...
}
Source

pub async fn add_peer(&self, addr: SocketAddr)

Add a known peer dynamically.

Thin facade over P2pEndpoint::add_known_peer. Known peers help with initial connectivity, discovery, and NAT traversal coordination.

Source

pub async fn connect_known_peers(&self) -> Result<usize, NodeError>

Connect to all known peers

Returns the number of successful connections.

Source

pub async fn disconnect(&self, peer_id: &PeerId) -> Result<(), NodeError>

Disconnect from a peer

Source

pub async fn connected_peers(&self) -> Vec<PeerConnection>

Get list of connected peers

Source

pub async fn is_connected(&self, peer_id: &PeerId) -> bool

Check if connected to a peer

Source

pub async fn connection_health(&self, peer_id: &PeerId) -> ConnectionHealth

Get a best-effort connection health snapshot for a peer.

Source

pub async fn connection_transport_stats( &self, peer_id: &PeerId, ) -> Option<ConnectionTransportStats>

Get qlog-style transport path telemetry for a connected peer.

Source

pub fn subscribe_peer_events( &self, peer_id: &PeerId, ) -> Receiver<PeerLifecycleEvent>

Subscribe to lifecycle events for a specific peer.

Source

pub fn subscribe_all_peer_events( &self, ) -> Receiver<(PeerId, PeerLifecycleEvent)>

Subscribe to lifecycle events for all peers.

Source

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

Send data to a peer

Source

pub async fn send_with_receive_ack( &self, peer_id: &PeerId, data: &[u8], timeout: Duration, ) -> Result<(), NodeError>

Send data and wait until the remote receive pipeline accepts it.

Source

pub async fn send_with_receive_ack_with_request_id( &self, peer_id: &PeerId, request_id: [u8; 16], data: &[u8], timeout: Duration, ) -> Result<(), NodeError>

Same as [send_with_receive_ack] but the caller supplies the ACK-v2 request id. Repeated calls with the same (peer_id, request_id, data) are duplicate-safe at the receiver — the second arrival is replayed from the receiver-side ACK dedupe cache and the payload is not redelivered to recv(). Intended for application-level request hedging (x0x X0X-0066).

Source

pub async fn probe_peer( &self, peer_id: &PeerId, timeout: Duration, ) -> Result<Duration, NodeError>

Actively probe peer liveness and measure round-trip time.

Sends a lightweight probe envelope and waits for the peer’s reader task to acknowledge it. Returns the measured round-trip duration on success. Probe traffic is invisible to Self::recv — it does not emit DataReceived events or deliver payloads.

Source

pub fn ack_diagnostics(&self) -> AckDiagnosticsSnapshot

Snapshot stage-by-stage ACK-v2 latency and outcome diagnostics.

Source

pub fn data_channel_diagnostics(&self) -> DataChannelDiagnosticsSnapshot

Snapshot data_tx channel saturation diagnostics (X0X-0039).

Surfaces depth, capacity, and cumulative high-water-count for the shared mpsc::Sender fed by every per-connection reader task. Consumed by x0x /diagnostics/connectivity to detect mesh-burst back-pressure.

Source

pub fn gso_diagnostics(&self) -> GsoDiagnosticsSnapshot

Snapshot GSO bundle send diagnostics (X0X-0043).

Returns cumulative counts of multi-segment GSO bundles submitted to the kernel send path and of bundles reported as partial / failed. Consumed by x0x /diagnostics/connectivity to test the Quinn issue #2627 GSO-tail-drop hypothesis as an alternative root cause for X0X-0030 idle-rot send timeouts. See crate::diagnostics::gso for the full discussion.

Source

pub async fn recv(&self) -> Result<(PeerId, Vec<u8>), NodeError>

Receive data from any peer

Source

pub async fn status(&self) -> NodeStatus

Get a snapshot of the node’s current status

This provides a practical snapshot of the node’s state, including a best-effort NAT behavior hint, connectivity, relay/coordinator hints, and performance.

§Example
let status = node.status().await;
println!("NAT behavior hint: {}", status.nat_type);
println!("Connected peers: {}", status.connected_peers);
println!("Acting as relay: {}", status.is_relaying);
Source

pub fn subscribe(&self) -> Receiver<NodeEvent>

Subscribe to node events

Returns a receiver for all significant node events including connections, disconnections, NAT detection, and relay activity.

§Example
let mut events = node.subscribe();
tokio::spawn(async move {
    while let Ok(event) = events.recv().await {
        match event {
            NodeEvent::PeerConnected { peer_id, .. } => {
                println!("Connected: {:?}", peer_id);
            }
            _ => {}
        }
    }
});
Source

pub fn subscribe_raw(&self) -> Receiver<P2pEvent>

Subscribe to raw P2pEvents (for advanced use)

This provides access to the underlying P2pEndpoint events. Most applications should use subscribe() for NodeEvents.

Source

pub async fn shutdown(self)

Gracefully shut down the node

This closes all connections and releases resources.

Source

pub fn is_running(&self) -> bool

Check if the node is still running

Trait Implementations§

Source§

impl Clone for Node

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Node

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Node

§

impl !RefUnwindSafe for Node

§

impl Send for Node

§

impl Sync for Node

§

impl Unpin for Node

§

impl UnsafeUnpin for Node

§

impl !UnwindSafe for Node

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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