Skip to main content

Native Proxy Configuration Guide

The native blindhop-proxy is a Rust binary that runs on your machine and routes all smoldot traffic through the Nym mixnet. It provides the best performance and full privacy mode support (None / Fast 2-hop / Full 5-hop).

When to Use Native Proxy​

ScenarioUse Native ProxyUse Browser Proxy
Production deployment✅
Maximum privacy (2-hop Fast mode)✅
Automated/scripted usage✅
Quick demo / evaluation✅
No Rust toolchain available✅

Installation​

Build from Source​

git clone https://github.com/blindhop/blindhop.git
cd blindhop
cargo build -p blindhop-proxy --release

# Binary at: ./target/release/blindhop-proxy

Verify​

./target/release/blindhop-proxy --help

Helper Scripts​

Convenience scripts are provided in scripts/:

# Start exit service (builds + runs)
./scripts/run_exit.sh --release

# Start proxy (auto-reads .exit_nym_address if available)
./scripts/run_proxy.sh --mode full

# All-in-one demo (exit + proxy + demo UI)
./scripts/run_demo.sh --exit-address <NYM_ADDRESS>

CLI Reference​

blindhop-proxy [OPTIONS]
FlagDefaultDescription
--listen127.0.0.1:9500Local WebSocket proxy address
--targetwss://sys.turboflakes.io/asset-hub-paseoSubstrate full node RPC URL
--privacy-modefullPrivacy level: none, fast, full
--exit-address(required for fast/full)Nym address of the exit service
--nym-gateway(auto-select)Reserved: accepted but not yet used (the proxy always auto-selects a gateway)
--allowed-origin(none)Browser origin allowed to connect (repeatable, e.g. https://demo.blindhop.wtf). Required for web apps.

:::info Browser Origin Security Browsers automatically send an Origin header during WebSocket handshakes and do not enforce CORS on WebSockets. To prevent arbitrary websites from connecting to your local proxy and switching it to direct mode or querying your IP, blindhop-proxy refuses any browser connection whose origin isn't listed in --allowed-origin. Non-browser clients (such as smoldot CLI apps) do not send an Origin header and are permitted by default. :::

Privacy Modes​

None (Direct)​

blindhop-proxy --privacy-mode none --target wss://rpc.polkadot.io
  • Direct WebSocket connection — no Nym, no privacy
  • Lowest latency (0ms proxy overhead)
  • Your IP is visible to the full node

Fast (2-hop dVPN)​

blindhop-proxy --privacy-mode fast --exit-address <NYM_ADDRESS>
  • 2-hop Nym dVPN mode: routes directly from entry gateway to exit gateway, skipping intermediate mix nodes
  • Poisson delays and loop cover traffic disabled for lowest mixnet latency (~1.4–1.8 s measured p50)
  • IP hidden from full node; timing analysis still possible

Full (5-hop Mixnet)​

blindhop-proxy --privacy-mode full --exit-address <NYM_ADDRESS>
  • Full 5-hop Nym mixnet with Loopix loop cover traffic and Poisson mixing delays
  • Complete metadata privacy (~2.0 s measured p50, ~3.1 s p90)
  • Strongest protection against timing and traffic analysis

Connecting smoldot​

Point your smoldot light client's WebSocket at the proxy:

// Instead of connecting directly:
// const ws = new WebSocket('wss://rpc.polkadot.io');

// Connect through BlindHop:
const ws = new WebSocket('ws://127.0.0.1:9500');

All JSON-RPC traffic is automatically routed through the configured privacy mode.

Control Messages​

The proxy supports special JSON-RPC methods for runtime control:

// Switch privacy mode at runtime
ws.send(JSON.stringify({
jsonrpc: '2.0', id: 99,
method: 'blindhop_setPrivacyMode',
params: ['fast']
}));

// On success, the proxy replies confirming the active mode:
// {"jsonrpc":"2.0","id":99,"result":{"mode":"2-hop dVPN — IP Hidden","mode_id":"fast","status":"ok"}}
// If connecting in the new mode fails, the reply is error -32000 with error.data.mode_id
// set to the mode actually in effect. An unknown mode name gives error -32602.

// Get current metrics
ws.send(JSON.stringify({
jsonrpc: '2.0', id: 100,
method: 'blindhop_getMetrics',
params: []
}));

Running as a Service​

systemd (Linux)​

sudo tee /etc/systemd/system/blindhop-proxy.service << 'EOF'
[Unit]
Description=BlindHop Proxy
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/blindhop-proxy \
--privacy-mode full \
--exit-address <NYM_ADDRESS>
Restart=always
RestartSec=10
Environment=RUST_LOG=info

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable --now blindhop-proxy

launchd (macOS)​

cat > ~/Library/LaunchAgents/wtf.blindhop.proxy.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>wtf.blindhop.proxy</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/blindhop-proxy</string>
<string>--privacy-mode</string><string>full</string>
<string>--exit-address</string><string>YOUR_NYM_ADDRESS</string>
</array>
<key>KeepAlive</key><true/>
</dict>
</plist>
EOF

launchctl load ~/Library/LaunchAgents/wtf.blindhop.proxy.plist

Troubleshooting​

IssueFix
Address already in useAnother process is on port 9500. Use --listen 127.0.0.1:9501
Nym gateway connection failedCheck internet connectivity and retry; the Nym SDK selects a gateway afresh on each connect
Exit service unreachableVerify the exit service is running and the Nym address is correct
High latency in Full modeNormal (1-3s). Nym mixing adds deliberate delays for privacy