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_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 public_key_bytes(&self) -> &[u8]

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

Source

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

Connect to a peer by address

This creates a direct connection to the specified address. NAT traversal is handled automatically if needed.

§Example
let conn = node.connect_addr("quic.saorsalabs.com:9000".parse()?).await?;
println!("Connected to: {:?}", conn.peer_id);
Source

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

Connect to a peer by ID

This uses NAT traversal to find and connect to the peer. A coordinator (known peer) is used to help with hole punching.

§Example
let conn = node.connect(peer_id).await?;
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

Known peers help with NAT traversal and peer discovery. You can add more peers at runtime.

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 send(&self, peer_id: &PeerId, data: &[u8]) -> Result<(), NodeError>

Send data to a peer

Source

pub async fn recv( &self, timeout: Duration, ) -> 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 complete visibility into the node’s state, including NAT type, connectivity, relay status, and performance.

§Example
let status = node.status().await;
println!("NAT type: {}", 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 · 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 !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