nexar

Library Listed

Distributed runtime that replaces MPI between nodes: point-to-point messages, 11 collectives and RPC over QUIC, with no mpirun or MPI library.

Checked · Sources

Downloads
1,191
Dependents
5
Stars
5
Forks
1
Contributors
2
Commits
111
Latest release
0.2.1 ·

About

From the project README ·

Distributed runtime for Rust. QUIC transport and 11 collectives, with no MPI.

Replaces MPI for inter-node communication. Point-to-point messages, collectives, and RPC run over QUIC without `mpirun` or an MPI library.

Docs · Crate · Collectives · Architecture · Example · Contributing

nexar handles the network layer for distributed Rust programs. It provides point-to-point transfers, collectives, elastic membership, and fault recovery. Workers find each other through a seed node and form a direct peer-to-peer mesh.

  • Language: the core nexar crate is Rust. It builds on quinn (QUIC), rustls, rkyv, and tokio. Only ring, a dependency of rustls, builds bundled C and assembly. There is no Python API.
  • Data types: F32, F64, F16, BF16, I8, I32, I64, U8, U32, U64. Reductions: Sum, Prod, Min, Max.
  • Hardware: the core crate moves host memory with CpuAdapter. nexar-rdma adds InfiniBand and RoCE through libibverbs, and GPU memory through GPUDirect. nexar-nccl uses NCCL inside a node and nexar between nodes.
  • Used by: boostr for distributed inference, behind its distributed feature.
Read the full README
Not supported yet
  • Python bindings.
  • GPU adapters other than CudaAdapter (NVIDIA, in nexar-rdma). The repository has no ROCm or Intel adapter.
  • GPU collectives without NCCL. nexar-nccl requires NCCL and CUDA.

Quick Start

Add the crates to your project:

cargo add nexar
cargo add tokio --features full

Start a 4-rank local cluster and run an in-place allreduce:

use nexar::{NexarClient, CpuAdapter, DataType, ReduceOp};
use std::sync::Arc;

#[tokio::main]
async fn main() -> nexar::Result<()> {
    let adapter = Arc::new(CpuAdapter::new());
    let clients = NexarClient::bootstrap_local(4, adapter).await?;

    // Each client holds rank 0..3 with connections to all peers.
    let client = &clients[0];
    assert_eq!(client.rank(), 0);
    assert_eq!(client.world_size(), 4);

    // Run allreduce in-place across all ranks.
    let mut data = vec![1.0f32; 1024];
    unsafe {
        client.all_reduce(
            data.as_mut_ptr() as u64,
            1024,
            DataType::F32,
            ReduceOp::Sum,
        ).await?;
    }

    Ok(())
}

The raw-pointer collective methods are unsafe. The caller must keep the buffer valid and sized for count elements of dtype until the call returns.

Multi-host deployment

bootstrap_local runs every rank in one process. For one process per rank, start rank 0 with bootstrap_seed and every other rank with bootstrap_join. Each host sets advertise_addr to its own reachable IP:

use nexar::{ClusterConfig, CpuAdapter, NexarClient};
use std::sync::Arc;

async fn join(is_seed: bool) -> nexar::Result<NexarClient> {
    let seed_addr = "10.0.0.1:7000".parse().unwrap();
    let cfg = ClusterConfig::new(4)
        .with_bind_addr("0.0.0.0:7001".parse().unwrap())
        .with_advertise_addr("10.0.0.2:7001".parse().unwrap())
        .with_cluster_token(std::env::var("NEXAR_CLUSTER_TOKEN").unwrap());
    let adapter = Arc::new(CpuAdapter::new());
    if is_seed {
        NexarClient::bootstrap_seed(seed_addr, &cfg, adapter).await
    } else {
        NexarClient::bootstrap_join(seed_addr, &cfg, adapter).await
    }
}

Formation runs in four steps:

  1. Each worker sends Hello to the seed. The seed assigns ranks and answers with Welcome: the rank and an mTLS certificate from an ephemeral cluster CA.
  2. Each rank binds its mesh listener and reports the address with MeshListen.
  3. The seed broadcasts the PeerTable once every rank reports. Each rank dials higher ranks and accepts lower ranks.
  4. Each rank enters the seed's ready barrier. The bootstrap returns once every rank is ready.

