Service providers
A service provider is a third party reachable through the mixnet — not Nym infrastructure, but a bridge from it to external systems that need no awareness of Nym. Wallet requests arrive as Sphinx packets carrying pre-computed reply routes (SURBs), so the provider can answer without ever learning who asked.
The fourth participant
The Nym whitepaper describes three kinds of node that make up the Nym infrastructure — validators, gateways, and mix nodes. A service provider is the fourth kind of participant, and it is not infrastructure at all: it is a third party that makes itself reachable through the mixnet, acting as an interface between Nym and external systems that need no modification — or even awareness — of Nym. The whitepaper’s own example is a provider that anonymously relays Bitcoin transactions to the Bitcoin peer-to-peer network; swap in Zcash and lightwalletd and you have exactly the broadcast path this site recommends.
From the provider’s perspective, it is not possible to identify which user is behind any request. It sees Sphinx packets arriving from the mixnet’s last layer — never a client IP, never a client address.
Forward: the wallet's request travels client → entry → three mix layers → exit → service provider as Sphinx packets. The reply rides a SURB — a pre-computed, sender-encrypted return route bundled with the request — back through the mixnet, so the provider never learns the client address.
Replying without knowing who asked
Requests arrive as ordinary Sphinx packets, but each carries something extra: a bundle of single-use reply blocks (SURBs) the client pre-computed before sending. A SURB is an encrypted Sphinx header describing a return route that ends at the client — the provider attaches its reply payload to one and hands it back to the mixnet. Because the SURB is encrypted by the sender, the provider can read nothing from it: not the route, not the per-hop timing, not the client’s address. Each SURB works exactly once, and reply packets are indistinguishable from forward packets, so requests and replies blend into one anonymity set.
A service provider in Rust
The receive-and-reply loop is small. The sketch below is illustrative — the shape matches the nym-sdk examples (see surb_reply.rs), which are the source of truth for the real API. An example service provider will soon land there too, under examples/service-providers/.
use nym_sdk::mixnet::{AnonymousSenderTag, MixnetClient, MixnetMessageSender};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// A service provider is an ordinary mixnet client: it dials OUT to its
// gateway and pulls messages from it — no inbound connections needed,
// so it can sit in a DMZ behind a deny-all firewall.
let mut client = MixnetClient::connect_new().await?;
println!("service provider listening on {}", client.nym_address());
while let Some(messages) = client.wait_for_messages().await {
for msg in messages.into_iter().filter(|m| !m.message.is_empty()) {
// The AnonymousSenderTag is an opaque token derived from the
// SURBs bundled with the request. It lets us reply WITHOUT
// ever learning the sender's address.
let return_to: AnonymousSenderTag = match msg.sender_tag {
Some(tag) => tag,
None => continue, // no SURBs attached — cannot reply
};
// Execute the request against the local backend, e.g. a
// lightwalletd SendTransaction gRPC call. The RPC sees only
// this process — never the wallet that asked.
let response = handle_grpc_request(&msg.message).await?;
// The reply rides a pre-computed SURB back through the mixnet.
client.send_reply(return_to, response).await?;
}
}
Ok(())
}Addressing: how clients reach you
Clients reach a service provider by its Nym address — the value the sketch above prints at startup. It has three parts, <user-identity-key>.<user-encryption-key>@<gateway-identity-key>: a base58-encoded Ed25519 identity key that routes to the client, the public key that encrypts the final Sphinx layer, and the identity of the gateway that holds the client’s messages.
That last component has an operational consequence: the gateway identity is baked into the address, so the address is only as stable as the gateway behind it. To have a stable address you need a stable gateway — one that is always part of the network. If the gateway disappears, every copy of your address stops working.
There is deliberately no discovery system for Nym addresses — clients should not be enumerable. Addresses are expected to be distributed out-of-band: a wallet would ship its service provider’s address in its configuration, not look it up.
Real world performance of mixnet mode on the Nym network
client-core. With ~2 KB Sphinx packets that is on the order of 100 KB/s at the very most — sent constantly, whether or not there is anything real to say. All of these numbers are configuration, not physics: a client built through the SDK (as in the sketch above) can override every one of them.You can reduce the burden on your machine by turning off the Poisson pacing and the loop cover stream (disable_main_poisson_packet_distribution, disable_loop_cover_traffic_stream), but this comes at a privacy cost: without them, the shape of your traffic starts to reflect your real activity.
Use-case: the forked lightwalletd
The forked-lightwalletd reference architecture is this pattern made concrete: a lightwalletd fork embeds a Nym service provider, receives wallet requests over the mixnet, executes the gRPC calls locally — a SendTransaction broadcast, say — and replies via SURBs. The RPC endpoint never learns which client made the request, because no client address ever reaches it.
Every reply packet consumes one SURB, so this suits transaction broadcasts and small queries — not bulk block sync, which the recommended architecture carries over dVPN instead.