Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions documentation/docs/pages/developers/clients/socks5.mdx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Socks5 Client (Standalone)

> This client can also be utilised via the [Rust SDK](/developers/rust).
> This proxy is also available embedded in a Rust application via the [nym-sdk SOCKS5 module](/developers/rust/socks5).

Many existing applications are able to use either the SOCKS4, SOCKS4A, or SOCKS5 proxy protocols. If you want to send such an application's traffic through the mixnet, you can use the `nym-socks5-client` to bounce network traffic through the Nym network, like this:
Many existing applications are able to use either the SOCKS4, SOCKS4a, or SOCKS5 proxy protocols. If you want to send such an application's traffic through the mixnet, you can use the `nym-socks5-client` to bounce network traffic through the Nym network, like this:

```mermaid
---
Expand Down
82 changes: 43 additions & 39 deletions documentation/docs/pages/developers/concepts/exit-security.mdx

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion documentation/docs/pages/developers/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The table below maps those two answers to a package.

| Runtime | End-to-end (both sides run Nym) | Proxy (exit to clearnet) |
|---|---|---|
| **Native Rust** (desktop / CLI / server) | [`nym-sdk`](/developers/rust): Mixnet, Stream, Client Pool | [`smolmix`](/developers/smolmix): `TcpStream` / `UdpSocket` · [`nym-sdk` SOCKS](/developers/rust) |
| **Native Rust** (desktop / CLI / server) | [`nym-sdk`](/developers/rust): Mixnet, Stream, Client Pool | [`smolmix`](/developers/smolmix): `TcpStream` / `UdpSocket` · [`nym-sdk` SOCKS5](/developers/rust/socks5) |
| **Browser / WebView** (JS + WASM) | [TypeScript SDK](/developers/typescript): `@nymproject/sdk` raw messaging | [`mix-fetch`](/developers/mix-fetch) HTTP/S · [`mix-dns`](/developers/mix-dns) DNS · [`mix-websocket`](/developers/mix-websocket) WS/WSS |

<Callout type="info">
Expand Down
3 changes: 2 additions & 1 deletion documentation/docs/pages/developers/rust.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ For an overview of what the SDK can do, see the **[Tour](./rust/tour)**. For set
|---|---|---|
| [**Stream**](./rust/stream) | Multiplexed `AsyncRead + AsyncWrite` byte streams over the Mixnet, the closest analogue to TCP sockets. | Recommended |
| [**Mixnet**](./rust/mixnet) | Raw message payloads, independently routed, no connections or ordering. Full control over the communication model. | Stable |
| [**SOCKS5**](./rust/socks5) | Local SOCKS5 proxy that routes any SOCKS-capable application through the Mixnet to a Network Requester (proxy mode, exits to clearnet). | Stable |
| [**Client Pool**](./rust/client-pool) | Keeps ready-to-use `MixnetClient` instances warm for bursty workloads. | Stable |
| [**TcpProxy**](./rust/tcpproxy) | TCP socket proxying with session management and message ordering. | Deprecated |
| [**FFI**](./rust/ffi) | Go and C/C++ bindings. | Stable |
Expand All @@ -50,4 +51,4 @@ For an overview of what the SDK can do, see the **[Tour](./rust/tour)**. For set
For proxy-mode integrations (reaching third-party services through an Exit Gateway), see also:

- [**`smolmix`**](/developers/smolmix): `TcpStream` and `UdpSocket` over the Mixnet via a userspace IP stack. Compatible with `tokio-rustls`, `hyper`, `tokio-tungstenite`, and the rest of the async Rust ecosystem.
- [**SOCKS Client**](./rust/mixnet): SOCKS4/4a/5 proxy via the Exit Gateway's Network Requester. Works with any SOCKS-capable application without code changes.
- [**SOCKS5 module**](./rust/socks5): SOCKS4/4a/5 proxy via the Exit Gateway's Network Requester. Works with any SOCKS-capable application without code changes.
1 change: 1 addition & 0 deletions documentation/docs/pages/developers/rust/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"importing": "Installation",
"mixnet": "Mixnet Module",
"stream": "Stream Module",
"socks5": "SOCKS5 Module",
"tcpproxy": "TcpProxy Module (Deprecated)",
"client-pool": "Client Pool Module",
"ffi": "FFI"
Expand Down
2 changes: 1 addition & 1 deletion documentation/docs/pages/developers/rust/mixnet.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Nym Rust SDK: Mixnet Messaging Module"
description: "Use the Nym Rust SDK Mixnet module to send messages through the mixnet. Covers builder patterns, custom topologies, SOCKS proxy, and anonymous replies."
description: "Use the Nym Rust SDK Mixnet module to send messages through the mixnet. Covers builder patterns, custom topologies, and anonymous replies."
schemaType: "TechArticle"
section: "Developers"
lastUpdated: "2026-03-13"
Expand Down
133 changes: 133 additions & 0 deletions documentation/docs/pages/developers/rust/socks5.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
title: "Nym Rust SDK: SOCKS5 Proxy Module"
description: "Use the Nym Rust SDK SOCKS5 module to route any SOCKS4/4a/5-capable application through the Mixnet. Covers Socks5MixnetClient, the socks5h proxy URL, automatic Network Requester discovery, country-based selection, and a reqwest example."
schemaType: "TechArticle"
section: "Developers"
lastUpdated: "2026-06-29"
---

# SOCKS5 Module

import { Callout } from 'nextra/components'

The `socks5` module provides [`Socks5MixnetClient`](https://docs.rs/nym-sdk/latest/nym_sdk/mixnet/struct.Socks5MixnetClient.html): a local SOCKS5 proxy that routes any SOCKS4, SOCKS4a, or SOCKS5-capable application's traffic through the Mixnet. Your application connects to a standard SOCKS5 proxy on `localhost`; the client forwards that traffic over the Mixnet to a [**Network Requester**](/network/infrastructure/exit-services#network-requester) running on an Exit Gateway, which makes the real request on your behalf.

<Callout type="warning">
This is a **proxy-mode** integration, not end-to-end. Unlike the [Mixnet](./mixnet) and [Stream](./stream) modules (where both sides run a Nym client), traffic here leaves the Mixnet at an Exit Gateway and continues to the destination over the public internet. The Mixnet anonymises the sender; protecting the payload (TLS) is your application's job. See [Exit security](/developers/concepts/exit-security) for the full model: what each hop sees, the trust boundaries, and how it compares with Tor and VPNs.
</Callout>

## How it works

```text
Your machine
Application (reqwest, curl, a browser, ...)
│ SOCKS5 (socks5h://127.0.0.1:1080)
Socks5MixnetClient (local listener + MixnetClient)
│ Sphinx packets
Entry Gateway → 3 mix layers → Exit Gateway
│ Network Requester
▼ makes the real request
Destination (clearnet)
```

The client chops the TCP stream into Sphinx packets, sends them through the Mixnet, and the Network Requester at the exit reassembles the stream and performs the request, returning the response the same way. The application never has to know the Mixnet exists.

## Quick example

You need the Nym address of a **Network Requester** (a service running on an Exit Gateway), or you can let the SDK discover one ([Automatic discovery](#automatic-discovery)). Point an HTTP client at the SOCKS5 URL the client exposes, and every request travels through the Mixnet:

```rust
use nym_sdk::mixnet::Socks5MixnetClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
nym_bin_common::logging::setup_tracing_logger();

// Connect to a Network Requester service provider over the Mixnet.
// This opens a local SOCKS5 listener (default 127.0.0.1:1080).
let client = Socks5MixnetClient::connect_new("provider_nym_address...").await?;

// Point any SOCKS5-capable HTTP client at the proxy URL.
let proxy = reqwest::Proxy::all(client.socks5_url())?;
let http = reqwest::Client::builder().proxy(proxy).build()?;

// This request now travels through the Mixnet and exits at the requester.
let body = http.get("https://nymtech.net").send().await?.text().await?;
println!("{body}");

client.disconnect().await;
Ok(())
}
```

`reqwest` must be built with its `socks` feature enabled, or `Proxy::all` will reject the `socks5h://` URL at runtime. The `setup_tracing_logger()` call is optional logging from the `nym-bin-common` crate; drop it if you do not want the dependency.

<Callout type="info">
`socks5_url()` returns a **`socks5h://`** URL, not `socks5://`. The trailing `h` tells the HTTP client to hand the hostname to the proxy and let the Network Requester resolve DNS at the exit, rather than resolving locally and leaking the destination through a local DNS query.
</Callout>

To combine the SOCKS5 proxy with other builder options (persistent keys via `StoragePaths`, custom configuration), attach a `Socks5` config to `MixnetClientBuilder` instead of using `connect_new`:

```rust
use nym_sdk::mixnet;

let socks5_config = mixnet::Socks5::new("provider_nym_address...".to_string());
let client = mixnet::MixnetClientBuilder::new_ephemeral()
.socks5_config(socks5_config)
.build()?
.connect_to_mixnet_via_socks5()
.await?;
```

## Automatic discovery

You do not have to hardcode a Network Requester address. [`connect_with`](https://docs.rs/nym-sdk/latest/nym_sdk/mixnet/struct.Socks5MixnetClient.html#method.connect_with) takes a `NetworkRequesterSelector` describing how to pick one plus an optional listener address, and connects in a single call. There are three selectors; the third, country-restricted discovery, has [its own section below](#selecting-by-country):

```rust
use nym_sdk::mixnet::{NetworkRequesterSelector, Socks5MixnetClient};

// Any available requester, weighted by performance (None = default 127.0.0.1:1080):
let client = Socks5MixnetClient::connect_with(NetworkRequesterSelector::any(), None).await?;

// A specific requester you already know:
let client = Socks5MixnetClient::connect_with(NetworkRequesterSelector::exact("address...")?, None).await?;
```

For `any`, discovery queries the mainnet directory for Exit Gateways that advertise a Network Requester and selects one weighted by performance. Discovery always queries mainnet, regardless of the network the client itself is configured for.

### Selecting by country

To constrain where your traffic leaves the Mixnet, build the requester with [ISO 3166 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes. The chosen requester must have declared a location in one of them (see the warning below):

```rust
use nym_sdk::mixnet::{NetworkRequesterSelector, Socks5MixnetClient};

let requester = NetworkRequesterSelector::in_countries(["CH", "DE"])?; // Switzerland or Germany

// Listen on 127.0.0.1:1081 instead of the default 1080:
let client = Socks5MixnetClient::connect_with(requester, Some("127.0.0.1:1081".parse()?)).await?;
```

Codes are case-insensitive and validated up front, so a typo fails immediately rather than at connect time. Passing more than one code means "any of these".

<Callout type="warning">
A requester's location is **self-reported by its operator and optional**. Requesters that have not declared a location are excluded once you set a country filter, so a narrow filter can return `NoGatewayInCountries` even when requesters exist there but have not advertised one. Discovery does not silently fall back to another country: an empty result is an error, since routing through an unintended jurisdiction would defeat the point of asking.
</Callout>

## SDK module or standalone binary

The same proxy logic ships two ways. They are interchangeable on the wire; choose by how you want to run it:

| | Use it when |
|---|---|
| **`socks5` module** (this page) | You want the proxy embedded in a Rust application and managed in-process, alongside other SDK clients. |
| [**Standalone `nym-socks5-client`**](/developers/clients/socks5) | You want a language-agnostic local proxy binary that any application (in any language) can point at, with no code changes. |

## Further reading

- [API reference on docs.rs](https://docs.rs/nym-sdk/latest/nym_sdk/mixnet/struct.Socks5MixnetClient.html): all methods, configuration, and types
- [Example: SOCKS5 proxy](https://github.com/nymtech/nym/blob/develop/sdk/rust/nym-sdk/examples/socks5.rs): select a requester (auto-discover, pin by country, or a known address) and proxy a request

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Avoid linking versioned docs to develop.

This page is being merged independently of the SDK rollout, so a develop-branch example can drift from the API documented here and confuse readers later. Prefer a tagged permalink or another version-stable target.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@documentation/docs/pages/developers/rust/socks5.mdx` at line 129, The SOCKS5
example link currently points to the mutable develop branch, which can drift
from the documented Rust API. Update the link in the socks5.mdx page to use a
version-stable target, such as a tagged permalink or another immutable
reference, so the example stays aligned with the docs. Use the existing
“Example: SOCKS5 proxy” link text as the anchor and replace only the
branch-based GitHub target.

- [Standalone SOCKS5 client](/developers/clients/socks5): the language-agnostic binary form
- [Exit security](/developers/concepts/exit-security): what an Exit Gateway can observe in proxy mode
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Because it operates at the application layer, the NR:
- Can enforce allow/deny lists on destination hosts and ports
- Sees the destination hostname and port, but not the contents if TLS is used

**Used by:** the [SDK's SOCKS client](/developers/rust/mixnet), [standalone SOCKS5 client](/developers/clients/socks5), and [mixFetch](/developers/mix-fetch) (which wraps SOCKS requests in a browser-friendly `fetch` API).
**Used by:** the [SDK's SOCKS5 module](/developers/rust/socks5) and the [standalone SOCKS5 client](/developers/clients/socks5).

## IP Packet Router

Expand All @@ -59,7 +59,7 @@ Because it operates at the IP layer, the IPR:
In both services, traffic between the Exit Gateway and the destination travels over the public internet, exactly as it would from any other server. The mixnet protects sender anonymity (the destination sees the gateway's IP, not yours), but does not encrypt the payload past the gateway. Use TLS or another application-layer cipher to protect payload confidentiality, just as you would on a direct connection.
</Callout>

**Used by:** [NymVPN anonymous mode](/network/dvpn-mode/protocol) (5-hop mixnet routing to the IPR), and [`smolmix`](/developers/smolmix) (programmatic `TcpStream`/`UdpSocket` access to the IPR via the Rust SDK).
**Used by:** [NymVPN anonymous mode](/network/dvpn-mode/protocol) (5-hop mixnet routing to the IPR), [`smolmix`](/developers/smolmix) (programmatic `TcpStream`/`UdpSocket` access to the IPR via the Rust SDK), and the browser `mix-*` packages built on `smolmix`: [`mix-tunnel`](/developers/mix-tunnel), [`mix-fetch`](/developers/mix-fetch), [`mix-dns`](/developers/mix-dns), and [`mix-websocket`](/developers/mix-websocket).

## Comparison

Expand All @@ -70,7 +70,7 @@ In both services, traffic between the Exit Gateway and the destination travels o
| **DNS** | Resolved by the NR | Client resolves its own |
| **Client gets** | Proxied connections | An allocated IP address |
| **Connection model** | Per-request | Persistent tunnel |
| **Used by** | SDK SOCKS client, mixFetch | NymVPN (anonymous mode), smolmix |
| **Used by** | SDK SOCKS5 module, standalone SOCKS5 client | NymVPN (anonymous mode), smolmix, mix-* packages |

## Trust model

Expand Down
Loading