ClusterConfig fields:

FieldMeaning
world_sizeNumber of ranks. Every process uses the same value.
bind_addrLocal mesh listener. The port carries QUIC (UDP) and the TCP bulk sidecar.
advertise_addrAddress peers dial. Default: bind_addr. An unspecified IP becomes the IP the seed observes.
requested_rankRank this process asks for. Default: the seed assigns ranks in arrival order.
cluster_tokenShared secret. Default: NEXAR_CLUSTER_TOKEN.
join_timeoutLimit for the whole formation. Default: 60 s.
nexarNexarConfig of the returned client.

Set these on every host:

  • Seed address: every process uses the address the seed binds. Rank 0 binds it.
  • Token: the bootstrap connection does not verify the seed's certificate. The token is the only defense against a stranger who joins or poses as the seed. A bind, seed, or advertise address that is not loopback requires a token, on every SeedNode constructor too. Use the same secret on every process.
  • Firewall: open the seed port for UDP. Open each rank's mesh port for UDP (QUIC) and TCP (bulk sidecar). With bind_addr port 0, the ports are ephemeral. Fix the port to write a firewall rule.
  • NAT and containers: set advertise_addr to the address peers can reach. With a published container port, map the same port for UDP and TCP.
  • Sidecar: set the same enable_tcp_bulk_sidecar and encrypt_bulk_transport on every rank. A mismatch fails formation. Set max_frame_bytes (default 4 GiB) to the largest message you send; a longer frame closes the sidecar with FrameTooLarge.
Shutdown

Call client.shutdown().await on every rank after its last send and recv, before the process exits. Closing a QUIC connection discards data the peer has not acknowledged, so a rank that exits right after its last collective can lose that collective's final message.

shutdown flushes and closes the TCP sidecars, exchanges a DONE byte with every direct peer, and closes each connection once the peer is done too. A peer that dropped its client counts as done. After the barrier timeout, or the limit passed to shutdown_with_timeout, every connection closes anyway and the call returns ShutdownIncomplete with the peers that did not finish. A peer that answers with another byte, finishes its stream early, or resets it lands in ShutdownIncomplete::bad_signal.

shutdown takes the client by value. A client shared as Arc<NexarClient> needs every other holder dropped first, then Arc::into_inner.

Dropping the last client that shares a mesh closes its connections at once. Unlike shutdown, a drop can lose unacknowledged data.

A failure on one rank aborts formation on every rank with a typed error: RankConflict, ClusterTokenMismatch, ProtocolMismatch, ClusterFormationTimeout, JoinTimeout, or ConnectionFailed { rank }.

Collectives

NexarClient exposes 11 collectives. Each one has an _nb variant that returns a CollectiveHandle. Call wait on the handle to finish the operation.

OperationAlgorithm
all_reduceRing. Basic ring for small worlds, pipelined ring for large messages
broadcastFlat fan-out for small worlds, tree for larger worlds
all_gatherRing
reduce_scatterRing
reduceBinomial tree
all_to_allEach rank sends a distinct chunk to every other rank
gatherRoot receives from all peers concurrently
scatterRoot sends one chunk to each rank
scanHillis-Steele prefix, O(log N) rounds
exclusive_scanExclusive variant of scan
barrierTwo-phase below 5 ranks, dissemination at 5 or more ranks

The client also exposes all_reduce_host, all_reduce_device, and the other _host and _device variants. The _device variants take memory managed by a DeviceAdapter.

Features

  • Point-to-point: tagged send and recv between any two ranks. A receive takes only its own tag, in send order. See Receive semantics.
  • Elastic membership: rebuild_adding and rebuild_excluding change the communicator at run time.
  • Fault recovery: RecoveryPolicy::Automatic, Manual, or Abort. Healthy ranks agree on the failed set and rebuild.
  • Sparse topologies: TopologyStrategy::FullMesh, KRegular { degree }, and Hypercube. Traffic to non-neighbors relays through peers.
  • Compressed allreduce: TopKCompressor and random-K gradient compression.
  • RPC: register_rpc registers a handler by function ID. rpc and rpc_with_timeout call it on a rank. Responses match requests by request ID.
  • Device adapters: the DeviceAdapter trait stages device memory for network I/O. CpuAdapter ships with nexar. CudaAdapter ships with nexar-rdma behind the gpudirect feature.

