# NOMAD Protocol
[](https://crates.io/crates/nomad-protocol)
[](https://docs.rs/nomad-protocol)
[](LICENSE-MIT)
[](https://github.com/DanEscher98/nomad-rs/actions)
**N**etwork-**O**ptimized **M**obile **A**pplication **D**atagram
A secure, UDP-based state synchronization protocol designed for real-time applications over unreliable networks. Inspired by [Mosh](https://mosh.org/) 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`:
```toml
[dependencies]
nomad-protocol = "0.1"
```
### Define Your State Type
```rust
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
```rust
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
```rust
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
| `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):
```toml
[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**:
| Key Exchange | X25519 (Noise_IK pattern) |
| AEAD | XChaCha20-Poly1305 |
| Hash | BLAKE2s-256 |
| KDF | HKDF-BLAKE2s |
## Performance Targets
| 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:
| `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
| 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:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
## Contributing
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
## Acknowledgments
- [Mosh](https://mosh.org/) - Original inspiration for state synchronization over UDP
- [WireGuard](https://www.wireguard.com/) - Inspiration for clean cryptographic design
- [Noise Protocol Framework](https://noiseprotocol.org/) - Key exchange pattern