Files
mfahampshire a70e68c7bd Max/smolmix docs (#6716)
* Smolmix documentation

* Add smolmix docs: landing page, tutorials, and developer page links

* Add Exit Gateway services page (NR vs IPR) and link from existing docs

* Update auto-generated command and API outputs

* Reorg of tutorials and architecture pages

* License information + remove TODO from docs.rs visibile comment + reorg
readme

* Add versions file for doc-wide versioning

* Relative -> absolute links

* Relative -> absolute links

* Update license + add old tutorial code as examples

* Streamline smolmix docs

* Clippy

* Clean up doc comments

* Last pass

* Add larger file download to list

* set new versions

* Clippy

* Remove blake pin from docs + add version range to root Cargo.toml

* Format example logging

* Remove crate blocked component

* Loose whitespace

* Add doc verification script for inline mdx

* Formatting

* Components regen

* Reorg + tighten text

* Voicing cohesion pass + remove bloated examples

* Voicing cont.

* Reduce max download size

* Small suggested clarifications

* Max/docs voicing consistency (#6769)

* Reduce max download size

* voicing consistency across docs

* New landing order w smolmix

* Tweaks

* Final tweaks
2026-05-13 11:19:44 +00:00

3.5 KiB

smolmix

TCP/UDP tunnel over the Nym mixnet. Uses a userspace network stack (smoltcp) to provide real TcpStream and UdpSocket types that work with the async Rust ecosystem: tokio-rustls, hyper, tokio-tungstenite, libp2p, and anything else built on AsyncRead + AsyncWrite.

Why IP, not messages

The Nym SDK works at the message layer: you send and receive Vec<u8> payloads through the mixnet. Every protocol has to be hand-adapted, with custom framing, ordering, connection state, and flow control.

smolmix operates at the IP layer. A userspace smoltcp stack manages real TCP state machines (retransmits, windowing, port allocation) and UDP datagram delivery, and the mixnet becomes the transport underneath. Any protocol that works over TCP or UDP works over smolmix without adaptation.

┌──────────────────────────────────────────────────────────────────┐
│  Application protocols that "just work" over smolmix             │
│                                                                  │
│  ┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌────────────────┐   │
│  │ TLS      │ │ HTTP/1.1 │ │ WebSocket    │ │ libp2p         │   │
│  │ (rustls) │ │ (hyper)  │ │ (tungstenite)│ │ (noise+yamux)  │   │
│  └────┬─────┘ └────┬─────┘ └──────┬───────┘ └───────┬────────┘   │
│       │             │              │                 │           │
│       └─────────────┴──────────────┴─────────────────┘           │
│                             │                                    │
│                   tokio_smoltcp::TcpStream                       │
│               (AsyncRead + AsyncWrite, Send, Unpin)              │
├──────────────────────────────────────────────────────────────────┤
│                     smolmix Tunnel                               │
│                   (smoltcp → mixnet → IPR)                       │
└──────────────────────────────────────────────────────────────────┘

Quick start

use smolmix::Tunnel;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

let tunnel = Tunnel::new().await?;

// Raw TCP, works with any protocol
let mut tcp = tunnel.tcp_connect("1.1.1.1:80".parse()?).await?;
tcp.write_all(b"GET / HTTP/1.1\r\nHost: 1.1.1.1\r\nConnection: close\r\n\r\n").await?;

// Raw UDP, datagrams over the mixnet
let udp = tunnel.udp_socket().await?;
udp.send_to(&packet, "1.1.1.1:53".parse()?).await?;

Examples

cargo run -p smolmix --example tcp         # HTTPS via hyper
cargo run -p smolmix --example udp         # DNS via hickory-proto
cargo run -p smolmix --example websocket   # WebSocket via tungstenite

Architecture

See core/src/ARCHITECTURE.md for the internal stack (smoltcp, device adapter, bridge, mixnet client).