Examples

Runnable examples live in nexar/examples/:

ExampleWhat it shows
send_recvPoint-to-point tagged send and recv between two ranks
allreduceRing allreduce (Sum) across 4 ranks
broadcastTree broadcast from root to all ranks
barrierBarrier synchronization with staggered arrivals
rpcRegister a remote function and call it across ranks
seed_workerOne process per rank: seed and join modes

Run an example from the repository root:

cargo run -p nexar --example send_recv
cargo run -p nexar --example allreduce

Architecture

seed node (discovery only, no data routing)
    │
    ├── worker 0 ──── worker 1
    │       \            /
    │        \          /
    │         worker 2 ── worker 3
    │              ...
    └── direct peer-to-peer mesh

Workers connect to the seed to get a rank, a certificate, and the peer table. They then open direct mTLS QUIC connections to their peers. The seed is not on the data path.

Each peer connection runs a router. The router accepts incoming QUIC streams and sends each message to the receiver of its key:

Receive keyTrafficOrder within the key
LaneKey::Data { comm_id, tag }Point-to-point send / recv, _iovSend order (FIFO)
LaneKey::Tagged { comm_id, tag }send_bytes_tagged / recv_bytes_taggedSend order (FIFO)
LaneKey::Raw { comm_id }send_bytes / recv_bytesSend order (FIFO)
ControlKey (barrier epoch, recovery)Barrier, recovery, elastic checkpointsNone; epochs keep apart
RPC requests, responses by request IDrpc calls and repliesNone
Receive semantics
  • A receive for (src, key) waits for a message with that key from src. It never takes, or fails on, a message with another key. That message stays queued for its own receiver.
  • Ordered: the messages of one byte lane from one sender, in the order their sends started on the connection. Each communicator (root, split, rebuilt) has its own tag space.
  • Not ordered: different keys, different senders, control messages within one key, and traffic that relays through other ranks in a sparse topology.
  • Each key queues NexarConfig::recv_queue_depth messages per peer (default 256). A message past that waits unread in its QUIC stream, and QUIC flow control slows its sender. Other keys from the same peer go on while the peer's open streams stay under the QUIC limit of 1024.
  • A send that fails or is cancelled does not stall the lane. The sender reports its sequence number in a skip notice, and later messages pass it.
  • When a peer disconnects, receivers first get the complete messages already queued, then NexarError::PeerDisconnected.
  • A message longer than NexarConfig::max_frame_bytes is refused before any allocation: the router stops its stream, logs FrameTooLarge, and the sender's write fails. The router of each peer reads at most NexarConfig::recv_inflight_bytes of payload at once (default 4 GiB). A message past that waits unread in its QUIC stream. A message longer than the budget reads alone.
  • The receiver refuses a sequence number 65536 or more past the lowest unknown one. A hostile peer cannot grow the reorder state.
  • Receives are cancel-safe: a receive dropped by a timeout consumes nothing.
  • The TCP bulk sidecar that collectives use is one ordered TCP stream per peer. A lagging tag on it delays the tags behind it.
Stream protocol

Each QUIC unidirectional stream starts with a 1-byte tag:

TagStream content
0x01Framed message: 8-byte length, then a serialized NexarMessage
0x05Sequenced bytes: lane kind (1 byte), comm_id (8), tag (8), sequence number (8), length (8), then the payload
0x06Skip notice: count (4 bytes), then that many 8-byte sequence numbers the sender abandoned

Integers are little-endian. One counter per connection numbers every sequenced message.

Messages use rkyv serialization.

Crates

CrateWhat it providesNative dependencies
nexarCore runtime: QUIC transport, collectives, RPCring builds bundled C and assembly
nexar-rdmaRDMA transport for InfiniBand and RoCElibibverbs (rdma-core)
nexar-rdma + gpudirectGPU memory to RDMA NIC, CudaAdapterlibibverbs and CUDA runtime
nexar-ncclHierarchical communicator: NCCL intra-node, nexar inter-nodeCUDA runtime and NCCL

