nomad-protocol

Crates.ionomad-protocol
lib.rsnomad-protocol
version0.2.0
created_at2025-12-25 11:58:19.405254+00
updated_at2025-12-26 10:56:26.745231+00
descriptionNOMAD Protocol - Network-Optimized Mobile Application Datagram. A secure UDP-based state synchronization protocol.
homepagehttps://github.com/DanEscher98/nomad-rs
repositoryhttps://github.com/DanEscher98/nomad-rs
max_upload_size
id2004468
size476,968
Danyiel Colin (DanEscher98)

documentation

https://docs.rs/nomad-protocol

README

NOMAD Protocol

Crates.io Documentation License CI

Network-Optimized Mobile Application Datagram

A secure, UDP-based state synchronization protocol designed for real-time applications over unreliable networks. Inspired by Mosh but redesigned from scratch with modern cryptography and a generic state synchronization framework.

Features

  • Security: End-to-end authenticated encryption using Noise_IK + XChaCha20-Poly1305
  • Mobility: Seamless IP address migration (WiFi ↔ cellular roaming)
  • Low Latency: Sub-100ms reconnection, optional client-side prediction
  • Simplicity: Fixed cryptographic suite, no negotiation complexity
  • Generality: Application-agnostic state synchronization framework

Quick Start

Add to your Cargo.toml:

[dependencies]
nomad-protocol = "0.1"

Define Your State Type

use nomad_protocol::prelude::*;

#[derive(Clone)]
struct GameState {
    players: Vec<Player>,
    score: u32,
}

#[derive(Clone)]
struct GameDiff {
    // Your diff representation
}

impl SyncState for GameState {
    type Diff = GameDiff;
    const STATE_TYPE_ID: &'static str = "com.example.game.v1";

    fn diff_from(&self, old: &Self) -> Self::Diff {
        // Compute idempotent diff
        todo!()
    }

    fn apply_diff(&mut self, diff: &Self::Diff) -> Result<(), ApplyError> {
        // Apply diff (must be idempotent)
        todo!()
    }

    fn encode_diff(diff: &Self::Diff) -> Vec<u8> {
        // Serialize for wire
        todo!()
    }

    fn decode_diff(data: &[u8]) -> Result<Self::Diff, DecodeError> {
        // Deserialize from wire
        todo!()
    }
}

Client Example

use nomad_protocol::prelude::*;

#[tokio::main]
async fn main() -> Result<(), NomadError> {
    // Connect to server
    let client = NomadClient::<GameState>::builder()
        .server_public_key(server_pubkey)
        .connect("game.example.com:19999")
        .await?;

    // Send state updates
    client.update_state(|state| {
        state.score += 10;
    }).await?;

    Ok(())
}

Server Example

use nomad_protocol::prelude::*;

#[tokio::main]
async fn main() -> Result<(), NomadError> {
    let server = NomadServer::<GameState>::builder()
        .private_key(server_privkey)
        .bind("0.0.0.0:19999")
        .await?;

    // Accept connections
    while let Some(session) = server.accept().await {
        tokio::spawn(async move {
            handle_session(session).await;
        });
    }

    Ok(())
}

Feature Flags

Feature Default Description
full Enable all features
client High-level client API
server High-level server API
compression zstd compression support
transport Transport layer
sync Sync layer

Minimal build (core + crypto only):

[dependencies]
nomad-protocol = { version = "0.1", default-features = false }

Protocol Overview

┌─────────────────────────────────────────────────────────────┐
│  APPLICATION     Your App (impl SyncState)                  │
├─────────────────────────────────────────────────────────────┤
│  EXTENSIONS      compression (zstd)                         │
├─────────────────────────────────────────────────────────────┤
│  SYNC LAYER      versioning • idempotent diffs • convergence│
├─────────────────────────────────────────────────────────────┤
│  TRANSPORT       frames • session ID • RTT • keepalive      │
├─────────────────────────────────────────────────────────────┤
│  SECURITY        Noise_IK • XChaCha20-Poly1305 • BLAKE2s    │
├─────────────────────────────────────────────────────────────┤
│  UDP             tokio::net::UdpSocket                      │
└─────────────────────────────────────────────────────────────┘

Cryptographic Suite

NOMAD uses a fixed cryptographic suite with no negotiation:

Purpose Algorithm
Key Exchange X25519 (Noise_IK pattern)
AEAD XChaCha20-Poly1305
Hash BLAKE2s-256
KDF HKDF-BLAKE2s

Performance Targets

Metric Target
Handshake < 1 RTT
Reconnection < 100ms
Frame rate 50 Hz max
Throughput > 10 MB/s

Crate Structure

This workspace publishes a single crate nomad-protocol that contains all functionality. Internal modules:

Module Description
core Core traits and constants
crypto Cryptographic primitives (Noise_IK, XChaCha20-Poly1305)
transport Frame encoding, RTT estimation, connection migration
sync State synchronization with idempotent diffs
extensions Optional extensions (compression)
client High-level async client API
server High-level async server API

Comparison with Mosh

Feature NOMAD Mosh
Encryption XChaCha20-Poly1305 AES-OCB
Key Exchange Noise_IK (1-RTT) Out-of-band (SSH)
State Types Generic (any) Terminal only
Rekeying Every 2 min No
Forward Secrecy Yes No
Protocol Version Extensible Fixed

Specification

The protocol specification is maintained separately. See the specs/ directory for:

  • 0-PROTOCOL.md - Overview and constants
  • 1-SECURITY.md - Cryptography and handshake
  • 2-TRANSPORT.md - Framing and timing
  • 3-SYNC.md - State synchronization
  • 4-EXTENSIONS.md - Optional extensions

License

Licensed under either of:

at your option.

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting PRs.

Acknowledgments

Commit count: 0

cargo fmt