What Nym cannot do
This page collects the reasons Nym might not fit your workload. Read it before you commit to an integration, while ruling Nym out is still cheap.
Everything below is either a default setting or a property of the design. No latency figures are quoted because none have been measured; where a number appears it comes from a configuration constant, and the source is named.
Is my workload a fit?
| You want to | Mixnet | dVPN | Why |
|---|---|---|---|
| Fetch a page, call an API, submit a transaction | Yes | Yes | Small independent requests are the strong case |
| Send messages between two parties | Yes | No | End to end, no exit gateway, and dVPN has no notion of a peer |
| Light-client sync: compact blocks, small repeated requests | Yes | Yes | Small and independent, even though there are many |
| Download a large file, sync a whole chain | No | Yes | Bulk transfer is the weakest case, see below |
| Stream video, run a voice or video call | No | Yes | Sustained throughput above what the default rate allows |
| Real-time gameplay, live position updates between players | No | No | Delay is the mechanism, and dVPN is not a peer transport |
| Anything needing a round-trip budget of well under a second | No | Maybe | Per-hop mixing delay is not tunable without removing the protection |
"dVPN" means dVPN mode, reached from Rust through
nym-smoldvpn. It hides your IP at line rate and splits
trust across two operators. It provides no in-transit timing protection, so
it is a different product, not a faster mixnet.
dVPN is a tunnel to a destination. It does not connect two peers to each other, so it does not rescue real-time traffic between two clients. If that is your workload, neither mode fits.
The default sending rate
A client paces its own sending on a Poisson schedule; the message queue page walks through how the two packet streams work. By default a client emits, per second:
- 50 real packets, one every 20 ms on average
- 5 loop cover packets, one every 200 ms on average
That is roughly 55 packets per second. A Sphinx packet carries a 2 KB payload,
so the ceiling is on the order of 100 KB/s. The stream runs constantly,
whether or not you have anything to say. The constants are
DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY and
DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY in
client-core (opens in a new tab).
All of these are configuration. A client built through the SDK can change every one of them. The consequences below hold whatever values you choose.
It costs the same when idle
When there is nothing real to send, the client sends cover packets instead: same size, same rate, same Sphinx processing. An idle service pays nearly the full bandwidth and compute bill of a busy one, continuously, for as long as it is up.
Capacity is a budget
Every reply to every client comes out of the same real-packet budget. One client can only serve so many peers. Measure your reply sizes and provision more mixnet clients when the budget runs out; it does not scale on its own.
Turning the pacing off has a cost
disable_main_poisson_packet_distribution and
disable_loop_cover_traffic_stream exist and will reduce the burden on your
machine. Without them the shape of your traffic starts to reflect your real
activity, which is the leak the pacing exists to close. If the default rate is
too slow for your workload, switch to dVPN mode instead of weakening the pacing.
Why bulk transfer is the weak case
There are two separate reasons, and the second one matters more.
The throughput ceiling above makes a large download slow.
The anonymity is also weaker. The mixnet is strongest for small, independent messages. A long sustained flow gives a network observer a great deal of correlated material to work with, and how far such flows can be correlated over time is an open question. Bulk transfer is therefore both the slowest workload and the one the protection covers least well.
Latency is the mechanism
Each mix node holds a packet for an average of 15 ms
(DEFAULT_AVERAGE_PACKET_DELAY) before forwarding it, and the path is five hops.
Reordering is what breaks the link between a packet entering the network and a
packet leaving it. Removing the delay removes the protection, so there is
nothing to tune.
This is why interactive workloads do not fit. The latency comes from the design, not from code that will get faster.
What the transport never does
Nym protects network-level metadata. It does not make an application private.
- Application-layer identity. Logins, session cookies, API tokens, account identifiers and the contents of a request reach the destination exactly as you sent them. See the two-layer model.
- Request patterns. What you ask for and when you ask for it is visible to the destination whatever the transport. Closing that is baseline hygiene, and it is your responsibility.
- A fixed exit is a linking key. Requests through one exit gateway stay linkable to each other at the destination. Rotate the exit per request to restore request unlinkability.
Your destination port may not be permitted
Every exit gateway applies the same Nym exit policy (opens in a new tab), a deny list agreed by operator governance. It decides which destination ports may leave the mixnet, it is identical across operators, and an individual operator cannot vary it.
Check it against the port your service listens on before anything else. It is the one constraint that no code on your side can work around. See exit security for which exit your package uses.
A refused port is not final: the policy can be changed through a governance proposal and an operator vote, described in the exit gateway community counsel.
There is no address discovery
This is deliberate: clients should not be enumerable, so there is no directory, no DHT and no lookup service for Nym addresses. Addresses are exchanged out of band. An application ships its service provider's address in its configuration instead of resolving it at runtime.
If your design assumes clients can find each other by name, that mechanism is yours to build.
If none of this rules you out
Start at choose a defence to pick a configuration from your threat model, then the developer front door to map that configuration to a package.