Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Network

The Network is the primary entry point to the mosaik SDK. It creates the QUIC transport layer, establishes node identity, and composes all subsystems (Discovery, Streams, Groups) into a single cohesive runtime.

Creating a Network

Quick Start

For prototyping, Network::new() creates a node with default settings:

use mosaik::{Network, NetworkId};

let network = Network::new("my-app".into()).await?;

This creates a node with:

  • A random secret key (new identity each run)
  • Default relay mode (iroh’s relay servers for NAT traversal)
  • No bootstrap peers
  • No tags
  • Default configs for all subsystems

Builder Pattern

For production use, the builder provides full control:

use mosaik::{Network, NetworkId, discovery, streams, groups};
use iroh::SecretKey;

let network = Network::builder("my-app".into())
    .with_secret_key(my_secret_key)
    .with_relay_mode(iroh::RelayMode::Disabled)
    .with_mdns_discovery(true)
    .with_addresses(bind_addrs)
    .with_discovery(
        discovery::Config::builder()
            .with_bootstrap(bootstrap_addr)
            .with_tags("my-role")
            .with_purge_after(Duration::from_secs(600))
    )
    .with_streams(
        streams::Config::builder()
    )
    .with_groups(
        groups::Config::builder()
    )
    .build()
    .await?;

Builder Options

MethodTypeDefaultDescription
with_secret_key()SecretKeyRandomNode identity (determines PeerId)
with_relay_mode()RelayModeDefaultNAT traversal via relay servers
with_mdns_discovery()boolfalseLocal network mDNS peer discovery
with_addresses()BTreeSet<SocketAddr>EmptyExplicit bind addresses
with_discovery()ConfigBuilderDefaultsDiscovery subsystem configuration
with_streams()ConfigBuilderDefaultsStreams subsystem configuration
with_groups()ConfigBuilderDefaultsGroups subsystem configuration

Accessing Subsystems

Once built, the Network provides access to all subsystems:

// Transport & identity
let local = network.local();
let peer_id = network.local().id();
let addr = network.local().addr();
let network_id = network.network_id();

// Subsystems
let discovery = network.discovery();
let streams = network.streams();
let groups = network.groups();

All handles are cheap to clone (Arc<Inner> internally).

LocalNode

LocalNode represents the local node’s transport and identity:

let local = network.local();

// Identity
let peer_id = local.id();          // Public key
let secret = local.secret_key();   // Secret key
let network_id = local.network_id();

// Address (for sharing with others)
let addr = local.addr();  // EndpointAddr: public key + relay URL + addrs

// Readiness
local.online().await;  // Blocks until the endpoint is ready

// Low-level access to iroh endpoint
let endpoint = local.endpoint();

Waiting for Readiness

After building, you can wait for the node to be fully online:

network.online().await;

This resolves once the iroh endpoint is ready and all subsystem handlers are installed. The build() method already waits for this, so online() is mainly useful when you have a cloned network handle.

Lifecycle & Shutdown

When a Network is dropped, it cancels the internal CancellationToken, which propagates shutdown to all subsystems:

{
    let network = Network::new(network_id).await?;
    // Node is running...
} // Network dropped here → all tasks cancelled

For long-running services, keep the network handle alive:

let network = Network::new(network_id).await?;
// ... set up streams, groups, etc.
core::future::pending::<()>().await;  // Block forever

Under the hood, all communication happens through Link<P> — a typed, framed, bidirectional QUIC stream:

pub struct Link<P: Protocol> { /* ... */ }

Links provide:

  • Length-delimited framing via LengthDelimitedCodec
  • Serialization via postcard (compact binary format)
  • Type safety via the Protocol trait and generic parameter
// Open a link to a remote peer
let link = Link::<MyProtocol>::open(local, remote_addr).await?;

// Send and receive typed messages
link.send(MyMessage { /* ... */ }).await?;
let response: MyResponse = link.recv().await?;

// Split into independent halves
let (sender, receiver) = link.split();

// Close with a typed reason
link.close(MyCloseReason::Success).await?;

Most users won’t use Link directly — it’s the building block that Streams, Groups, and Discovery use internally.

Error Handling

Network construction can fail with:

ErrorCause
MissingNetworkIdNetwork ID not provided
Bind(BindError)Failed to bind the QUIC endpoint
InvalidAddressInvalid socket address in configuration
DiscoveryConfig(...)Invalid discovery configuration
StreamsConfig(...)Invalid streams configuration
GroupsConfig(...)Invalid groups configuration

Connection-level errors use typed close reasons:

CodeNameMeaning
200SuccessProtocol completed normally
204GracefulShutdownClean shutdown
100InvalidAlpnWrong protocol identifier
101DifferentNetworkPeer on a different network
102CancelledOperation cancelled
400ProtocolViolationMessage deserialization failed
401UnknownPeerPeer not in discovery catalog