Skip to content

Add direct transport context for unbounded signaling#200

Merged
myleshorton merged 2 commits intonelson/unbounded-integrationfrom
afisk/unbounded-direct-transport
Mar 25, 2026
Merged

Add direct transport context for unbounded signaling#200
myleshorton merged 2 commits intonelson/unbounded-integrationfrom
afisk/unbounded-direct-transport

Conversation

@myleshorton
Copy link
Contributor

Summary

  • Adds adapter.ContextWithDirectTransport / DirectTransportFromContext for injecting an http.RoundTripper that bypasses the VPN tunnel
  • Unbounded's WebRTC signaling HTTP client now uses the direct transport when registered, falling back to the outbound dialer otherwise

Motivation

Unbounded needs kindling (censorship-evasion smart transport) for its signaling HTTP requests. Kindling lives in radiance, not lantern-box. This PR provides the context-based injection point so radiance can wire kindling's transport (backed by sing-box's direct outbound with proper socket protection) into unbounded without creating a circular dependency.

Radiance side: After tunnel Start(), radiance will retrieve the direct outbound, build a kindling-powered http.RoundTripper using its DialContext, and register it via adapter.ContextWithDirectTransport(ctx, rt) before the service is created.

How it bypasses the tunnel on each platform:

Platform Mechanism
Desktop (daemon) Daemon's own traffic doesn't enter TUN
Android VpnService.protect(fd) via PlatformInterface.AutoDetectInterfaceControl
iOS/macOS IP_BOUND_IF / IPV6_BOUND_IF socket option
Linux SO_BINDTOIFINDEX socket option

Test plan

  • Existing unbounded tests pass (fallback path unchanged)
  • Verify DirectTransportFromContext returns nil when nothing registered
  • Integration test with radiance registering a transport (future PR)

🤖 Generated with Claude Code

Introduce adapter.ContextWithDirectTransport / DirectTransportFromContext
so that the host application (radiance) can inject an http.RoundTripper
whose DialContext is wired through sing-box's direct outbound. This
ensures unbounded's WebRTC signaling HTTP requests bypass the VPN tunnel
on all platforms via the existing socket protection mechanisms
(VpnService.protect on Android, IP_BOUND_IF on iOS/macOS, etc.).

The unbounded outbound now checks for this direct transport at
construction time and falls back to the outbound dialer when none is
registered (preserving existing behavior in tests and standalone usage).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a context-based injection point for a “direct” http.RoundTripper so Unbounded’s WebRTC signaling HTTP requests can bypass the VPN tunnel when radiance registers a kindling-backed transport, while preserving existing fallback behavior.

Changes:

  • Introduce adapter.ContextWithDirectTransport / adapter.DirectTransportFromContext for wiring a bypass transport via context.Context.
  • Update Unbounded outbound initialization to prefer the injected direct transport for signaling HTTP, falling back to the outbound dialer-backed transport.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
protocol/unbounded/outbound.go Uses an injected direct http.RoundTripper for signaling when present; otherwise retains the existing dialer-based transport.
adapter/direct_transport.go Adds context helpers to register/retrieve a direct http.RoundTripper for tunnel-bypass HTTP flows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

myleshorton added a commit to getlantern/radiance that referenced this pull request Mar 24, 2026
Add lazyDirectTransport, an http.RoundTripper that wraps sing-box's
direct outbound dialer. It is registered in the context before the
sing-box service is created (so unbounded can retrieve it during
construction) and resolved after (once the direct outbound exists).

The direct outbound's dialer carries platform-specific socket protection
(VpnService.protect on Android, IP_BOUND_IF on iOS/macOS, etc.),
ensuring that unbounded's signaling traffic bypasses the VPN tunnel on
all platforms.

Depends on getlantern/lantern-box#200

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DialContext is sufficient; the Dial field was deprecated and captured
the outer context which is misleading.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@myleshorton
Copy link
Contributor Author

Bringing this in, as it's needed and harmless.

@myleshorton myleshorton merged commit 22d3546 into nelson/unbounded-integration Mar 25, 2026
1 check passed
@myleshorton myleshorton deleted the afisk/unbounded-direct-transport branch March 25, 2026 10:34
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.

2 participants