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
impl Node
Sourcepub async fn bind(addr: SocketAddr) -> Result<Self, NodeError>
pub async fn bind(addr: SocketAddr) -> Result<Self, NodeError>
Sourcepub async fn with_peers(peers: Vec<SocketAddr>) -> Result<Self, NodeError>
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?;Sourcepub async fn with_keypair(
public_key: MlDsaPublicKey,
secret_key: MlDsaSecretKey,
) -> Result<Self, NodeError>
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?;Sourcepub async fn with_host_identity(
host: &HostIdentity,
network_id: &[u8],
storage_dir: impl AsRef<Path>,
) -> Result<Self, NodeError>
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 derivationnetwork_id- Network identifier for per-network keypair isolationstorage_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?;Sourcepub async fn with_config(config: NodeConfig) -> Result<Self, NodeError>
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?;Sourcepub fn peer_id(&self) -> PeerId
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.
Sourcepub fn local_addr(&self) -> Option<SocketAddr>
pub fn local_addr(&self) -> Option<SocketAddr>
Get the local bind address
Returns None if the endpoint hasn’t bound yet.
Sourcepub fn external_addr(&self) -> Option<SocketAddr>
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.
Sourcepub fn direct_path_status(&self, peer_id: PeerId) -> Option<DirectPathStatus>
pub fn direct_path_status(&self, peer_id: PeerId) -> Option<DirectPathStatus>
Return the latest best-effort direct-path status for a peer, when known.
Sourcepub fn public_key_bytes(&self) -> &[u8] ⓘ
pub fn public_key_bytes(&self) -> &[u8] ⓘ
Get the ML-DSA-65 public key bytes (1952 bytes)
Sourcepub fn inner_endpoint(&self) -> &Arc<P2pEndpoint>
pub fn inner_endpoint(&self) -> &Arc<P2pEndpoint>
Get access to the underlying P2pEndpoint for advanced operations.
Sourcepub fn transport_registry(&self) -> &TransportRegistry
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.
Sourcepub async fn connect_addr(
&self,
addr: SocketAddr,
) -> Result<PeerConnection, NodeError>
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.
Sourcepub async fn connect_peer(
&self,
peer_id: PeerId,
) -> Result<PeerConnection, NodeError>
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.
Sourcepub async fn connect(
&self,
peer_id: PeerId,
) -> Result<PeerConnection, NodeError>
👎Deprecated: use connect_peer(peer_id) for the canonical peer-oriented API
pub async fn connect( &self, peer_id: PeerId, ) -> Result<PeerConnection, NodeError>
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.
Sourcepub async fn connect_peer_with_addrs(
&self,
peer_id: PeerId,
addrs: Vec<SocketAddr>,
) -> Result<PeerConnection, NodeError>
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.
Sourcepub async fn upsert_peer_hints(
&self,
peer_id: PeerId,
addrs: Vec<SocketAddr>,
capabilities: Option<PeerCapabilities>,
)
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.
Sourcepub async fn accept(&self) -> Option<PeerConnection>
pub async fn accept(&self) -> Option<PeerConnection>
Sourcepub async fn add_peer(&self, addr: SocketAddr)
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.
Sourcepub async fn connect_known_peers(&self) -> Result<usize, NodeError>
pub async fn connect_known_peers(&self) -> Result<usize, NodeError>
Connect to all known peers
Returns the number of successful connections.
Sourcepub async fn disconnect(&self, peer_id: &PeerId) -> Result<(), NodeError>
pub async fn disconnect(&self, peer_id: &PeerId) -> Result<(), NodeError>
Disconnect from a peer
Sourcepub async fn connected_peers(&self) -> Vec<PeerConnection>
pub async fn connected_peers(&self) -> Vec<PeerConnection>
Get list of connected peers
Sourcepub async fn is_connected(&self, peer_id: &PeerId) -> bool
pub async fn is_connected(&self, peer_id: &PeerId) -> bool
Check if connected to a peer
Sourcepub async fn connection_health(&self, peer_id: &PeerId) -> ConnectionHealth
pub async fn connection_health(&self, peer_id: &PeerId) -> ConnectionHealth
Get a best-effort connection health snapshot for a peer.
Sourcepub async fn connection_transport_stats(
&self,
peer_id: &PeerId,
) -> Option<ConnectionTransportStats>
pub async fn connection_transport_stats( &self, peer_id: &PeerId, ) -> Option<ConnectionTransportStats>
Get qlog-style transport path telemetry for a connected peer.
Sourcepub fn subscribe_peer_events(
&self,
peer_id: &PeerId,
) -> Receiver<PeerLifecycleEvent>
pub fn subscribe_peer_events( &self, peer_id: &PeerId, ) -> Receiver<PeerLifecycleEvent>
Subscribe to lifecycle events for a specific peer.
Sourcepub fn subscribe_all_peer_events(
&self,
) -> Receiver<(PeerId, PeerLifecycleEvent)>
pub fn subscribe_all_peer_events( &self, ) -> Receiver<(PeerId, PeerLifecycleEvent)>
Subscribe to lifecycle events for all peers.
Sourcepub async fn send(&self, peer_id: &PeerId, data: &[u8]) -> Result<(), NodeError>
pub async fn send(&self, peer_id: &PeerId, data: &[u8]) -> Result<(), NodeError>
Send data to a peer
Sourcepub async fn send_with_receive_ack(
&self,
peer_id: &PeerId,
data: &[u8],
timeout: Duration,
) -> Result<(), NodeError>
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.
Sourcepub async fn send_with_receive_ack_with_request_id(
&self,
peer_id: &PeerId,
request_id: [u8; 16],
data: &[u8],
timeout: Duration,
) -> Result<(), NodeError>
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).
Sourcepub async fn probe_peer(
&self,
peer_id: &PeerId,
timeout: Duration,
) -> Result<Duration, NodeError>
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.
Sourcepub fn ack_diagnostics(&self) -> AckDiagnosticsSnapshot
pub fn ack_diagnostics(&self) -> AckDiagnosticsSnapshot
Snapshot stage-by-stage ACK-v2 latency and outcome diagnostics.
Sourcepub fn data_channel_diagnostics(&self) -> DataChannelDiagnosticsSnapshot
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.
Sourcepub fn gso_diagnostics(&self) -> GsoDiagnosticsSnapshot
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.
Sourcepub async fn status(&self) -> NodeStatus
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);Sourcepub fn subscribe(&self) -> Receiver<NodeEvent>
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);
}
_ => {}
}
}
});Sourcepub fn subscribe_raw(&self) -> Receiver<P2pEvent>
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.
Sourcepub async fn shutdown(self)
pub async fn shutdown(self)
Gracefully shut down the node
This closes all connections and releases resources.
Sourcepub fn is_running(&self) -> bool
pub fn is_running(&self) -> bool
Check if the node is still running