Skip to content

add proxy support for realtime calls (websocket based) - #5788

Merged
akshaydeo merged 1 commit into
mainfrom
08-02-add_proxy_support_for_realtime_calls_websocket_based_
Aug 3, 2026
Merged

akshaydeo merged 1 commit into
mainfrom
08-02-add_proxy_support_for_realtime_calls_websocket_based_

Conversation

@akshaydeo

@akshaydeo akshaydeo commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

WebSocket connections (Realtime and Responses) were not routing through the provider-level proxy configuration. HTTP requests already respected ProxyConfig, but the WebSocket dial path bypassed it entirely, causing WebSocket traffic to go direct regardless of what proxy was configured.

Changes

  • Added ConfigureWebSocketProxy in core/providers/utils/utils.go that mirrors ConfigureProxy for *ws.Dialer, supporting HTTP, SOCKS5, env-based, and no-proxy configurations. Unlike ConfigureProxy, it returns an error directly rather than swapping in a failing dial func, since WebSocket dials are resolved fresh on every call.
  • Updated Dial, DialUpstream, Pool.Get, and Pool.dial in the WebSocket transport to accept and apply a *schemas.ProxyConfig.
  • Updated WSRealtimeHandler.runRealtimeSession and WSResponsesHandler.tryNativeWSUpstream to look up the provider's ProxyConfig and pass it through to the dial path.
  • Added unit tests for ConfigureWebSocketProxy covering literal URL, env-backed URL, empty env value (fail-fast), nil config, NoProxy, and SOCKS5 cases.
  • Added integration tests in pool_test.go with a real CONNECT-based forward proxy to verify dials actually route through the proxy (TestPoolGetDialsThroughConfiguredHTTPProxy) and that an unreachable proxy fails the dial rather than falling back silently (TestPoolGetFailsWithUnreachableProxy).
  • Added an informational UI alert on the proxy configuration form clarifying that the proxy applies to HTTP and WebSocket connections but not WebRTC-based Realtime media paths.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

# Core/Transports
go test ./core/providers/utils/...
go test ./transports/bifrost-http/websocket/...

# UI
cd ui
pnpm i
pnpm build

Configure a provider with an HTTP or SOCKS5 proxy and open a Realtime or Responses WebSocket session. Verify traffic routes through the proxy (e.g., via proxy access logs or by pointing at a local intercepting proxy). Confirm that setting an unreachable proxy URL causes the connection to fail rather than silently connecting directly.

Breaking changes

  • No

Pool.Get, Pool.dial, Dial, and DialUpstream all gained a proxyConfig *schemas.ProxyConfig parameter. Passing nil preserves the previous direct-dial behavior.

Security considerations

Proxy credentials (Username, Password) are sourced from SecretVar and embedded into the parsed proxy URL only when both are non-empty. CA certificate PEM for proxy TLS is validated at dial time and fails fast if an env-backed secret resolves to an empty value, preventing silent misconfiguration.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • WebSocket connections now support HTTP, HTTPS, SOCKS5, and environment-based proxy settings.
    • Proxy authentication, custom certificates, and bypass rules are supported for WebSocket traffic.
    • Realtime and Responses connections consistently apply configured proxy settings to direct and pooled connections.
  • Bug Fixes
    • Invalid or unreachable proxy configurations now fail clearly without silently bypassing the proxy.
  • UI
    • Added guidance clarifying proxy coverage; WebRTC sessions use a separate media path.

Walkthrough

WebSocket dialing now accepts provider proxy configuration for HTTP, SOCKS5, and environment-backed proxies. Realtime and Responses handlers pass proxy settings to direct and pooled connections. Tests cover proxy configuration, tunneled WebSocket flows, and realtime event streaming. The provider form documents proxy coverage.

Changes

WebSocket proxy support