Build each crate from the repository root:

cargo build -p nexar --release
cargo build -p nexar-rdma --release
cargo build -p nexar-rdma --release --features gpudirect
cargo build -p nexar-nccl --release

Requires Rust 1.89 or later. See CONTRIBUTING.md for test and lint commands.

When to Use nexar

Use nexar when:

  • You need collectives such as allreduce or broadcast across machines.
  • You want async, non-blocking communication in Rust.
  • You want to avoid MPI installation, mpirun, and C FFI.
  • You are building distributed ML training or inference.

Use NCCL directly when:

  • All GPUs sit in one machine. NCCL uses the local interconnect without a network hop.

Contributing

Contributions are welcome. See CONTRIBUTING.md for conventions and checks.

Questions and design discussion happen on Discord.

License

Licensed under Apache-2.0. See LICENSE.

Runs on
CUDA
What shows it
  • CUDA: nexar-nccl dependency cudarc, nexar-rdma feature gpudirect (dependency cudarc)
Native code
Required
  • Binds native code: ring
  • Build uses a native toolchain: bindgen, pkg-config
Python bindings
No Python binding crate found
License
Apache-2.0

Compare with similar projects

Order: strongest tier first, then most downloads, then name, with unmeasured values last. A library counts downloads over 30 days. An app counts binary downloads per release. An app with none counts its crate downloads over 30 days.

nexar beside the projects that share a topic with it
ProjectDownloadsDependentsStarsLatest releaseRuns on (GPU)Native codeLicense
nexar This page Listed

Distributed runtime that replaces MPI between nodes: point-to-point messages, 11 collectives and RPC over QUIC, with no mpirun or MPI library.

1,191 downloads over 30 days 5 5 0.2.1 CUDA Binds native code: ring. Build uses a native toolchain: bindgen, pkg-config Apache-2.0
boostr Listed

ML framework on numr for training and inference on CPU, CUDA and WebGPU: attention, quantization, inference scheduling and distributed execution. Models include LLaMA, Mamba2, Qwen3.5, Whisper, Kokoro, VITS, VoxCPM2, Silero VAD, CLAP and super-resolution. Loads SafeTensors and GGUF.

1,265 downloads over 30 days 7 20 0.11.2 CUDA, WebGPU Binds native code: ring (feature distributed) Apache-2.0
tboard-rs Listed Dormant

Write and read tensorboard files.

Python bindings

445 downloads over 30 days 3 23 0.1.1 GPU support unknown No native code found Apache-2.0 OR MIT
prophet Listed Dormant

A neural network implementation with a focus on cache-efficiency and sequential performance.

87 downloads over 30 days 0 41 0.4.2 GPU support unknown No native code found Apache-2.0 OR MIT
rust-lstm Listed

A complete LSTM neural network library with training capabilities, multiple optimizers, and peephole variants.

15 downloads over 30 days 0 24 0.10.0 GPU support unknown No native code found MIT
hal Listed Dormant

HAL: a machine learning library that is able to run on Nvidia, OpenCL or CPU BLAS based compute backends. It currently provides stackable classical neural networks, RNN's and soon to be LSTM's.

2 downloads over 30 days 0 92 0.2.0 GPU support unknown No native code found BSD-3-Clause
Why ListedNo archive, New or Featured rule places it, so it is Listed.

What it lacks for Featured

  • It has 0 active dependents and 0 new dependents in 365 days. Featured needs 11 active dependents or 9 new dependents in 365 days. The repository route reads 5 stars, 1 fork and 2 contributors. It needs 4,539 stars, 434 forks and 79 contributors together.

What else the check found

  • The last activity is on 6 Nov 2026, 3 days ago. The rules expect an update within 365 days. Dormancy rule

Exact rules: Listed, Featured library. The check on applied thresholds version 9.

