Client Configuration Reference
Complete reference for all fields in client.toml.
Top-level Fields
Section titled “Top-level Fields”| Field | Type | Default | Description |
|---|---|---|---|
server_address | string | "wss://localhost:443/api/v1/ws" | Full WebSocket URL of the server. Must include scheme (wss://), hostname, port, and path. |
sni_hostname | string | (from URL) | Override the TLS SNI hostname sent during the handshake. Useful when connecting by IP address or CDN. |
identity_key_file | path | "identity.key" | Path to your client identity key. Generated with rvpn keygen. |
prekey_bundle | path | (none) | Path to the server’s prekey-bundle.json. Required for first-time setup. |
server_public_key | string | (none) | Hex-encoded server public key. Alternative to prekey_bundle when you only have the raw key. |
tls_fingerprint | string | "chrome" | TLS ClientHello fingerprint to mimic. Options: "chrome", "firefox", "safari", "none". Use "chrome" for best DPI resistance. |
data_dir | path | (platform default) | Directory for runtime data (known hosts, stats). Defaults to ~/.local/share/rvpn/ on Linux, ~/Library/Application Support/rvpn/ on macOS. |
[socks5]
Section titled “[socks5]”SOCKS5 proxy settings. These only apply when running in SOCKS5 mode (the default).
| Field | Type | Default | Description |
|---|---|---|---|
listen_address | string | "127.0.0.1:1080" | Address and port to accept SOCKS5 connections on. Use "0.0.0.0:1080" to share the proxy with other devices on your network. |
udp_associate | bool | true | Enable SOCKS5 UDP ASSOCIATE command (for UDP-based apps). |
auth_enabled | bool | false | Require username/password authentication for SOCKS5 clients. |
auth_username | string | (none) | Username when auth_enabled = true. |
auth_password | string | (none) | Password when auth_enabled = true. |
multiplex | bool | false | Use a single multiplexed WebSocket for all connections. When enabled, uses 0-RTT flow creation for lower latency. Default false — one WebSocket per connection creates a traffic pattern that blends in with normal browsing. See Connection Modes. |
mux_path | string | (auto) | Override the mux WebSocket endpoint path. Only used when multiplex = true. Defaults to {server_path}/mux. |
[socks5]listen_address = "127.0.0.1:1080"auth_enabled = trueauth_username = "alice"auth_password = "hunter2"[http_proxy]
Section titled “[http_proxy]”HTTP/HTTPS proxy that runs alongside the SOCKS5 proxy. Disabled by default. Enables use of HTTP_PROXY/HTTPS_PROXY environment variables for system-wide or per-tool VPN routing. Both proxies share the same connection pool — running both simultaneously adds zero overhead.
Handles two request types:
- HTTP CONNECT — used for HTTPS. The client sends
CONNECT host:443 HTTP/1.1, the proxy establishes an encrypted tunnel to the destination. - Plain HTTP forwarding — used for unencrypted HTTP. The proxy connects to the host and forwards the request.
Both paths are split-tunnel-aware and use the same multiplexed WebSocket tunnel as SOCKS5.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Start the HTTP proxy alongside the SOCKS5 proxy. |
listen_address | string | "127.0.0.1:8118" | Address and port to accept HTTP proxy connections on. |
auth_enabled | bool | false | Require Basic authentication for HTTP proxy clients. |
auth_username | string | (none) | Username when auth_enabled = true. |
auth_password | string | (none) | Password when auth_enabled = true. |
multiplex | bool | false | Use a single multiplexed WebSocket. Default false is recommended — see Connection Modes. |
mux_path | string | (auto) | Override the mux WebSocket endpoint path. Only used when multiplex = true. Defaults to {server_path}/mux. |
[http_proxy]enabled = truelisten_address = "127.0.0.1:8118"With authentication:
[http_proxy]enabled = truelisten_address = "127.0.0.1:8118"auth_enabled = trueauth_username = "user"auth_password = "changeme"See HTTP proxy setup for usage with environment variables and per-app configuration.
[dns_proxy]
Section titled “[dns_proxy]”Routes DNS queries through the encrypted tunnel to prevent DNS leaks in SOCKS5 mode. Disabled by default.
When enabled, the client listens for UDP DNS queries on listen_address and forwards them to the server’s /dns WebSocket endpoint using the same X3DH + Double Ratchet encryption as regular traffic. Split tunnel rules are respected: bypass domains are resolved locally and blocked ad/tracker domains return NXDOMAIN immediately.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Start the local DNS proxy alongside the SOCKS5 proxy. |
listen_address | string | "127.0.0.1:5353" | UDP address and port for the DNS proxy. Use port 53 for system-wide compatibility (requires root or CAP_NET_BIND_SERVICE). |
nameservers | list | ["223.5.5.5:53", "1.1.1.1:53", "8.8.8.8:53"] | Public DNS servers used for bypass domains. Queries are sent directly via UDP to avoid looping back to the DNS proxy itself. CN users should put a local DNS first (e.g. 119.29.29.29:53). |
[dns_proxy]enabled = truelisten_address = "127.0.0.1:53"nameservers = ["223.5.5.5:53", "1.1.1.1:53", "8.8.8.8:53"]See DNS proxy setup for instructions on pointing your system DNS at this address.
TUN interface settings. Enable TUN mode by setting enabled = true. The client IP address, gateway IP, and DNS servers are assigned dynamically by the server via the VirtualIp message — do not set them manually.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable full tunnel (TUN) mode. All IP traffic is routed through the VPN. |
interface_name | string | (auto) | TUN interface name. Omit to let the OS assign one — typically utun0 on macOS and tun0 on Linux/FreeBSD. Set to a specific name if you need one for firewall rules. |
routes | list | ["0.0.0.0/0"] | Routes to send through the tunnel. Default routes all traffic. Specify individual CIDRs for split-routing. |
mtu | integer | 1420 | MTU of the TUN interface. Lower values reduce fragmentation on high-latency links. |
[tun]enabled = true# interface_name = "vpn0" # optional, defaults to OS-assigned namemtu = 1420routes = ["0.0.0.0/0"][split_tunnel]
Section titled “[split_tunnel]”Controls which traffic bypasses the VPN and which is forced through it.
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable split tunneling. Must be true for any other split tunnel settings to take effect. |
builtin_bypass_countries | list | ["CN"] | Country codes whose IP ranges are automatically bypassed. Uses APNIC data. Supported: "CN", "HK", "TW", "RU", and others. Set to [] to disable. |
bypass_networks_file | path | (none) | Path to a file of CIDR networks (one per line) that connect directly, bypassing the VPN. |
bypass_domains_file | path | (none) | Path to a file of domain names (one per line) that connect directly. |
tunnel_networks_file | path | (none) | Path to a file of CIDR networks that are forced through the VPN (overrides bypass rules). |
tunnel_domains_file | path | (none) | Path to a file of domain names forced through the VPN. |
bypass_networks | list | [] | Inline list of CIDR networks to bypass. Same as bypass_networks_file but defined directly in the config. |
auto_reload_interval | integer | 86400 | How often (in seconds) to reload the bypass/tunnel files. Set to 0 to disable auto-reload. |
block_ads | bool | false | Block known ad and tracking domains at the DNS level. No bytes sent to blocked domains. |
ad_block_file | path | (none) | Path to a custom ad block list (one domain per line). Used in addition to the built-in list when block_ads = true. |
[split_tunnel]enabled = truebuiltin_bypass_countries = ["CN"]block_ads = truebypass_networks_file = "~/.config/rvpn/bypass-networks.txt"[network]
Section titled “[network]”Client-side network behavior.
| Field | Type | Default | Description |
|---|---|---|---|
ipv6_enabled | bool | true | Enable IPv6 connections through the proxy. |
prefer_ipv4 | bool | true | When both IPv4 and IPv6 are available, prefer IPv4. Reduces latency on most networks. |
dns_cache_enabled | bool | true | Cache DNS responses to reduce repeated lookups. |
dns_cache_ttl | integer | 14400 | How long (in seconds) to cache DNS entries. Defaults to 4 hours. |
dns_cache_size | integer | 1000 | Maximum number of entries in the DNS cache. |
dns_servers | list | [] | Custom upstream DNS nameservers for resolving bypass domains (direct connections). Queries are sent directly via UDP to these servers, bypassing the system resolver entirely. Leave empty to use the system default. Useful when the system resolver is unreliable or you want to use a specific DNS provider for China traffic. |
[network]prefer_ipv4 = truedns_cache_enabled = truedns_cache_ttl = 14400dns_servers = ["223.5.5.5", "223.6.6.6"] # Alibaba DNS for CN bypass domains[server_identity]
Section titled “[server_identity]”Pins the server’s Ed25519 identity key (SSH-style TOFU) so the client refuses to connect if the server ever presents a different key. See Server Identity Pinning for the full model, including the operator rotation ceremony.
| Field | Type | Default | Description |
|---|---|---|---|
fingerprint | string | (none) | Expected pin in the canonical ik:1:<base32> form (e.g. ik:1:d4rgmp5b7ta6qmxi2mccwkjq4qxopfxzr7qivbfgu4wjycmuxnla). If set, the client rejects connections where the server’s identity doesn’t match. Legacy 32-character hex values from older installs are still accepted and rewritten on save. |
trust_on_first_use | bool | true | Accept any server identity on the first connection and pin it for future verification. |
known_hosts_file | path | "known_hosts.json" | Where to store pinned server identities. Entries in the legacy hex format are migrated to ik:1:… on save, keeping the original first_seen timestamp. |
strict | bool | true | If true, abort the connection on a mismatch. If false, only log a warning and continue. |
strict_mode | bool | false | Strict TOFU. If true, reject unknown server identities even on the first connection — you must set fingerprint explicitly before the first connect. Default false accepts and pins on first use. |
[server_identity]trust_on_first_use = truestrict = trueMulti-server routing
Section titled “Multi-server routing”The CLI client can hold a small pool of servers and dispatch each SOCKS5 flow to one of them based on the target hostname or IP. Typical use case: default all traffic through Hong Kong, but send google.com (and friends) through Singapore so geolocated content resolves correctly on the Singapore side.
This feature is SOCKS5-only. TUN mode wraps the whole network stack in one tunnel and has no per-flow hook; enabling multi-server config in TUN mode is refused with a clear error. Multiplexed SOCKS5 (socks5.multiplex = true) is also refused, because the shared multiplex tunnel is single-server.
The client’s identity_key_file is shared across every server (SSH-model — one client key, many hosts). Each server has its own prekey bundle.
[[server]]
Section titled “[[server]]”Zero or more extra servers can be declared with [[server]] blocks. The top-level server_address + prekey_bundle remain the implicit "default" server, so existing single-server configs keep working unchanged.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Symbolic name used in [routing.<name>]. Must be unique; "default" is reserved. |
address | string | yes | WebSocket URL (wss://...). |
prekey_bundle | path | yes | Path to this server’s X3DH prekey bundle JSON. |
sni_hostname | string | no | TLS SNI override. Defaults to the URL host. |
fingerprint | string | no | Pinned ik:1:... identity. Overrides the top-level [server_identity] for this server. |
[routing.<name>]
Section titled “[routing.<name>]”Routing rules keyed by server name. A SOCKS5 request is dispatched to the named server if any of its domains or ips match; otherwise it goes to the default server.
| Field | Type | Description |
|---|---|---|
domains | array of strings | Hostname patterns — see the matching rules below. Case-insensitive. |
ips | array of strings | Literal IPs (8.8.8.8) or CIDR blocks (1.1.1.0/24, 2606:4700::/32). |
Domain pattern matching
Section titled “Domain pattern matching”Three forms are supported. The distinction between google.com and *.google.com is important — they do different things.
| Pattern | Matches | Does NOT match | Use when |
|---|---|---|---|
google.com | google.com and mail.google.com, www.google.com, … | notgoogle.com | You want the apex and every subdomain (the common case). |
*.google.com | mail.google.com, www.google.com | google.com itself — the apex is excluded on purpose | You want subdomains only, e.g. because the apex should follow a different rule. |
google.com. (trailing dot) | Only exactly google.com | Any subdomain | You need a strict single-host match. |
Wildcards other than a leading *. are rejected at startup — google.* or *.google.* are not valid.
Common gotcha: if you write *.google.com and then browse to https://google.com, the request goes to the default server, not the routed one. Use the bare google.com form to cover both the apex and subdomains.
Match order is hostname first, IP fallback. A SOCKS5 request with ATYP = 0x03 (domain) carries the raw hostname; the client sends that hostname unresolved through the chosen server, so DNS is done server-side and geo-DNS lookups return answers local to the chosen exit.
If your SOCKS5 client resolves DNS locally (e.g. curl -x socks5://… rather than socks5h://…, or a system-wide proxy setting), the request arrives with ATYP = 0x01 — a literal IP — and domain rules cannot match it. Either flip your client to hostname-passthrough mode (socks5h://) or add the destination IP ranges under ips.
server_address = "wss://hk.example.com"identity_key_file = "identity.key"prekey_bundle = "hk.bundle.json"
[[server]]name = "sg"address = "wss://sg.example.com"prekey_bundle = "sg.bundle.json"
[routing.sg]domains = ["google.com", "*.google.com", "youtube.com"]ips = ["8.8.8.8/32", "1.1.1.1"]With this config, curl -x socks5h://127.0.0.1:1080 https://google.com opens a WebSocket to the SG server and lets SG resolve google.com; everything else keeps going through HK.
If a routed server is unreachable, the SOCKS5 request fails — the client does not silently fall back to the default. That’s deliberate: geo-routed traffic returning from the wrong region is worse than a clean failure.
[performance]
Section titled “[performance]”Tuning for high-throughput or resource-constrained environments.
| Field | Type | Default | Description |
|---|---|---|---|
worker_threads | integer | 4 | Number of async worker threads. Increase on high-core servers. |
recv_buffer_size | integer | 262144 | TCP receive buffer size in bytes (256 KB). |
send_buffer_size | integer | 262144 | TCP send buffer size in bytes (256 KB). |
crypto_worker_count | integer | 4 | Threads dedicated to encryption/decryption. Higher values improve throughput under heavy concurrent load. |
[performance]worker_threads = 4crypto_worker_count = 4Full Example
Section titled “Full Example”server_address = "wss://your.server.com/api/v1/ws"identity_key_file = "~/.config/rvpn/identity.key"prekey_bundle = "~/.config/rvpn/prekey-bundle.json"tls_fingerprint = "chrome"
[socks5]listen_address = "127.0.0.1:1080"
[http_proxy]enabled = truelisten_address = "127.0.0.1:8118"
[tun]enabled = truemtu = 1420routes = ["0.0.0.0/0"]
[dns_proxy]enabled = truelisten_address = "127.0.0.1:53"nameservers = ["223.5.5.5:53", "1.1.1.1:53", "8.8.8.8:53"]
[split_tunnel]enabled = truebuiltin_bypass_countries = ["CN"]block_ads = true
[network]prefer_ipv4 = truedns_cache_enabled = true
[server_identity]trust_on_first_use = truestrict = true