add proxy support for realtime calls (websocket based) - #5788
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughWebSocket 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. ChangesWebSocket proxy support
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
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
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
📒 Files selected for processing (8)
core/providers/utils/proxy_test.gocore/providers/utils/utils.gotransports/bifrost-http/handlers/wsrealtime.gotransports/bifrost-http/handlers/wsresponses.gotransports/bifrost-http/websocket/connection.gotransports/bifrost-http/websocket/pool.gotransports/bifrost-http/websocket/pool_test.goui/app/workspace/providers/fragments/proxyFormFragment.tsx
3cacee3 to
8ff1080
Compare
8ff1080 to
661c00c
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
core/providers/utils/utils.go (1)
632-639: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winFail closed instead of silently dialing direct when an explicit proxy has no URL.
For
HTTPProxyandSocks5Proxy, ifproxyURLValueis empty, the code logs a warning and returnsdialer, nilat Line 638, leavingdialer.Proxyunset. 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 winConsider sharing URL/credential-resolution logic with
ConfigureProxy.
ConfigureWebSocketProxyduplicates the secret-resolution check, URL parsing, and username/password merging already present inConfigureProxy(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, andUserPasswordmerge, 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 notConfigureProxy.🤖 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
⛔ Files ignored due to path filters (14)
core/go.sumis excluded by!**/*.sumframework/go.sumis excluded by!**/*.sumplugins/compat/go.sumis excluded by!**/*.sumplugins/governance/go.sumis excluded by!**/*.sumplugins/jsonparser/go.sumis excluded by!**/*.sumplugins/logging/go.sumis excluded by!**/*.sumplugins/maxim/go.sumis excluded by!**/*.sumplugins/mocker/go.sumis excluded by!**/*.sumplugins/modelcatalogresolver/go.sumis excluded by!**/*.sumplugins/otel/go.sumis excluded by!**/*.sumplugins/prompts/go.sumis excluded by!**/*.sumplugins/semanticcache/go.sumis excluded by!**/*.sumplugins/telemetry/go.sumis excluded by!**/*.sumtransports/go.sumis excluded by!**/*.sum
📒 Files selected for processing (26)
Makefilecore/go.modcore/providers/utils/proxy_test.gocore/providers/utils/utils.goframework/go.modplugins/compat/go.modplugins/governance/go.modplugins/jsonparser/go.modplugins/logging/go.modplugins/maxim/go.modplugins/mocker/go.modplugins/modelcatalogresolver/go.modplugins/otel/go.modplugins/prompts/go.modplugins/semanticcache/go.modplugins/telemetry/go.modtests/cmd/e2eseed/go.modtests/cmd/seed/go.modtests/cmd/seedvks/go.modtransports/bifrost-http/handlers/wsrealtime.gotransports/bifrost-http/handlers/wsresponses.gotransports/bifrost-http/websocket/connection.gotransports/bifrost-http/websocket/pool.gotransports/bifrost-http/websocket/pool_test.gotransports/go.modui/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
661c00c to
38b8f75
Compare
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (18)
core/go.sumis excluded by!**/*.sumframework/go.sumis excluded by!**/*.sumplugins/compat/go.sumis excluded by!**/*.sumplugins/governance/go.sumis excluded by!**/*.sumplugins/jsonparser/go.sumis excluded by!**/*.sumplugins/logging/go.sumis excluded by!**/*.sumplugins/maxim/go.sumis excluded by!**/*.sumplugins/mocker/go.sumis excluded by!**/*.sumplugins/modelcatalogresolver/go.sumis excluded by!**/*.sumplugins/otel/go.sumis excluded by!**/*.sumplugins/prompts/go.sumis excluded by!**/*.sumplugins/semanticcache/go.sumis excluded by!**/*.sumplugins/telemetry/go.sumis excluded by!**/*.sumscripts/realtime-test/go.sumis excluded by!**/*.sumtests/cmd/e2eseed/go.sumis excluded by!**/*.sumtests/cmd/seed/go.sumis excluded by!**/*.sumtests/cmd/seedvks/go.sumis excluded by!**/*.sumtransports/go.sumis excluded by!**/*.sum
📒 Files selected for processing (29)
Makefilecore/go.modcore/providers/utils/proxy_test.gocore/providers/utils/utils.goframework/go.modplugins/compat/go.modplugins/governance/go.modplugins/jsonparser/go.modplugins/logging/go.modplugins/maxim/go.modplugins/mocker/go.modplugins/modelcatalogresolver/go.modplugins/otel/go.modplugins/prompts/go.modplugins/semanticcache/go.modplugins/telemetry/go.modscripts/realtime-test/go.modscripts/realtime-test/main.gotests/cmd/e2eseed/go.modtests/cmd/seed/go.modtests/cmd/seedvks/go.modtransports/bifrost-http/handlers/wsrealtime.gotransports/bifrost-http/handlers/wsresponses.gotransports/bifrost-http/tests/realtime/realtime_ws_test.gotransports/bifrost-http/websocket/connection.gotransports/bifrost-http/websocket/pool.gotransports/bifrost-http/websocket/pool_test.gotransports/go.modui/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
38b8f75 to
cd727fd
Compare
cd727fd to
f48d195
Compare
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (18)
core/go.sumis excluded by!**/*.sumframework/go.sumis excluded by!**/*.sumplugins/compat/go.sumis excluded by!**/*.sumplugins/governance/go.sumis excluded by!**/*.sumplugins/jsonparser/go.sumis excluded by!**/*.sumplugins/logging/go.sumis excluded by!**/*.sumplugins/maxim/go.sumis excluded by!**/*.sumplugins/mocker/go.sumis excluded by!**/*.sumplugins/modelcatalogresolver/go.sumis excluded by!**/*.sumplugins/otel/go.sumis excluded by!**/*.sumplugins/prompts/go.sumis excluded by!**/*.sumplugins/semanticcache/go.sumis excluded by!**/*.sumplugins/telemetry/go.sumis excluded by!**/*.sumscripts/realtime-test/go.sumis excluded by!**/*.sumtests/cmd/e2eseed/go.sumis excluded by!**/*.sumtests/cmd/seed/go.sumis excluded by!**/*.sumtests/cmd/seedvks/go.sumis excluded by!**/*.sumtransports/go.sumis excluded by!**/*.sum
📒 Files selected for processing (29)
Makefilecore/go.modcore/providers/utils/proxy_test.gocore/providers/utils/utils.goframework/go.modplugins/compat/go.modplugins/governance/go.modplugins/jsonparser/go.modplugins/logging/go.modplugins/maxim/go.modplugins/mocker/go.modplugins/modelcatalogresolver/go.modplugins/otel/go.modplugins/prompts/go.modplugins/semanticcache/go.modplugins/telemetry/go.modscripts/realtime-test/go.modscripts/realtime-test/main.gotests/cmd/e2eseed/go.modtests/cmd/seed/go.modtests/cmd/seedvks/go.modtransports/bifrost-http/handlers/wsrealtime.gotransports/bifrost-http/handlers/wsresponses.gotransports/bifrost-http/tests/realtime/realtime_ws_test.gotransports/bifrost-http/websocket/connection.gotransports/bifrost-http/websocket/pool.gotransports/bifrost-http/websocket/pool_test.gotransports/go.modui/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
| 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 |
There was a problem hiding this comment.
🔒 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
fiRepository: 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*' \) -printRepository: 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*' \) -printRepository: 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")
PYRepository: 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:
- 1: https://github.com/rofl0r/microsocks
- 2: https://github.com/rofl0r/microsocks/blob/master/microsocks.1
- 3: https://github.com/itsjfx/microsocks
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
| $(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)"; \ |
There was a problem hiding this comment.
🎯 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 -12Repository: 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]}")
PYRepository: 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)
PYRepository: 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 -- MakefileRepository: 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
Citations:
- 1: https://stackoverflow.com/questions/39036774/advanced-variable-inheritance-in-gnu-make
- 2: https://www.chiark.greenend.org.uk/doc/make-doc/make.html/Using-Variables.html
- 3: https://stackoverflow.com/questions/2838715/makefile-variable-initialization-and-export
- 4: https://lists.gnu.org/archive/html/info-gnu/2022-10/msg00008.html
🏁 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 -- MakefileRepository: 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
| 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" |
There was a problem hiding this comment.
🩺 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
doneRepository: 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 || trueRepository: 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
Merge activity
|
## 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
## 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
## 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

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
ConfigureWebSocketProxyincore/providers/utils/utils.gothat mirrorsConfigureProxyfor*ws.Dialer, supporting HTTP, SOCKS5, env-based, and no-proxy configurations. UnlikeConfigureProxy, it returns an error directly rather than swapping in a failing dial func, since WebSocket dials are resolved fresh on every call.Dial,DialUpstream,Pool.Get, andPool.dialin the WebSocket transport to accept and apply a*schemas.ProxyConfig.WSRealtimeHandler.runRealtimeSessionandWSResponsesHandler.tryNativeWSUpstreamto look up the provider'sProxyConfigand pass it through to the dial path.ConfigureWebSocketProxycovering literal URL, env-backed URL, empty env value (fail-fast), nil config,NoProxy, and SOCKS5 cases.pool_test.gowith 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).Type of change
Affected areas
How to test
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
Pool.Get,Pool.dial,Dial, andDialUpstreamall gained aproxyConfig *schemas.ProxyConfigparameter. Passingnilpreserves the previous direct-dial behavior.Security considerations
Proxy credentials (
Username,Password) are sourced fromSecretVarand 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
docs/contributing/README.mdand followed the guidelines