Every rule outcome, as the check records it
  • expected-interval: version 0.2.1 base 300, capped at 5 releases = 250, raised to the 365-day floor: E = 365 days
  • dormancy: last activity 2026-11-06, 3 days ago, E = 365: active
  • featured: not featured: active_users 0 < 11 and new_adopters 0 < 9 and repository route stars 5 (needs 4539), forks 1 (needs 434), contributors 2 (needs 79)
  • listed: listed: no archive, new or featured rule applies
Crates3 crates

Measurements and sources

Each signal row shows the raw value, its source and the fetch date. The Sources list gathers every distinct source on this page. Movement needs more history.

Sources6 sources
Adoption0 active dependents, 0 new dependents in 365 days1 not measured
Adoption measurements
SignalValueSourceFetchedCounts
Active dependents0crates.io database dumpYes
New dependents (365 days)0crates.io database dumpYes
Peak active dependents0crates.io database dumpYes
Active dependents as a share of the peak1crates.io database dumpYes
Binary downloads per releaseNot measuredYes
MaintenanceLatest human commit: 3 days ago3 not measured
Maintenance measurements
SignalValueSourceFetchedCounts
Latest human commit2026-11-06GitHub repositoryYes
Latest release2026-11-06crates.io database dumpYes
First release2026-03-14crates.io database dumpYes
Latest version0.2.1crates.io database dumpYes
Days the oldest outside issue waits for a reply0GitHub repositoryYes
Maintainer replies over 365 days0GitHub repositoryYes
Repository archivedfalseGitHub repositoryYes
Repository gonefalseGitHub repositoryYes
Release commit or manifest in the repotruecrates.io packageYes
Published docstruecrates.io database dumpYes
Latest release fails cargo checkNot measuredYes
Build check dateNot measuredYes
Build check toolchainNot measuredYes
SecurityUnpatched vulnerabilities: none
Security measurements
SignalValueSourceFetchedCounts
Unpatched vulnerabilities past the grace windownoneRustSec advisory databaseYes
RustSec unmaintained advisorynoneRustSec advisory databaseYes
Deprecation noticenonecrates.io database dumpYes
Deprecated requirementnonecrates.io database dumpYes
Distribution5 releases, 1,191 downloads over 30 days5 not measured
Distribution measurements
SignalValueSourceFetchedCounts
Releases5crates.io database dumpYes
Downloads over 30 days1191crates.io database dumpYes
Binary downloads over the last 3 releasesNot measuredYes
Homebrew installs on request over 365 daysNot measuredYes
Repology repository familiesNot measuredYes
Docker pull growth over 90 daysNot measuredYes
Docker pulls at the growth baselineNot measuredYes
Every version a prereleasefalsecrates.io database dumpYes
Every version 0.0.xfalsecrates.io database dumpYes
Every version yankedfalsecrates.io database dumpYes
Crate deleted from crates.iofalsecrates.io database dumpYes
Repository5 stars, 1 forks, 2 contributors, 111 commits. Stars alone never qualify a project.
Repository measurements
SignalValueSourceFetchedCounts
GitHub stars5GitHub repositoryYes — one part of the repository route
GitHub forks1GitHub repositoryYes — one part of the repository route
Contributors with a GitHub account2GitHub contributorsYes — one part of the repository route
Commits on the default branch111GitHub repositoryNo
Repository's main crate left out of the entrynonecrates.io database dumpYes
Facts that never count
Facts that never count measurements
SignalValueSourceFetchedCounts
LicenseApache-2.0crates.io database dumpNo
Runs on, from listed crates and their direct dependenciesCUDAcrates.io database dumpNo
What shows each backendCUDA: nexar-nccl dependency cudarc, nexar-rdma feature gpudirect (dependency cudarc)crates.io database dumpNo
Native code, from listed crates and their dependenciesBinds native code: ring. Build uses a native toolchain: bindgen, pkg-configcrates.io database dumpNo
Python bindings, from direct dependencies of listed cratesNo Python binding crate foundcrates.io database dumpNo
Crates that depend on a listed crate directly5crates.io database dumpNo

Corrections and review history

Report a correction

No correction applies to this entry.

Judgments

A judgment enters only through an approved issue and a merged pull request.

No judgment applies to this entry.