Monocoque
A Rust-native ZeroMQ-compatible messaging runtime built on
io_uring
Monocoque is a ZeroMQ-compatible messaging library written in Rust. It implements ZMTP 3.1 from scratch on top of io_uring (via compio), so it interoperates with any existing libzmq peer while staying entirely within Rust's memory model.
The name comes from Formula 1 engineering, where the monocoque chassis achieves structural strength through form rather than bolt-on reinforcement. Same idea here: performance through correct architecture, not unsafe shortcuts.
Features
- All 11 ZeroMQ socket types: REQ, REP, DEALER, ROUTER, PUB, SUB, XPUB, XSUB, PUSH, PULL, PAIR
- PLAIN and CURVE (CurveZMQ/X25519) authentication, ZAP support
- TCP and IPC (Unix domain socket) transports
- Automatic reconnection with exponential backoff on all socket types
- ZMTP 3.1 heartbeating (PING/PONG) wired into all send/recv loops
- Socket monitoring via channel-based lifecycle events
- Explicit batching API for maximum throughput
- Zero-copy message passing via
Bytesrefcounting
Performance
Benchmarked against rust-zmq (FFI bindings to libzmq). REQ/REP round-trip latency on loopback TCP (Intel Core i7-1355U, Linux 6.17, release build):
| Message size | Monocoque | rust-zmq | Improvement |
|---|---|---|---|
| 64B | 7.3μs | 25.9μs | 72% faster |
| 256B | 7.3μs | 27.8μs | 74% faster |
| 1024B | 7.5μs | 25.6μs | 71% faster |
Throughput with the batching API reaches 2.5M+ msg/sec for small messages (2.97M at 64B, 1.23M at 1KB). IPC is about 35% faster than TCP loopback for local communication. See docs/performance.md for the full breakdown.
Quick Start
[]
= { = "0.1", = ["zmq"] }
= { = "0.13", = ["runtime"] }
use ;
// Connect a DEALER
let mut dealer = connect.await?;
dealer.send.await?;
let reply = dealer.recv.await?;
// Bind a ROUTER
let mut router = bind.await?;
let msg = router.recv.await?; // msg[0] is the routing identity
// PUB/SUB
let mut publisher = bind.await?;
publisher.send.await?;
let mut subscriber = connect.await?;
subscriber.subscribe.await?;
let msg = subscriber.recv.await?;
For high throughput, buffer messages and flush once:
for msg in &batch
dealer.flush.await?;
Safety
unsafe code is confined to a single file: monocoque-core/src/alloc.rs, which implements the arena allocator for io_uring-safe buffer management. Everything else is 100% safe Rust.
Memory invariants:
- Buffers are never reused while referenced (tracked via
Bytesrefcounts) SlabMut->Bytesis a one-way transition; no mutation after freeze- PUB fanout is refcount-based (
Bytes::clone()), never copies payloads
Development
Interop testing against libzmq: see docs/INTEROP_TESTING.md.
Roadmap
Core features are complete. Possible future work:
- io_uring fixed buffers (
IORING_OP_READ_FIXED) - removes the last kernel-boundary copy per read; ~5-15% latency improvement at an already low baseline - Prefix trie for topic matching - only relevant with 100+ concurrent subscribers using deep topic hierarchies
- Concurrent PUB fanout - prevents one slow subscriber from delaying others in large-subscriber deployments
Long term: high-performance RPC, additional transports (QUIC, shared memory), custom protocol framework.
License
MIT - see LICENSE.
Built with: compio, bytes, flume, smallvec