New: a threat-model-first guide to choosing your network defence, plus the nym-smoldvpn dVPN package and nym-swizzle sender hygiene.
Developers
Key Concepts
Exit Security

Exit security

Every tool that reaches an external service through the Nym mixnet shares one exit security model. The Exit Gateway sees your destination and any payload you did not encrypt yourself. It does not see who you are. Exit Gateway Services documents that model once: what each hop sees, and the trust it implies. This page is the developer-facing half. It covers which exit your package uses, how to pin one, and what your application is responsible for.

Tools where both ends run a Nym client (nym-sdk, the TypeScript SDK) never exit the mixnet at all. Traffic stays Sphinx-encrypted from your client to the peer's client. There is no Exit Gateway and no clearnet hop, so nothing on this page applies. No exit exists to trust, and no payload is exposed to one. For the threat-model verdicts on that mode, see end to end.

Which exit your package uses

Exit Gateways run two proxy services, and which one carries your traffic is decided by the package, not by configuration.

PackageExit serviceWhat the exit sees
smolmixIP Packet RouterDestination IP and port
mix-tunnel and everything built on it: mix-fetch, mix-dns, mix-websocketIP Packet RouterDestination IP and port
SOCKS5 module and the standalone nym-socks5-clientNetwork RequesterDestination hostname and port with socks5h://, an IP otherwise. Plus your Nym address, unless anonymous replies are on

Two practical differences follow.

The first is DNS. The IPR works at the network layer and never learns the hostname you resolved. The NR resolves DNS for you, so it sees the name, but only with socks5h://. With plain socks5:// your client resolves first and the lookup goes out over clearnet.

The second is identity. The default SOCKS5 configuration sends replies to a return address. The NR therefore receives your Nym address, which is stable across sessions and names your Entry Gateway. Turn on anonymous replies (--use-anonymous-replies on the standalone client) to withhold it. See the exit trust model for why that matters.

Check your destination is reachable

Every exit applies the same Nym exit policy (opens in a new tab), a deny list agreed by operator governance and identical across nodes. It decides which destination ports may leave the mixnet at all, so it constrains an integration before anything else does: if the port your service listens on is not permitted, no amount of configuration on your side helps.

The list is maintained outside these docs, so check the current version against your destination port rather than any copy of it. An individual operator cannot vary it.

If the policy refuses a port you need, the route is a governance proposal. It is decided by an operator vote, and the exit gateway community counsel describes the process.

Protect the payload

The Exit Gateway strips the Sphinx layers, so whatever is inside is visible to it unless your application encrypted it. This is your job, not the mixnet's.

  • Use https:// rather than http://, and wss:// rather than ws://. Over TLS the exit only ever handles ciphertext bound for that destination. Plain HTTP, plain WebSocket frames, and plain UDP DNS are readable in full at the exit.
  • Do not rely on the mixnet for application-layer anonymity. Logging in, sending a cookie, or including an API token identifies you to the destination whatever the network path. See what the exit does not protect against.

Per-transport specifics (where TLS terminates, what the resolver sees, wss:// vs ws://) are on each package page: smolmix, the SOCKS5 module, mix-fetch, mix-dns, and mix-websocket.

Pin an exit

By default a client discovers an exit automatically. If the exit operator or its jurisdiction matters to you, pin one. The control depends on the exit service:

Pinning trades availability for predictability. A pinned exit that is offline fails rather than falling back, so pin when the operator matters and leave discovery on when it does not.

IPR-backed, browser and wasm packages. Pass preferredIpr to setupMixTunnel:

await setupMixTunnel({
  preferredIpr: 'D1rrUqJY9pesL3pTaMaxLnpZGGYQ4ZpZwpQXCqaeBXTW.6PpFkRvF...',
});

IPR-backed, native smolmix. Construct the tunnel with an explicit IPR recipient instead of letting it auto-discover:

let ipr: Recipient = "D1rrUqJY...@6PpFkRvF...".parse()?;
let tunnel = Tunnel::new_with_ipr(ipr).await?;

Network Requester, SOCKS5 module. Select a requester and pass it to connect_with:

// a specific requester, by its Nym address
let selector = NetworkRequesterSelector::exact("D1rrUqJY...@6PpFkRvF...")?;
 
// or constrain the jurisdiction to ISO 3166 alpha-2 country codes
let selector = NetworkRequesterSelector::in_countries(["CH", "DE"])?;
 
// the second argument is the local SOCKS5 bind address; None takes the default
let client = Socks5MixnetClient::connect_with(selector, None).await?;

Cover traffic and Poisson timing

The mixnet's timing protection against network observers comes from mixing delays, Poisson sending, and cover traffic. The browser and wasm packages expose switches to turn the last two off. That trades timing protection for latency and bandwidth:

  • disableCoverTraffic and disablePoissonTraffic on mix-tunnel and the feature packages built on it.

The native smolmix crate does not expose them. Configure the mixnet client yourself and pass it to Tunnel::from_stream. Turning either off changes nothing about what the exit sees. It weakens you against a network observer, the only actor the mixing protects you from.

Read more

  • Exit Gateway Services: the full model, what each hop sees, and the trust the exit implies.
  • The two-layer model: why hiding your identity from the destination and disciplining what your requests leak are separate problems.
  • Nym vs other systems: how this compares with VPNs, Tor, I2P, and end-to-end encryption.