Routing traffic through a SOCKS5 proxy
Set proxyproxyproxyproxyproxyproxyproxyProxyProxy on the config to send the connections the SDK opens through a SOCKS5 proxy, such as a local Tor daemon.
let mut config = default_config(Network::Mainnet);
config.api_key = Some("<breez api key>".to_string());
// Route the SDK's connections through a SOCKS5 proxy, such as a local Tor
// daemon. Set username and password together for proxies that require
// RFC 1929 authentication.
config.proxy = Some(ProxyConfig {
host: "127.0.0.1".to_string(),
port: 9050,
username: None,
password: None,
});
var config = defaultConfig(network: Network.mainnet)
config.apiKey = "<breez api key>"
// Route the SDK's connections through a SOCKS5 proxy, such as a local Tor
// daemon. Set username and password together for proxies that require
// RFC 1929 authentication.
config.proxy = ProxyConfig(
host: "127.0.0.1",
port: 9050,
username: nil,
password: nil
)
val config = defaultConfig(Network.MAINNET)
config.apiKey = "<breez api key>"
// Route the SDK's connections through a SOCKS5 proxy, such as a local Tor
// daemon. Set username and password together for proxies that require
// RFC 1929 authentication.
config.proxy = ProxyConfig(
host = "127.0.0.1",
port = 9050u,
username = null,
password = null,
)
// Route the SDK's connections through a SOCKS5 proxy, such as a local
// Tor daemon. Set username and password together for proxies that
// require RFC 1929 authentication.
var config = BreezSdkSparkMethods.DefaultConfig(Network.Mainnet) with
{
apiKey = "<breez api key>",
proxy = new ProxyConfig(
host: "127.0.0.1",
port: 9050,
username: null,
password: null)
};
const config = defaultConfig(Network.Mainnet)
config.apiKey = '<breez api key>'
// Route the SDK's connections through a SOCKS5 proxy, such as a local Tor
// daemon. Set username and password together for proxies that require
// RFC 1929 authentication.
config.proxy = {
host: '127.0.0.1',
port: 9050,
username: undefined,
password: undefined
}
// Route the SDK's connections through a SOCKS5 proxy, such as a local Tor
// daemon. Set username and password together for proxies that require
// RFC 1929 authentication.
final config = defaultConfig(network: Network.mainnet).copyWith(
apiKey: "<breez api key>",
proxy: const ProxyConfig(
host: "127.0.0.1",
port: 9050,
username: null,
password: null,
),
);
config = default_config(network=Network.MAINNET)
config.api_key = "<breez api key>"
# Route the SDK's connections through a SOCKS5 proxy, such as a local Tor
# daemon. Set username and password together for proxies that require
# RFC 1929 authentication.
config.proxy = ProxyConfig(
host="127.0.0.1",
port=9050,
username=None,
password=None,
)
config := breez_sdk_spark.DefaultConfig(breez_sdk_spark.NetworkMainnet)
apiKey := "<breez api key>"
config.ApiKey = &apiKey
// Route the SDK's connections through a SOCKS5 proxy, such as a local Tor
// daemon. Set username and password together for proxies that require
// RFC 1929 authentication.
config.Proxy = &breez_sdk_spark.ProxyConfig{
Host: "127.0.0.1",
Port: 9050,
Username: nil,
Password: nil,
}
Set usernameusernameusernameusernameusernameusernameusernameUsernameUsername and passwordpasswordpasswordpasswordpasswordpasswordpasswordPasswordPassword together for proxies that require RFC 1929 authentication. Setting only one of them is rejected.
What the proxy covers
Every connection the SDK opens, HTTP and gRPC alike.
Hostnames are resolved by the proxy, never locally, so no DNS query reveals which host you are reaching. BIP353 name lookups switch from plain DNS to DNS-over-HTTPS for the same reason: plain DNS is UDP, which a SOCKS5 proxy does not carry, so those queries would otherwise escape the tunnel. They stay DNSSEC-verified either way.
Only the proxy's own address is resolved locally, which is unavoidable: it is the one host that cannot be reached through itself.
Failing closed
A connection that cannot be established through the proxy fails. The SDK never retries it directly, and setting a proxy also disables system-proxy autodetection, so no environment variable can route traffic around it.
A configuration the SDK cannot honour is rejected where it is supplied, rather than partly applied: at connectconnectconnectconnectconnectconnectconnectConnectConnect, or at the constructor of a component built outside the SDK.
| Rejected combination | Why |
|---|---|
| A proxy on WASM | The browser owns connection setup and exposes no proxy control. |
proxyproxyproxyproxyproxyproxyproxyProxyProxy with connections_per_operatorconnections_per_operatorconnectionsPerOperatorconnectionsPerOperatorconnectionsPerOperatorconnectionsPerOperatorconnectionsPerOperatorConnectionsPerOperatorConnectionsPerOperator above 1 | Balanced operator connections build their own connectors and cannot be routed. |
A proxy carrying credentials on PasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfig | Nostr relay connections cannot authenticate to a proxy. Wallet labels live on those relays and are one of the salts a wallet seed derives from, so a label that cannot be published cannot be recovered: this fails before a wallet exists rather than after one is funded. |
Shared SDK Context
A shared SDK Context owns the pooled HTTP client and gRPC channels, so the proxy has to be set on SdkContextConfigSdkContextConfigSdkContextConfigSdkContextConfigSdkContextConfigSdkContextConfigSdkContextConfigSdkContextConfigSdkContextConfig as well. It must match the proxyproxyproxyproxyproxyproxyproxyProxyProxy on the ConfigConfigConfigConfigConfigConfigConfigConfigConfig of every SDK built from that context: the SDK rejects a mismatch at connectconnectconnectconnectconnectconnectconnectConnectConnect, since a disagreement would mean part of the traffic bypassed the proxy.
Components built outside the SDK
A few APIs run without an SDK instance, so they cannot pick the setting up on their own. Pass the same proxy to each one you use:
get_spark_statusget_spark_statusgetSparkStatusgetSparkStatusgetSparkStatusgetSparkStatusgetSparkStatusGetSparkStatusGetSparkStatus, viaGetSparkStatusRequestGetSparkStatusRequestGetSparkStatusRequestGetSparkStatusRequestGetSparkStatusRequestGetSparkStatusRequestGetSparkStatusRequestGetSparkStatusRequestGetSparkStatusRequestnew_rest_chain_servicenew_rest_chain_servicenewRestChainServicenewRestChainServicenewRestChainServicenewRestChainServicenewRestChainServiceNewRestChainServiceNewRestChainService, viaNewRestChainServiceRequestNewRestChainServiceRequestNewRestChainServiceRequestNewRestChainServiceRequestNewRestChainServiceRequestNewRestChainServiceRequestNewRestChainServiceRequestNewRestChainServiceRequestNewRestChainServiceRequest- The Turnkey signer, via
TurnkeyConfigTurnkeyConfigTurnkeyConfigTurnkeyConfigTurnkeyConfigTurnkeyConfigTurnkeyConfigTurnkeyConfigTurnkeyConfig - The passkey client, via
PasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfigPasskeyConfig. A proxy carrying credentials is rejected when the client is constructed (see above).
A service you supply yourself through with_chain_servicewith_chain_servicewithChainServicewithChainServicewithChainServicewithChainServicewithChainServiceWithChainServiceWithChainService, with_fiat_servicewith_fiat_servicewithFiatServicewithFiatServicewithFiatServicewithFiatServicewithFiatServiceWithFiatServiceWithFiatService, with_lnurl_clientwith_lnurl_clientwithLnurlClientwithLnurlClientwithLnurlClientwithLnurlClientwithLnurlClientWithLnurlClientWithLnurlClient or with_lnurl_server_clientwith_lnurl_server_clientwithLnurlServerClientwithLnurlServerClientwithLnurlServerClientwithLnurlServerClientwithLnurlServerClientWithLnurlServerClientWithLnurlServerClient already owns its transport, which the SDK cannot inspect or re-route. Make it connect through the proxy yourself; the SDK logs a warning when it sees one alongside a proxy. with_rest_chain_servicewith_rest_chain_servicewithRestChainServicewithRestChainServicewithRestChainServicewithRestChainServicewithRestChainServiceWithRestChainServiceWithRestChainService is built on the SDK's own client and is proxied automatically.
JavaScript and WASM
A SOCKS5 proxy cannot be honoured in a browser, and setting proxyproxyproxyproxyproxyproxyproxyProxyProxy on a WASM build is an error rather than a silent direct connection.
In Node, route the SDK by installing a proxy dispatcher on the global fetch before connecting, which covers both the HTTP and gRPC calls the WASM build makes:
import { setGlobalDispatcher, ProxyAgent } from 'undici'
setGlobalDispatcher(new ProxyAgent('socks5://127.0.0.1:9050'))