Layer / File(s) Summary
Proxy configuration and validation
core/providers/utils/utils.go, core/providers/utils/proxy_test.go
Added WebSocket proxy configuration for HTTP, SOCKS5, environment-backed proxies, credentials, and custom CA settings. Added validation tests.
Proxy-aware dialing and pooling
transports/bifrost-http/websocket/connection.go, transports/bifrost-http/websocket/pool.go, transports/bifrost-http/websocket/pool_test.go
Passed proxy configuration through WebSocket dialing and pool creation. Added CONNECT proxy, message exchange, and unreachable-proxy tests.
Provider proxy propagation
transports/bifrost-http/handlers/wsrealtime.go, transports/bifrost-http/handlers/wsresponses.go
Loaded provider proxy configuration for realtime and native Responses connections.
Realtime WebSocket validation
transports/bifrost-http/tests/realtime/realtime_ws_test.go, scripts/realtime-test/*
Added end-to-end and standalone realtime WebSocket tests for event exchange and streamed responses.
Proxy tooling and module dependencies
Makefile, core/go.mod, framework/go.mod, plugins/*/go.mod, transports/go.mod, tests/cmd/*/go.mod
Added SOCKS5 and HTTP proxy installation and launch targets. Updated module dependencies.
Proxy coverage notice
ui/app/workspace/providers/fragments/proxyFormFragment.tsx
Documented HTTP and WebSocket coverage and excluded WebRTC media sessions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RealtimeClient
  participant BifrostHandler
  participant WebSocketPool
  participant ConfigureWebSocketProxy
  participant ProviderUpstream
  RealtimeClient->>BifrostHandler: send realtime events
  BifrostHandler->>WebSocketPool: pass provider ProxyConfig
  WebSocketPool->>ConfigureWebSocketProxy: configure new dialer
  ConfigureWebSocketProxy-->>WebSocketPool: return configured dialer
  WebSocketPool->>ProviderUpstream: establish proxied WebSocket
  ProviderUpstream-->>BifrostHandler: stream response events
  BifrostHandler-->>RealtimeClient: forward response events
Loading

Possibly related PRs

  • maximhq/bifrost#5731: Both changes update the same seed module files and core dependency versions.
  • maximhq/bifrost#5756: Both changes update overlapping dependency declarations across multiple go.mod files.
  • maximhq/bifrost#5778: Both changes modify proxy configuration paths, including WebSocket proxy handling and HTTP proxy-update restrictions.

Suggested reviewers: danpiths, pratham-mishra04

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding proxy support for WebSocket-based realtime calls.
Description check ✅ Passed The description covers the purpose, implementation, testing, affected areas, security considerations, and checklist with only minor template omissions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 08-02-add_proxy_support_for_realtime_calls_websocket_based_

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@akshaydeo
akshaydeo marked this pull request as ready for review August 3, 2026 06:34

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with 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.

Inline comments:
In `@core/providers/utils/utils.go`:
- Around line 661-665: Update the custom CA handling in the proxy configuration
flow around createTLSConfigWithCA: when TLS configuration creation fails, return
or propagate the configuration error instead of only logging it and continuing
with an unset dialer.TLSClientConfig; retain the successful path that assigns
tlsConfig to the dialer.
- Around line 635-638: The explicit HTTPProxy or Socks5Proxy path must fail
closed when proxyConfig.URL.GetValue() is empty instead of returning the direct
dialer. Update the proxy setup logic in core/providers/utils/utils.go:635-638 to
return a clear invalid-proxy-configuration error, and update the corresponding
expectations in core/providers/utils/proxy_test.go:156-170 to assert that error
rather than direct-dial behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f24be127-653b-4f67-963e-58cd44eb336c

📥 Commits

Reviewing files that changed from the base of the PR and between 2daaea6 and 3cacee3.

📒 Files selected for processing (8)
  • core/providers/utils/proxy_test.go
  • core/providers/utils/utils.go
  • transports/bifrost-http/handlers/wsrealtime.go
  • transports/bifrost-http/handlers/wsresponses.go
  • transports/bifrost-http/websocket/connection.go
  • transports/bifrost-http/websocket/pool.go
  • transports/bifrost-http/websocket/pool_test.go
  • ui/app/workspace/providers/fragments/proxyFormFragment.tsx

Comment thread core/providers/utils/utils.go
Comment thread core/providers/utils/utils.go
@akshaydeo
akshaydeo force-pushed the 08-02-add_proxy_support_for_realtime_calls_websocket_based_ branch from 3cacee3 to 8ff1080 Compare August 3, 2026 07:27
@akshaydeo
akshaydeo requested a review from a team as a code owner August 3, 2026 07:27
@akshaydeo
akshaydeo force-pushed the 08-02-add_proxy_support_for_realtime_calls_websocket_based_ branch from 8ff1080 to 661c00c Compare August 3, 2026 07:35

@coderabbitai coderabbitai Bot left a comment

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.

♻️ Duplicate comments (1)
core/providers/utils/utils.go (1)

632-639: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Fail closed instead of silently dialing direct when an explicit proxy has no URL.

For HTTPProxy and Socks5Proxy, if proxyURLValue is empty, the code logs a warning and returns dialer, nil at Line 638, leaving dialer.Proxy unset. The caller then dials directly, bypassing the configured proxy.

This is the same pattern flagged in a previous review round on these lines, which was reported as "Addressed in commit 8ff1080" with the fix of returning an error instead of falling back to direct dialing. The code shown here still contains the pre-fix behavior, so either the fix did not land on this branch or was reverted. Confirm whether the intended fix is present in this branch, and if not, reapply it.

🔒 Proposed fix
 		proxyURLValue := proxyConfig.URL.GetValue()
 		if proxyURLValue == "" {
-			getLogger().Warn("Warning: proxy URL is required for setting up WebSocket proxy")
-			return dialer, nil
+			return nil, fmt.Errorf("invalid proxy configuration: proxy URL is required for WebSocket proxy")
 		}
🤖 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 `@core/providers/utils/utils.go` around lines 632 - 639, Update the empty proxy
URL handling in the HTTPProxy and Socks5Proxy setup paths around proxyURLValue
so an explicitly configured proxy fails closed: return a descriptive error
instead of logging a warning and returning dialer with no Proxy. Preserve the
existing secret-reference validation and normal proxy setup behavior for
non-empty URLs.
🧹 Nitpick comments (1)
core/providers/utils/utils.go (1)

623-670: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider sharing URL/credential-resolution logic with ConfigureProxy.

ConfigureWebSocketProxy duplicates the secret-resolution check, URL parsing, and username/password merging already present in ConfigureProxy (lines 530-553, 556-580). The doc comment at Line 614 correctly explains why the two functions differ in failure behavior (fail-fast dial func vs. direct error return), but the URL/credential-resolution portion itself does not need to differ.

Extracting a small shared helper, e.g. resolveProxyURL(proxyConfig, fieldName) (*url.URL, error), that performs the secret check, GetValue(), url.Parse, and UserPassword merge, would let both callers apply the same validation. This also reduces the risk of the two code paths drifting apart, which is part of why the fail-open regression above only affects the WebSocket path and not ConfigureProxy.

🤖 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 `@core/providers/utils/utils.go` around lines 623 - 670, Extract the duplicated
proxy URL and credential resolution from ConfigureProxy and
ConfigureWebSocketProxy into a shared helper such as resolveProxyURL, including
secret-reference validation, value retrieval, URL parsing, and username/password
merging. Update both callers to use the helper while preserving their existing
distinct error-handling behavior and the WebSocket-specific empty URL handling.
🤖 Prompt for all review comments with 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.

Duplicate comments:
In `@core/providers/utils/utils.go`:
- Around line 632-639: Update the empty proxy URL handling in the HTTPProxy and
Socks5Proxy setup paths around proxyURLValue so an explicitly configured proxy
fails closed: return a descriptive error instead of logging a warning and
returning dialer with no Proxy. Preserve the existing secret-reference
validation and normal proxy setup behavior for non-empty URLs.

---

Nitpick comments:
In `@core/providers/utils/utils.go`:
- Around line 623-670: Extract the duplicated proxy URL and credential
resolution from ConfigureProxy and ConfigureWebSocketProxy into a shared helper
such as resolveProxyURL, including secret-reference validation, value retrieval,
URL parsing, and username/password merging. Update both callers to use the
helper while preserving their existing distinct error-handling behavior and the
WebSocket-specific empty URL handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 224caeec-5f93-4c58-a248-aa43708e01fd

📥 Commits

Reviewing files that changed from the base of the PR and between 8ff1080 and 661c00c.

⛔ Files ignored due to path filters (14)
  • core/go.sum is excluded by !**/*.sum
  • framework/go.sum is excluded by !**/*.sum
  • plugins/compat/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
  • plugins/jsonparser/go.sum is excluded by !**/*.sum
  • plugins/logging/go.sum is excluded by !**/*.sum
  • plugins/maxim/go.sum is excluded by !**/*.sum
  • plugins/mocker/go.sum is excluded by !**/*.sum
  • plugins/modelcatalogresolver/go.sum is excluded by !**/*.sum
  • plugins/otel/go.sum is excluded by !**/*.sum
  • plugins/prompts/go.sum is excluded by !**/*.sum
  • plugins/semanticcache/go.sum is excluded by !**/*.sum
  • plugins/telemetry/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (26)
  • Makefile
  • core/go.mod
  • core/providers/utils/proxy_test.go
  • core/providers/utils/utils.go
  • framework/go.mod
  • plugins/compat/go.mod
  • plugins/governance/go.mod
  • plugins/jsonparser/go.mod
  • plugins/logging/go.mod
  • plugins/maxim/go.mod
  • plugins/mocker/go.mod
  • plugins/modelcatalogresolver/go.mod
  • plugins/otel/go.mod
  • plugins/prompts/go.mod
  • plugins/semanticcache/go.mod
  • plugins/telemetry/go.mod
  • tests/cmd/e2eseed/go.mod
  • tests/cmd/seed/go.mod
  • tests/cmd/seedvks/go.mod
  • transports/bifrost-http/handlers/wsrealtime.go
  • transports/bifrost-http/handlers/wsresponses.go
  • transports/bifrost-http/websocket/connection.go
  • transports/bifrost-http/websocket/pool.go
  • transports/bifrost-http/websocket/pool_test.go
  • transports/go.mod
  • ui/app/workspace/providers/fragments/proxyFormFragment.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
  • tests/cmd/seed/go.mod
  • transports/bifrost-http/handlers/wsrealtime.go
  • tests/cmd/seedvks/go.mod
  • transports/bifrost-http/websocket/pool.go
  • transports/bifrost-http/handlers/wsresponses.go
  • ui/app/workspace/providers/fragments/proxyFormFragment.tsx
  • tests/cmd/e2eseed/go.mod
  • Makefile
  • transports/bifrost-http/websocket/connection.go
  • transports/bifrost-http/websocket/pool_test.go
  • core/providers/utils/proxy_test.go

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 3, 2026
@akshaydeo
akshaydeo force-pushed the 08-02-add_proxy_support_for_realtime_calls_websocket_based_ branch from 661c00c to 38b8f75 Compare August 3, 2026 15:52

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with 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.

Inline comments:
In `@scripts/realtime-test/go.mod`:
- Line 13: Update the indirect golang.org/x/net requirement in the realtime-test
module to a version newer than v0.33.0 that resolves all three flagged
advisories, then run go mod tidy from that module directory to regenerate the
corresponding go.sum entries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1369d534-7f58-4921-9414-bc7b51b0a818

📥 Commits

Reviewing files that changed from the base of the PR and between 661c00c and 38b8f75.

⛔ Files ignored due to path filters (18)
  • core/go.sum is excluded by !**/*.sum
  • framework/go.sum is excluded by !**/*.sum
  • plugins/compat/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
  • plugins/jsonparser/go.sum is excluded by !**/*.sum
  • plugins/logging/go.sum is excluded by !**/*.sum
  • plugins/maxim/go.sum is excluded by !**/*.sum
  • plugins/mocker/go.sum is excluded by !**/*.sum
  • plugins/modelcatalogresolver/go.sum is excluded by !**/*.sum
  • plugins/otel/go.sum is excluded by !**/*.sum
  • plugins/prompts/go.sum is excluded by !**/*.sum
  • plugins/semanticcache/go.sum is excluded by !**/*.sum
  • plugins/telemetry/go.sum is excluded by !**/*.sum
  • scripts/realtime-test/go.sum is excluded by !**/*.sum
  • tests/cmd/e2eseed/go.sum is excluded by !**/*.sum
  • tests/cmd/seed/go.sum is excluded by !**/*.sum
  • tests/cmd/seedvks/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (29)
  • Makefile
  • core/go.mod
  • core/providers/utils/proxy_test.go
  • core/providers/utils/utils.go
  • framework/go.mod
  • plugins/compat/go.mod
  • plugins/governance/go.mod
  • plugins/jsonparser/go.mod
  • plugins/logging/go.mod
  • plugins/maxim/go.mod
  • plugins/mocker/go.mod
  • plugins/modelcatalogresolver/go.mod
  • plugins/otel/go.mod
  • plugins/prompts/go.mod
  • plugins/semanticcache/go.mod
  • plugins/telemetry/go.mod
  • scripts/realtime-test/go.mod
  • scripts/realtime-test/main.go
  • tests/cmd/e2eseed/go.mod
  • tests/cmd/seed/go.mod
  • tests/cmd/seedvks/go.mod
  • transports/bifrost-http/handlers/wsrealtime.go
  • transports/bifrost-http/handlers/wsresponses.go
  • transports/bifrost-http/tests/realtime/realtime_ws_test.go
  • transports/bifrost-http/websocket/connection.go
  • transports/bifrost-http/websocket/pool.go
  • transports/bifrost-http/websocket/pool_test.go
  • transports/go.mod
  • ui/app/workspace/providers/fragments/proxyFormFragment.tsx
🚧 Files skipped from review as they are similar to previous changes (23)
  • plugins/telemetry/go.mod
  • transports/bifrost-http/handlers/wsrealtime.go
  • plugins/prompts/go.mod
  • plugins/semanticcache/go.mod
  • plugins/otel/go.mod
  • plugins/modelcatalogresolver/go.mod
  • plugins/mocker/go.mod
  • ui/app/workspace/providers/fragments/proxyFormFragment.tsx
  • transports/go.mod
  • transports/bifrost-http/websocket/connection.go
  • transports/bifrost-http/websocket/pool.go
  • plugins/governance/go.mod
  • framework/go.mod
  • plugins/jsonparser/go.mod
  • plugins/compat/go.mod
  • core/providers/utils/utils.go
  • plugins/maxim/go.mod
  • transports/bifrost-http/handlers/wsresponses.go
  • transports/bifrost-http/websocket/pool_test.go
  • core/go.mod
  • plugins/logging/go.mod
  • Makefile
  • core/providers/utils/proxy_test.go

Comment thread scripts/realtime-test/go.mod
@akshaydeo
akshaydeo force-pushed the 08-02-add_proxy_support_for_realtime_calls_websocket_based_ branch from 38b8f75 to cd727fd Compare August 3, 2026 15:58
@akshaydeo
akshaydeo force-pushed the 08-02-add_proxy_support_for_realtime_calls_websocket_based_ branch from cd727fd to f48d195 Compare August 3, 2026 15:59

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🤖 Prompt for all review comments with 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.

Inline comments:
In `@Makefile`:
- Around line 173-178: Update the temporary configuration setup in the Makefile
recipe to use the portable mktemp template
"$${TMPDIR:-/tmp}/bifrost-tinyproxy.XXXXXX" and exit immediately if mktemp
fails. Also make the printf command that writes "$$CONF" fail fast with "|| exit
1" before starting tinyproxy.
- Around line 159-164: Update the socks5-proxy target to reject non-loopback
HOST values before starting microsocks, unless the command configures
authentication and an appropriate allowlist. Preserve the existing loopback
defaults and startup behavior, and ensure unsafe values such as 0.0.0.0 cannot
launch an unauthenticated open proxy.
- Around line 162-163: Update the proxy startup messages around the SOCKS5
host/port output to use a display-only host value that wraps IPv6 literals in
brackets, producing valid URLs such as socks5://[::1]:1080. Keep PROXY_HOST
unchanged for the proxy processes and preserve unbracketed output for non-IPv6
hosts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 43790028-5451-4ac2-b475-2e07877d0157

📥 Commits

Reviewing files that changed from the base of the PR and between 38b8f75 and f48d195.

⛔ Files ignored due to path filters (18)
  • core/go.sum is excluded by !**/*.sum
  • framework/go.sum is excluded by !**/*.sum
  • plugins/compat/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
  • plugins/jsonparser/go.sum is excluded by !**/*.sum
  • plugins/logging/go.sum is excluded by !**/*.sum
  • plugins/maxim/go.sum is excluded by !**/*.sum
  • plugins/mocker/go.sum is excluded by !**/*.sum
  • plugins/modelcatalogresolver/go.sum is excluded by !**/*.sum
  • plugins/otel/go.sum is excluded by !**/*.sum
  • plugins/prompts/go.sum is excluded by !**/*.sum
  • plugins/semanticcache/go.sum is excluded by !**/*.sum
  • plugins/telemetry/go.sum is excluded by !**/*.sum
  • scripts/realtime-test/go.sum is excluded by !**/*.sum
  • tests/cmd/e2eseed/go.sum is excluded by !**/*.sum
  • tests/cmd/seed/go.sum is excluded by !**/*.sum
  • tests/cmd/seedvks/go.sum is excluded by !**/*.sum
  • transports/go.sum is excluded by !**/*.sum
📒 Files selected for processing (29)
  • Makefile
  • core/go.mod
  • core/providers/utils/proxy_test.go
  • core/providers/utils/utils.go
  • framework/go.mod
  • plugins/compat/go.mod
  • plugins/governance/go.mod
  • plugins/jsonparser/go.mod
  • plugins/logging/go.mod
  • plugins/maxim/go.mod
  • plugins/mocker/go.mod
  • plugins/modelcatalogresolver/go.mod
  • plugins/otel/go.mod
  • plugins/prompts/go.mod
  • plugins/semanticcache/go.mod
  • plugins/telemetry/go.mod
  • scripts/realtime-test/go.mod
  • scripts/realtime-test/main.go
  • tests/cmd/e2eseed/go.mod
  • tests/cmd/seed/go.mod
  • tests/cmd/seedvks/go.mod
  • transports/bifrost-http/handlers/wsrealtime.go
  • transports/bifrost-http/handlers/wsresponses.go
  • transports/bifrost-http/tests/realtime/realtime_ws_test.go
  • transports/bifrost-http/websocket/connection.go
  • transports/bifrost-http/websocket/pool.go
  • transports/bifrost-http/websocket/pool_test.go
  • transports/go.mod
  • ui/app/workspace/providers/fragments/proxyFormFragment.tsx
🚧 Files skipped from review as they are similar to previous changes (28)
  • core/go.mod
  • transports/bifrost-http/handlers/wsrealtime.go
  • plugins/prompts/go.mod
  • scripts/realtime-test/go.mod
  • transports/bifrost-http/websocket/pool.go
  • ui/app/workspace/providers/fragments/proxyFormFragment.tsx
  • plugins/compat/go.mod
  • plugins/semanticcache/go.mod
  • plugins/modelcatalogresolver/go.mod
  • plugins/governance/go.mod
  • plugins/jsonparser/go.mod
  • transports/bifrost-http/tests/realtime/realtime_ws_test.go
  • plugins/otel/go.mod
  • plugins/logging/go.mod
  • scripts/realtime-test/main.go
  • tests/cmd/seedvks/go.mod
  • plugins/telemetry/go.mod
  • tests/cmd/seed/go.mod
  • tests/cmd/e2eseed/go.mod
  • core/providers/utils/utils.go
  • transports/go.mod
  • transports/bifrost-http/websocket/pool_test.go
  • transports/bifrost-http/handlers/wsresponses.go
  • transports/bifrost-http/websocket/connection.go
  • plugins/maxim/go.mod
  • plugins/mocker/go.mod
  • core/providers/utils/proxy_test.go
  • framework/go.mod

Comment thread Makefile
Comment on lines +159 to +164
socks5-proxy: install-microsocks ## Run a local SOCKS5 proxy for testing provider proxy_config (Usage: make socks5-proxy [PORT=1080] [HOST=127.0.0.1])
@PROXY_PORT=$${PORT:-1080}; \
PROXY_HOST=$${HOST:-127.0.0.1}; \
$(ECHO) "$(GREEN)Starting SOCKS5 proxy on $$PROXY_HOST:$$PROXY_PORT (no auth, logs each connection, Ctrl+C to stop)...$(NC)"; \
$(ECHO) "$(YELLOW)Point a provider's proxy_config at socks5://$$PROXY_HOST:$$PROXY_PORT to test$(NC)"; \
microsocks -i $$PROXY_HOST -p $$PROXY_PORT

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

block="$(sed -n '/^socks5-proxy:/,/^$/p' Makefile)"
if ! grep -Eq 'case .*PROXY_HOST|microsocks .*(-u|-P|-w)' <<<"$block"; then
  echo "No non-loopback protection found for socks5-proxy" >&2
  exit 1
fi

Repository: maximhq/bifrost

Length of output: 203


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- repository state ---'
git status --short
git branch --show-current
git diff --stat

printf '%s\n' '--- Makefile target context ---'
cat -n Makefile | sed -n '125,180p'

printf '%s\n' '--- related proxy targets and configuration ---'
rg -n -C 3 'socks5-proxy|microsocks|proxy_config|Allow|allowlist|SOCKS5' . \
  -g '!node_modules' -g '!dist' -g '!build'

printf '%s\n' '--- stack metadata ---'
find .. -maxdepth 2 -type f \( -iname '*stack*' -o -iname '*pull*request*' \) -print

Repository: maximhq/bifrost

Length of output: 50374


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

log=/tmp/coderabbit-shell-logs/shell-output-xxXwlU

printf '%s\n' '--- state and target output ---'
sed -n '1,140p' "$log"

printf '%s\n' '--- exact target matches ---'
rg -n -C 8 '^socks5-proxy:|microsocks|proxy_config' Makefile README.md docs .github 2>/dev/null || true

printf '%s\n' '--- Makefile size and nearby targets ---'
wc -l Makefile
cat -n Makefile | sed -n '140,175p'

printf '%s\n' '--- changed files and stack indicators ---'
git diff --name-status
git status --short
find .. -maxdepth 2 -type f \( -iname '*stack*' -o -iname '*pull*request*' \) -print

Repository: maximhq/bifrost

Length of output: 50373


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- dry-run command expansion ---'
make -n socks5-proxy HOST=0.0.0.0 PORT=1080 | tail -n 8
make -n socks5-proxy HOST=192.0.2.10 PORT=1234 | tail -n 8

printf '%s\n' '--- microsocks option documentation ---'
curl -fsSL https://raw.githubusercontent.com/rofl0r/microsocks/master/README.md \
  | sed -n '/^Usage/,/^$/p;/^-i /p;/^-p /p;/^-u /p;/^-P /p;/^-w /p'

printf '%s\n' '--- microsocks bind and authentication implementation ---'
curl -fsSL https://raw.githubusercontent.com/rofl0r/microsocks/master/microsocks.c \
  | rg -n -C 3 'listen_ip|bind|username|password|SOCKS5_USER|SOCKS5_PASS|-i|-u|-P|-w'

Repository: maximhq/bifrost

Length of output: 229


🏁 Script executed:

#!/usr/bin/env bash
set -u

printf '%s\n' '--- microsocks option documentation ---'
if curl -fsSL https://raw.githubusercontent.com/rofl0r/microsocks/master/README.md > /tmp/microsocks-readme; then
  sed -n '/^Usage/,/^$/p;/^-i /p;/^-p /p;/^-u /p;/^-P /p;/^-w /p' /tmp/microsocks-readme
else
  echo 'Unable to retrieve microsocks README' >&2
fi

printf '%s\n' '--- microsocks bind and authentication implementation ---'
if curl -fsSL https://raw.githubusercontent.com/rofl0r/microsocks/master/microsocks.c > /tmp/microsocks-c; then
  rg -n -C 4 'listen_ip|bind|username|password|SOCKS5_USER|SOCKS5_PASS|getopt|case .[ipuPw]' /tmp/microsocks-c || true
else
  echo 'Unable to retrieve microsocks source' >&2
fi

printf '%s\n' '--- Makefile recipe expansion by static substitution ---'
python3 - <<'PY'
from pathlib import Path
text = Path("Makefile").read_text()
start = text.index("socks5-proxy:")
end = text.index("\n\n", start)
recipe = text[start:end]
print(recipe)
print("\nFor HOST=0.0.0.0, the final command arguments are:")
print("microsocks -i 0.0.0.0 -p 1080")
PY

Repository: maximhq/bifrost

Length of output: 972


🌐 Web query:

microsocks GitHub README -i listen IP -u username -P password -w whitelist

💡 Result:

Microsocks is a lightweight SOCKS5 server that provides command-line options for configuring its behavior, including listening address, port, authentication, and IP whitelisting [1][2]. All arguments are optional, and by default, it listens on 0.0.0.0 at port 1080 [1][3]. Below are the relevant command-line options based on the official documentation: -i listenip: Specifies the local IP address or hostname the server should listen on [2]. -p port: Sets the TCP port the server listens on [2]. -u username: Specifies the username for authentication [2]. This must be used in conjunction with the -P option [2]. -P password: Specifies the password for authentication [2]. This must be used in conjunction with the -u option [2]. -w whitelist: Allows a comma-separated list of IP addresses to use the proxy without authentication [1][2]. -1 (auth_once mode): Once a client from a specific IP address successfully authenticates with the provided username and password, that IP is added to a temporary whitelist and can subsequently use the proxy without further authentication [1][2]. This option requires both -u and -P to be specified [2]. To restrict access so that only specific whitelisted IPs can connect, users can configure an intentionally difficult or impossible-to-guess username/password combination along with the -w option [2]. For more detailed information, you can refer to the official repository at https://github.com/rofl0r/microsocks [1].

Citations:


Reject non-loopback SOCKS5 binds unless access control is configured.

HOST=0.0.0.0 starts microsocks without authentication or an allowlist, which can expose an open proxy. Validate HOST as loopback, or require authentication and an appropriate allowlist for non-loopback binds.

🤖 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 `@Makefile` around lines 159 - 164, Update the socks5-proxy target to reject
non-loopback HOST values before starting microsocks, unless the command
configures authentication and an appropriate allowlist. Preserve the existing
loopback defaults and startup behavior, and ensure unsafe values such as 0.0.0.0
cannot launch an unauthenticated open proxy.

Source: MCP tools

Comment thread Makefile
Comment on lines +162 to +163
$(ECHO) "$(GREEN)Starting SOCKS5 proxy on $$PROXY_HOST:$$PROXY_PORT (no auth, logs each connection, Ctrl+C to stop)...$(NC)"; \
$(ECHO) "$(YELLOW)Point a provider's proxy_config at socks5://$$PROXY_HOST:$$PROXY_PORT to test$(NC)"; \

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- repository status ---'
git status --short --branch

printf '%s\n' '--- concise recent commits ---'
git log --oneline -12

printf '%s\n' '--- stack indicators ---'
rg -n -i 'stack|stacked|depends.?on|parent|base branch|change.?id' \
  .github README.md CONTRIBUTING.md Makefile 2>/dev/null || true

printf '%s\n' '--- Makefile relevant sections ---'
cat -n Makefile | sed -n '135,190p'

printf '%s\n' '--- proxy URL references ---'
rg -n -C 3 'socks5://|http://.*PROXY|PROXY_HOST|PROXY_PORT' Makefile .

Repository: maximhq/bifrost

Length of output: 13974


🏁 Script executed:

#!/bin/bash
set -eu
cat -n Makefile | sed -n '135,190p'
printf '%s\n' '--- proxy URL references ---'
rg -n -C 3 'socks5://|http://.*PROXY|PROXY_HOST|PROXY_PORT' Makefile .
printf '%s\n' '--- concise history and status ---'
git status --short --branch
git log --oneline -12

Repository: maximhq/bifrost

Length of output: 11355


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
p = Path("Makefile")
lines = p.read_text().splitlines()
for start, end in ((145, 180),):
    for i in range(start, min(end, len(lines)) + 1):
        print(f"{i}: {lines[i-1]}")
PY

Repository: maximhq/bifrost

Length of output: 2861


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- HOST export declarations ---'
rg -n '(^|[[:space:]])export[[:space:]]+.*HOST|^HOST[[:space:]]*[:?+]?=' Makefile . \
  -g 'Makefile' -g '*.mk' 2>/dev/null || true

printf '%s\n' '--- standalone GNU make command-line propagation probe ---'
if command -v make >/dev/null 2>&1; then
  env -i PATH="$PATH" make -f - HOST='::1' <<'MAKE'
print:
	`@printf` 'shell HOST=<%s>\n' "$$HOST"
MAKE
else
  printf '%s\n' 'make unavailable'
fi

printf '%s\n' '--- standalone URI authority probe ---'
python3 - <<'PY'
from urllib.parse import urlsplit

for scheme, host, port in (
    ("socks5", "127.0.0.1", "1080"),
    ("http", "proxy.example", "8888"),
    ("socks5", "::1", "1080"),
    ("http", "2001:db8::1", "8888"),
):
    raw = f"{scheme}://{host}:{port}"
    bracketed = f"{scheme}://[{host}]:{port}" if ":" in host else raw
    for label, value in (("raw", raw), ("bracketed", bracketed)):
        try:
            parsed = urlsplit(value)
            print(label, value, "->", parsed.hostname, parsed.port)
        except ValueError as exc:
            print(label, value, "-> ValueError:", exc)
PY

Repository: maximhq/bifrost

Length of output: 857


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- Makefile header and variable definitions ---'
cat -n Makefile | sed -n '1,35p'
rg -n -C 2 '(^|[[:space:]])HOST([[:space:]]|$)|PROXY_HOST|socks5-proxy|http-proxy' Makefile

printf '%s\n' '--- current commit summary ---'
git show --stat --oneline --no-renames HEAD

printf '%s\n' '--- current commit Makefile patch ---'
git show --format= --no-ext-diff --unified=12 HEAD -- Makefile

Repository: maximhq/bifrost

Length of output: 11982


🌐 Web query:

GNU make manual command line variables exported to environment of recipe

💡 Result:

In GNU Make, variables defined on the command line are automatically exported to the environment of recipes executed by Make [1][2]. When Make runs a recipe, it places variables into the environment of the shell used to execute that recipe [1][2]. Variables that originate from the command line, as well as those that were present in the environment when Make was invoked, are automatically included in this environment [1][2]. For other variables defined within the Makefile, they are only exported to the environment of recipes if they are explicitly marked with the export directive [1][2]. It is important to note that this behavior applies specifically to recipe commands [3]. Historically, the $(shell...) function did not receive these exported variables; however, as of GNU Make 4.4, all exported variables are now properly exported to the environment of the $(shell...) function as well [4].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
cat -n Makefile | sed -n '1,35p'
git show --stat --oneline --no-renames HEAD
git show --format= --no-ext-diff --unified=12 HEAD -- Makefile

Repository: maximhq/bifrost

Length of output: 9483


Bracket IPv6 literals in the proxy messages.

When invoked with HOST=::1, the messages emit invalid URLs such as socks5://::1:1080. Use a display-only host that wraps IPv6 literals in [...] without changing PROXY_HOST passed to the proxy processes.

🤖 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 `@Makefile` around lines 162 - 163, Update the proxy startup messages around
the SOCKS5 host/port output to use a display-only host value that wraps IPv6
literals in brackets, producing valid URLs such as socks5://[::1]:1080. Keep
PROXY_HOST unchanged for the proxy processes and preserve unbracketed output for
non-IPv6 hosts.

Source: MCP tools

Comment thread Makefile
Comment on lines +173 to +178
CONF=$$(mktemp -t bifrost-tinyproxy); \
trap 'rm -f "$$CONF"' EXIT INT TERM; \
printf 'Port %s\nListen %s\nTimeout 600\nAllow 127.0.0.1\nAllow ::1\nLogLevel Info\n' "$$PROXY_PORT" "$$PROXY_HOST" > "$$CONF"; \
$(ECHO) "$(GREEN)Starting HTTP proxy on $$PROXY_HOST:$$PROXY_PORT (no auth, logs each connection, Ctrl+C to stop)...$(NC)"; \
$(ECHO) "$(YELLOW)Point a provider's proxy_config at http://$$PROXY_HOST:$$PROXY_PORT to test$(NC)"; \
tinyproxy -d -c "$$CONF"

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

for template in bifrost-tinyproxy bifrost-tinyproxy.XXXXXX; do
  if path="$(mktemp -t "$template" 2>/dev/null)"; then
    printf 'accepted: %s -> %s\n' "$template" "$path"
    rm -f "$path"
  else
    printf 'rejected: %s\n' "$template"
  fi
done

Repository: maximhq/bifrost

Length of output: 249


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- Makefile context ---'
sed -n '145,185p' Makefile

printf '%s\n' '--- stack and working-tree summary ---'
git status --short
git branch --show-current
git log --oneline -8

printf '%s\n' '--- mktemp implementations available ---'
command -v mktemp
mktemp --version 2>/dev/null | head -n 2 || true

Repository: maximhq/bifrost

Length of output: 3377


Use a portable temporary-file template and fail fast.

GNU/Linux rejects mktemp -t bifrost-tinyproxy because the template lacks three X characters. Use mktemp "$${TMPDIR:-/tmp}/bifrost-tinyproxy.XXXXXX" || exit 1, and add || exit 1 to printf before launching tinyproxy.

🤖 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 `@Makefile` around lines 173 - 178, Update the temporary configuration setup in
the Makefile recipe to use the portable mktemp template
"$${TMPDIR:-/tmp}/bifrost-tinyproxy.XXXXXX" and exit immediately if mktemp
fails. Also make the printf command that writes "$$CONF" fail fast with "|| exit
1" before starting tinyproxy.

Source: MCP tools

akshaydeo commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

  • Aug 3, 6:34 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Aug 3, 6:34 PM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo merged commit 34fbc09 into main Aug 3, 2026
13 of 14 checks passed
@akshaydeo
akshaydeo deleted the 08-02-add_proxy_support_for_realtime_calls_websocket_based_ branch August 3, 2026 18:34
atharvamhaske pushed a commit to atharvamhaske/bifrost that referenced this pull request Aug 13, 2026
## Summary

WebSocket connections (Realtime and Responses) were not routing through the provider-level proxy configuration. HTTP requests already respected `ProxyConfig`, but the WebSocket dial path bypassed it entirely, causing WebSocket traffic to go direct regardless of what proxy was configured.

## Changes

- Added `ConfigureWebSocketProxy` in `core/providers/utils/utils.go` that mirrors `ConfigureProxy` for `*ws.Dialer`, supporting HTTP, SOCKS5, env-based, and no-proxy configurations. Unlike `ConfigureProxy`, it returns an error directly rather than swapping in a failing dial func, since WebSocket dials are resolved fresh on every call.
- Updated `Dial`, `DialUpstream`, `Pool.Get`, and `Pool.dial` in the WebSocket transport to accept and apply a `*schemas.ProxyConfig`.
- Updated `WSRealtimeHandler.runRealtimeSession` and `WSResponsesHandler.tryNativeWSUpstream` to look up the provider's `ProxyConfig` and pass it through to the dial path.
- Added unit tests for `ConfigureWebSocketProxy` covering literal URL, env-backed URL, empty env value (fail-fast), nil config, `NoProxy`, and SOCKS5 cases.
- Added integration tests in `pool_test.go` with a real CONNECT-based forward proxy to verify dials actually route through the proxy (`TestPoolGetDialsThroughConfiguredHTTPProxy`) and that an unreachable proxy fails the dial rather than falling back silently (`TestPoolGetFailsWithUnreachableProxy`).
- Added an informational UI alert on the proxy configuration form clarifying that the proxy applies to HTTP and WebSocket connections but not WebRTC-based Realtime media paths.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Core/Transports
go test ./core/providers/utils/...
go test ./transports/bifrost-http/websocket/...

# UI
cd ui
pnpm i
pnpm build
```

Configure a provider with an HTTP or SOCKS5 proxy and open a Realtime or Responses WebSocket session. Verify traffic routes through the proxy (e.g., via proxy access logs or by pointing at a local intercepting proxy). Confirm that setting an unreachable proxy URL causes the connection to fail rather than silently connecting directly.

## Breaking changes

- [x] No

`Pool.Get`, `Pool.dial`, `Dial`, and `DialUpstream` all gained a `proxyConfig *schemas.ProxyConfig` parameter. Passing `nil` preserves the previous direct-dial behavior.

## Security considerations

Proxy credentials (`Username`, `Password`) are sourced from `SecretVar` and embedded into the parsed proxy URL only when both are non-empty. CA certificate PEM for proxy TLS is validated at dial time and fails fast if an env-backed secret resolves to an empty value, preventing silent misconfiguration.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
akhsaul pushed a commit to akhsaul/bifrost that referenced this pull request Aug 27, 2026
## Summary

WebSocket connections (Realtime and Responses) were not routing through the provider-level proxy configuration. HTTP requests already respected `ProxyConfig`, but the WebSocket dial path bypassed it entirely, causing WebSocket traffic to go direct regardless of what proxy was configured.

## Changes

- Added `ConfigureWebSocketProxy` in `core/providers/utils/utils.go` that mirrors `ConfigureProxy` for `*ws.Dialer`, supporting HTTP, SOCKS5, env-based, and no-proxy configurations. Unlike `ConfigureProxy`, it returns an error directly rather than swapping in a failing dial func, since WebSocket dials are resolved fresh on every call.
- Updated `Dial`, `DialUpstream`, `Pool.Get`, and `Pool.dial` in the WebSocket transport to accept and apply a `*schemas.ProxyConfig`.
- Updated `WSRealtimeHandler.runRealtimeSession` and `WSResponsesHandler.tryNativeWSUpstream` to look up the provider's `ProxyConfig` and pass it through to the dial path.
- Added unit tests for `ConfigureWebSocketProxy` covering literal URL, env-backed URL, empty env value (fail-fast), nil config, `NoProxy`, and SOCKS5 cases.
- Added integration tests in `pool_test.go` with a real CONNECT-based forward proxy to verify dials actually route through the proxy (`TestPoolGetDialsThroughConfiguredHTTPProxy`) and that an unreachable proxy fails the dial rather than falling back silently (`TestPoolGetFailsWithUnreachableProxy`).
- Added an informational UI alert on the proxy configuration form clarifying that the proxy applies to HTTP and WebSocket connections but not WebRTC-based Realtime media paths.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Core/Transports
go test ./core/providers/utils/...
go test ./transports/bifrost-http/websocket/...

# UI
cd ui
pnpm i
pnpm build
```

Configure a provider with an HTTP or SOCKS5 proxy and open a Realtime or Responses WebSocket session. Verify traffic routes through the proxy (e.g., via proxy access logs or by pointing at a local intercepting proxy). Confirm that setting an unreachable proxy URL causes the connection to fail rather than silently connecting directly.

## Breaking changes

- [x] No

`Pool.Get`, `Pool.dial`, `Dial`, and `DialUpstream` all gained a `proxyConfig *schemas.ProxyConfig` parameter. Passing `nil` preserves the previous direct-dial behavior.

## Security considerations

Proxy credentials (`Username`, `Password`) are sourced from `SecretVar` and embedded into the parsed proxy URL only when both are non-empty. CA certificate PEM for proxy TLS is validated at dial time and fails fast if an env-backed secret resolves to an empty value, preventing silent misconfiguration.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
occcat pushed a commit to occcat/bifrost that referenced this pull request Sep 2, 2026
## Summary

WebSocket connections (Realtime and Responses) were not routing through the provider-level proxy configuration. HTTP requests already respected `ProxyConfig`, but the WebSocket dial path bypassed it entirely, causing WebSocket traffic to go direct regardless of what proxy was configured.

## Changes

- Added `ConfigureWebSocketProxy` in `core/providers/utils/utils.go` that mirrors `ConfigureProxy` for `*ws.Dialer`, supporting HTTP, SOCKS5, env-based, and no-proxy configurations. Unlike `ConfigureProxy`, it returns an error directly rather than swapping in a failing dial func, since WebSocket dials are resolved fresh on every call.
- Updated `Dial`, `DialUpstream`, `Pool.Get`, and `Pool.dial` in the WebSocket transport to accept and apply a `*schemas.ProxyConfig`.
- Updated `WSRealtimeHandler.runRealtimeSession` and `WSResponsesHandler.tryNativeWSUpstream` to look up the provider's `ProxyConfig` and pass it through to the dial path.
- Added unit tests for `ConfigureWebSocketProxy` covering literal URL, env-backed URL, empty env value (fail-fast), nil config, `NoProxy`, and SOCKS5 cases.
- Added integration tests in `pool_test.go` with a real CONNECT-based forward proxy to verify dials actually route through the proxy (`TestPoolGetDialsThroughConfiguredHTTPProxy`) and that an unreachable proxy fails the dial rather than falling back silently (`TestPoolGetFailsWithUnreachableProxy`).
- Added an informational UI alert on the proxy configuration form clarifying that the proxy applies to HTTP and WebSocket connections but not WebRTC-based Realtime media paths.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Core/Transports
go test ./core/providers/utils/...
go test ./transports/bifrost-http/websocket/...

# UI
cd ui
pnpm i
pnpm build
```

Configure a provider with an HTTP or SOCKS5 proxy and open a Realtime or Responses WebSocket session. Verify traffic routes through the proxy (e.g., via proxy access logs or by pointing at a local intercepting proxy). Confirm that setting an unreachable proxy URL causes the connection to fail rather than silently connecting directly.

## Breaking changes

- [x] No

`Pool.Get`, `Pool.dial`, `Dial`, and `DialUpstream` all gained a `proxyConfig *schemas.ProxyConfig` parameter. Passing `nil` preserves the previous direct-dial behavior.

## Security considerations

Proxy credentials (`Username`, `Password`) are sourced from `SecretVar` and embedded into the parsed proxy URL only when both are non-empty. CA certificate PEM for proxy TLS is validated at dial time and fails fast if an env-backed secret resolves to an empty value, preventing silent misconfiguration.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant