Proposed changes
Add a public way to route the SDK's WebSocket connections
(listen.v2.connect, speak.v1.connect, agent.v1.connect, etc.)
through a custom HTTP agent — e.g. an HttpsProxyAgent — so streaming
traffic can transit an HTTP/HTTPS proxy.
Today this is not possible:
BaseClientOptions.fetch / BaseClientOptions.fetcher only affect
REST requests; the WebSocket upgrade in core/websocket/ws.js is built
directly via the ws package and never goes through the fetcher.
ReconnectingWebSocket.Options already declares WebSocket?: any,
which would be a perfect injection point, but the connect() methods
hard-code the options object and never expose it.
Context
We run the SDK behind a corporate egress proxy. All other outbound
traffic from our service (REST APIs, WebSockets from other providers via a custom
ws agent, etc.) honours HTTPS_PROXY. The Deepgram SDK is the only
client that cannot, which forced direct DNS resolution for
api.eu.deepgram.com from inside our cluster. When that hostname's
in-cluster DNS resolution stuttered, every Deepgram session failed with
getaddrinfo EAI_AGAIN api.eu.deepgram.com and the ReconnectingWebSocket
retried in a tight loop — a routine outage we would not have hit if the
SDK had taken the proxy route the rest of the egress already uses.
Anyone running the SDK from a Node service in a network-policy-controlled
environment (Kubernetes with egress proxies, enterprise networks, regional
gateways) hits the same wall.
Possible Implementation
Any one of these would resolve it:
- Forward agent on ConnectArgs. Most surgical: pipe the value
through to the underlying new ws.WebSocket(url, protocols, { agent })
call.
- Pass through a WebSocket override on ConnectArgs to
ReconnectingWebSocket.options.WebSocket. The slot already exists,
only the plumbing is missing.
- Honour HTTPS_PROXY / HTTP_PROXY automatically in the Node
branch of getGlobalWebSocket() — e.g. attach an https-proxy-agent
when those env vars are set, similar to how undici honours them.
(1) keeps the surface tiny and is symmetric with the agent option ws
already accepts.
Other information
Workaround we currently use: patch ws.WebSocket at process startup with
a subclass that injects options.agent = new HttpsProxyAgent(process.env.HTTPS_PROXY)
for Deepgram URLs. The patch has to reach the real
module.exports.WebSocket via module.createRequire("ws"), because
import * as ws from "ws" produces a frozen / copied namespace whose
mutation does not propagate back to other consumers — including the SDK
itself.
Proposed changes
Add a public way to route the SDK's WebSocket connections
(
listen.v2.connect,speak.v1.connect,agent.v1.connect, etc.)through a custom HTTP agent — e.g. an
HttpsProxyAgent— so streamingtraffic can transit an HTTP/HTTPS proxy.
Today this is not possible:
BaseClientOptions.fetch/BaseClientOptions.fetcheronly affectREST requests; the WebSocket upgrade in
core/websocket/ws.jsis builtdirectly via the
wspackage and never goes through the fetcher.ReconnectingWebSocket.Optionsalready declaresWebSocket?: any,which would be a perfect injection point, but the
connect()methodshard-code the options object and never expose it.
Context
We run the SDK behind a corporate egress proxy. All other outbound
traffic from our service (REST APIs, WebSockets from other providers via a custom
ws agent, etc.) honours HTTPS_PROXY. The Deepgram SDK is the only
client that cannot, which forced direct DNS resolution for
api.eu.deepgram.com from inside our cluster. When that hostname's
in-cluster DNS resolution stuttered, every Deepgram session failed with
getaddrinfo EAI_AGAIN api.eu.deepgram.com and the ReconnectingWebSocket
retried in a tight loop — a routine outage we would not have hit if the
SDK had taken the proxy route the rest of the egress already uses.
Anyone running the SDK from a Node service in a network-policy-controlled
environment (Kubernetes with egress proxies, enterprise networks, regional
gateways) hits the same wall.
Possible Implementation
Any one of these would resolve it:
through to the underlying new ws.WebSocket(url, protocols, { agent })
call.
ReconnectingWebSocket.options.WebSocket. The slot already exists,
only the plumbing is missing.
branch of getGlobalWebSocket() — e.g. attach an https-proxy-agent
when those env vars are set, similar to how undici honours them.
(1) keeps the surface tiny and is symmetric with the agent option ws
already accepts.
Other information
Workaround we currently use: patch ws.WebSocket at process startup with
a subclass that injects options.agent = new HttpsProxyAgent(process.env.HTTPS_PROXY)
for Deepgram URLs. The patch has to reach the real
module.exports.WebSocket via module.createRequire("ws"), because
import * as ws from "ws" produces a frozen / copied namespace whose
mutation does not propagate back to other consumers — including the SDK
itself.