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

165 lines
6.2 KiB
Plaintext

# Tour of the Rust SDK
import { Callout } from 'nextra/components'
A quick walkthrough of the most important things you can do with `nym-sdk`. Each section shows working code and links to the module that covers it in depth.
<Callout type="warning">
The Mixnet is not like regular internet networking. There are no persistent connections, no guaranteed message ordering, and no TCP underneath. At its core, the Mixnet is a message-based anonymity network: you send individual payloads that are Sphinx-encrypted, mixed through multiple nodes, and independently reconstructed at the destination.
The raw [message API](./mixnet) therefore works differently from what most developers expect. The [Stream module](./stream) bridges this gap by providing `AsyncRead + AsyncWrite` byte streams on top of the Mixnet. If you are coming from socket-based networking, start with streams.
</Callout>
## Send a raw message payload
The message API gives you direct access to the Mixnet's native communication model: individually addressed payloads with no connections and no ordering guarantees. This is useful when you want full control, but it's not how most networking code works:
```rust
use nym_sdk::mixnet::{self, MixnetMessageSender};
#[tokio::main]
async fn main() {
let mut client = mixnet::MixnetClient::connect_new().await.unwrap();
let addr = *client.nym_address();
println!("Connected: {addr}");
// Send a message to ourselves
client
.send_plain_message(addr, "hello mixnet!")
.await
.unwrap();
// Receive it (filter empty SURB management messages)
if let Some(msgs) = client.wait_for_messages().await {
for msg in msgs.iter().filter(|m| !m.message.is_empty()) {
println!("Got: {}", String::from_utf8_lossy(&msg.message));
}
}
// Always disconnect for clean shutdown
client.disconnect().await;
}
```
The message is Sphinx-encrypted, mixed across 5 nodes, and reconstructed on arrival. The whole round trip takes a few seconds.
Next: [Mixnet module](./mixnet) | [Tutorial: Send Your First Private Message](./mixnet/tutorial)
## Reply anonymously with SURBs
Every received message carries a `sender_tag`, an opaque token that lets you reply **without knowing the sender's Nym address**. Replies travel back through pre-built Single Use Reply Blocks (SURBs):
```rust
// After receiving a message...
let tag = received_msg.sender_tag.expect("message includes sender tag");
client.send_reply(tag, "anonymous reply!").await.unwrap();
```
The replying side never learns where the reply is going, enabling anonymous communication without mutual identity disclosure.
## Open a bidirectional stream
If you're used to working with TCP sockets, this is where you'll feel at home. The [Stream module](./stream) provides persistent, bidirectional byte channels that implement tokio's `AsyncRead + AsyncWrite`, so any code that works with sockets works with `MixnetStream`:
```rust
use nym_sdk::mixnet;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() {
let mut sender = mixnet::MixnetClient::connect_new().await.unwrap();
let mut receiver = mixnet::MixnetClient::connect_new().await.unwrap();
let recv_addr = *receiver.nym_address();
// Receiver creates a listener (activates stream mode)
let mut listener = receiver.listener().unwrap();
// Sender opens a stream to the receiver
let mut out = sender.open_stream(recv_addr, None).await.unwrap();
// Receiver accepts it
let mut inc = listener.accept().await.unwrap();
// Standard tokio I/O: write, flush, read
out.write_all(b"hello stream").await.unwrap();
out.flush().await.unwrap();
let mut buf = vec![0u8; 1024];
let n = inc.read(&mut buf).await.unwrap();
println!("{}", String::from_utf8_lossy(&buf[..n]));
drop(out);
drop(inc);
sender.disconnect().await;
receiver.disconnect().await;
}
```
<Callout type="info">
Activating stream mode (by calling `listener()` or `open_stream()`) disables message-based methods like `send_plain_message()` and `wait_for_messages()`. A single client operates in one mode at a time.
</Callout>
Next: [Stream module](./stream) | [Tutorial: Build a Private Echo Server](./stream/tutorial)
## Use a client pool for bursty traffic
Creating a `MixnetClient` takes several seconds (gateway handshake, key generation, topology fetch). The [Client Pool](./client-pool) pre-creates clients in the background so they're ready when you need them:
```rust
use nym_sdk::client_pool::ClientPool;
#[tokio::main]
async fn main() {
let pool = ClientPool::new(3); // maintain 3 clients in reserve
let bg = pool.clone();
tokio::spawn(async move { bg.start().await });
// Wait for pool to fill, then grab a ready client
tokio::time::sleep(std::time::Duration::from_secs(15)).await;
if let Some(client) = pool.get_mixnet_client().await {
println!("Got client: {}", client.nym_address());
client.disconnect().await;
}
pool.disconnect_pool().await;
}
```
Clients are consumed, not returned; the pool creates replacements automatically.
Next: [Client Pool module](./client-pool) | [Tutorial: Handle Bursty Traffic](./client-pool/tutorial)
## Persist your identity
By default, `connect_new()` creates ephemeral keys that are discarded on disconnect. To keep the same Nym address across restarts, use the builder with on-disk storage:
```rust
use nym_sdk::mixnet::{MixnetClientBuilder, StoragePaths};
use std::path::PathBuf;
let storage = StoragePaths::new_from_dir(
&PathBuf::from("/tmp/my-nym-client")
).unwrap();
let client = MixnetClientBuilder::new_with_default_storage(storage)
.await
.unwrap()
.build()
.unwrap()
.connect_to_mixnet()
.await
.unwrap();
// This address is the same every time you run with the same path
println!("Persistent address: {}", client.nym_address());
```
## Where to go next
- [Installation](./importing): add `nym-sdk` to your project
- [Mixnet Tutorial](./mixnet/tutorial): send, receive, and reply with SURBs
- [Stream Tutorial](./stream/tutorial): build a private echo server
- [Client Pool Tutorial](./client-pool/tutorial): handle bursty traffic
- [API Reference on docs.rs](https://docs.rs/nym-sdk/latest/nym_sdk/): type details, method signatures, architecture docs