Muxio: A High-Performance Multiplexing and RPC Framework for Rust
DRAFT -- WORK IN PROGRESS
Muxio provides a robust and flexible foundation for building high-performance, transport-agnostic, and runtime-agnostic services in Rust. It offers a layered architecture that cleanly separates low-level binary stream multiplexing from high-level RPC logic, enabling you to create custom-tailored communication protocols.
What is Muxio?
At its core, Muxio is a set of layered components that enable multiplexed data transmission over a single, unified connection. Think of it as a toolkit for managing multiple, independent data streams, such as RPC calls, file transfers, and real-time data feeds—without the overhead of multiple connections.
On top of this multiplexing layer, Muxio offers a minimal, unopinionated RPC framework. While you can use it directly, it's often more practical to leverage the provided extensions, which offer ready-to-use solutions for common environments (such as tokio or WASM).
Key Features
-
Efficient Multiplexing: Muxio's foundational framing protocol can reliably manage numerous concurrent data streams over a single connection, correctly reassembling interleaved and out-of-order frames.
-
Minimalist RPC Layer: A lightweight RPC mechanism is provided on top of the framing layer, giving you the freedom to choose your own serialization formats, dispatching logic, and error-handling strategies.
-
Low-Overhead Binary Protocol: Muxio uses a compact binary framing protocol to minimize data transmission overhead, making it highly efficient for performance-sensitive applications. All communication, from frame headers to RPC payloads, is handled as raw bytes. The protocol defines a minimal header structure to keep data transfer lean.
-
Transport and Runtime Agnostic: The core logic uses a flexible, callback-driven design, enabling seamless adaptation across different environments. It supports both Tokio and standard library servers, as well as native and WASM clients, with or without Tokio.
-
Extensible by Design: Muxio comes with pre-built extensions that demonstrate how to integrate the core library into real-world applications:.
- Tokio-based WebSocket Server/Client: For native, multi-threaded environments.
- WASM-based Web Client: For seamless integration into web applications, communicating with a JavaScript host via a simple byte-passing bridge.
- Tokio-based IPC Server/Client: For local inter-process communication over Unix domain sockets or Windows named pipes.
How Muxio Compares
Minimal framing overhead: 17 bytes per frame (stream ID, sequence ID, frame kind, timestamp) vs HTTP/2's 9 bytes or gRPC's ~50+ bytes per protobuf message. For high-frequency small messages — keystrokes, mouse events, terminal output chunks — this overhead difference is significant.
Transport-agnostic core: The RpcServiceCallerInterface trait abstracts away the transport so the same application code works over WebSocket, Unix domain sockets, or WASM bridges without modification. Not many (if any) other Rust RPC frameworks offer WASM as a first-class transport.
FFI-friendly byte model: The core dispatcher receives and emits raw byte slices, making it straightforward to bridge to C, C++, Python, or JavaScript. The included WASM client demonstrates this pattern with #[wasm_bindgen].
No protobuf dependency: You choose your serialization — bitcode, bincode, manual encoding, or anything else. The framework handles framing and dispatch; it doesn't mandate a schema format.
Tradeoffs:
- No built-in backpressure or flow control. The write channel between encoder and transport I/O is unbounded by design — switching to a bounded channel without per-stream flow control (like HTTP/2
WINDOW_UPDATE) would cause head-of-line blocking. Under sustained producer > consumer load, memory can grow. Real applications should either size their chunks conservatively or implement application-level backpressure.
Note: The proper fix is likely per-stream byte budgets at the encoder. When a stream exceeds its budget,
write_bytespauses that stream's frames without blocking others going through the same channel. The framing layer already hasstream_idon every frame; what's missing is the budget tracking. Not implemented yet.
- No service discovery, load balancing, TLS, or auth. These are left entirely to the user. gRPC and Tonic ship them out of the box.
- Smaller ecosystem. Muxio has one primary author. Tonic/gRPC have broad adoption, protobuf tooling, interceptors, and reflection.
Other Notes:
Muxio is designed to be compiled into Rust first. Interop with other languages happens through FFI (PyO3 for Python, #[wasm_bindgen] for JavaScript, C ABI for other languages) — you embed the Rust core rather than reimplementing the protocol from a spec. This is the same model as libnghttp2 or libssl: the library is consumed as a compiled dependency, not implemented independently.
How Muxio Compares
Minimal framing overhead: 17 bytes per frame (stream ID, sequence ID, frame kind, timestamp) vs HTTP/2's 9 bytes or gRPC's ~50+ bytes per protobuf message. For high-frequency small messages — keystrokes, mouse events, terminal output chunks — this overhead difference is significant.
Transport-agnostic core: The RpcServiceCallerInterface trait abstracts away the transport so the same application code works over WebSocket, Unix domain sockets, or WASM bridges without modification. Not many (if any) other Rust RPC frameworks offer WASM as a first-class transport.
FFI-friendly byte model: The core dispatcher receives and emits raw byte slices, making it straightforward to bridge to C, C++, Python, or JavaScript. The included WASM client demonstrates this pattern with #[wasm_bindgen].
No protobuf dependency: You choose your serialization — bitcode, bincode, manual encoding, or anything else. The framework handles framing and dispatch; it doesn't mandate a schema format.
Core Use Cases & Design Philosophy
Muxio is engineered to solve specific challenges in building modern, distributed systems. Its architecture and features are guided by the following principles:
-
Low-Latency, High-Performance Communication: Muxio is built for speed. It uses a compact, low-overhead binary protocol (instead of text-based formats like JSON). This significantly reduces the size of data sent over the network and minimizes the CPU cycles needed for serialization and deserialization. By avoiding complex parsing, Muxio lowers end-to-end latency, making it well-suited for real-time applications such as financial data streaming, multiplayer games, and interactive remote tooling.
-
Cross-Platform Code with Agnostic Frontends: Write your core application logic once and deploy it across multiple platforms. Muxio achieves this through its generic
RpcServiceCallerInterfacetrait, which abstracts away the underlying transport. The same application code that calls an RPC method using the nativeRpcClientcan also be utilized in a browser with theRpcWasmClientwith minimal changes, while additional client types can also be added, provided they implement the same aformentionedRpcServiceCallerInterface. This design ensures that improvements to the core service logic benefit all clients simultaneously, even custom-built clients. -
Shared Service Definitions for Type-Safe APIs: Enforce integrity between your server and client by defining RPC methods, inputs, and outputs in a shared crate. By implementing the
RpcMethodPrebufferedtrait , both client and server depend on a single source of truth for the API contract. This completely eliminates a common class of runtime errors, as any mismatch in data structures between the client and server will result in a compile-time error. -
A Strong Foundation for Foreign Function Interfaces (FFI): The framework's byte-oriented design makes it an ideal foundation for bridging Rust with other languages. Because the core dispatcher only needs to receive and emit byte slices, you can easily create an FFI layer that connects Muxio to C, C++, Swift, or any language that can handle byte array (including Python). The included
muxio-wasm-rpc-clientserves as a perfect example, usingwasm_bindgento create a simple bridge between the Rust client and the JavaScript host environment.
Installation
For Muxio's core:
This provides the low-level functionality, but Muxio extensions are likely more desirable for most use cases.
WebSocket Usage Example
Let's build a simple sample app which spins up a Tokio-based WebSocket server, adds some routes, then spins up a client, performs some requests, then shuts everything down.
This example code was taken from the example-muxio-ws-rpc-app crate.
use ;
use ;
use ;
use Arc;
use join;
use TcpListener;
async
WASM WebSocket RPC
The WASM client follows a callback-driven pattern — the browser owns the WebSocket, and Rust is called on events. The static_lib module provides #[wasm_bindgen] exports that JS calls on onopen, onmessage, and onclose. The core RpcWasmClient implements the same RpcServiceCallerInterface used above, so calling methods like Add::call(...) works identically in the browser.
IPC Usage Example
The same application code works over Unix domain sockets or Windows named pipes via the IPC transport. Only the client and server types change — the service definitions (Add, Mult, Echo) are identical to the WebSocket example above.
This example code was taken from the example-muxio-ws-rpc-app crate.
use ;
use ;
use ;
use join;
async
Streaming RPC Example
Muxio supports streaming requests over any transport. Each stream is half-duplex: the sender writes chunks and ends the stream, then reads the single response. True bidirectional messaging is achieved with two independent concurrent streams (one per direction).
Streaming a request from the client
use StreamExt;
use RpcRequest;
use DynamicChannelType;
use ;
async
Streaming from the server to the client (server-initiated calls)
Any handle that implements RpcServiceCallerInterface — such as the
ConnectionContextHandle obtained from a ClientConnected event —
can initiate streaming calls:
use Error;
use RpcRequest;
use DynamicChannelType;
use RpcServiceCallerInterface;
use RpcServerEvent;
async
Concurrent bidirectional streaming
Streams are multiplexed over a single connection. Each stream is unidirectional, so bidirectional communication uses two independent streams without a separate connection per direction.
A single bidirectional stream would interleave both directions in one channel, coupling their backpressure, error handling, and lifecycle; two (or more) unidirectional streams keep each direction isolated and individually cancellable.
Single connection, multiple streams.
Client Server
│ │
├─ call_rpc_streaming ────────► │ stream A open
│ ◄──── call_rpc_streaming ─────│ stream B open
│ ── chunk A ─────────────────► │
│ ◄──── chunk B ─────────────── │ interleaved
│ ── chunk A ─────────────────► │ writes
│ ◄──── chunk B ─────────────── │
│ ... │
│ ── End A ───────────────────► │
│ ◄──── End B ───────────────── │
│ ◄──── response A (echo) ───── │
│ ── response B (echo) ───────► │
This is exactly what the concurrent_bidirectional_streaming integration test exercises. It spawns two tokio::spawn tasks that write chunks in opposite directions and yield between each chunk so the writes are truly interleaved at the application level, not buffered and sent in one burst per direction.
License
Licensed under the Apache-2.0 License.