feat: local bypass proxy for kindling traffic#341
Merged
myleshorton merged 4 commits intomainfrom Feb 25, 2026
Merged
Conversation
…nnel On mobile (Android/iOS), process-based bypass isn't available, so kindling's HTTP requests (config fetches, server discovery via AMP/smart transport) get captured by the TUN device and loop back through the VPN. This adds a local HTTP CONNECT proxy (mixed inbound on 127.0.0.1:14985) in the VPN process that routes kindling connections directly via the physical interface, replacing the fragile domain-suffix approach for these transports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a local HTTP CONNECT “bypass” path so Kindling-originated traffic can escape the VPN tunnel on mobile by routing through a loopback sing-box inbound that is forced to the protected direct outbound.
Changes:
- Introduces a new
bypasspackage implementing a proxy-first dialer with direct-dial fallback. - Extends VPN sing-box options with a loopback mixed inbound (
127.0.0.1:14985) and a routing rule to send that inbound todirect. - Switches Kindling AMP + smart transport dialing to use the bypass dialer for the TCP layer.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
bypass/bypass.go |
New dialer that connects via local CONNECT proxy and falls back to direct. |
vpn/boxoptions.go |
Adds mixed inbound on loopback + routing rule for bypass inbound tag. |
kindling/fronted/amp.go |
Routes AMP TCP dials through bypass.Dial() then wraps with TLS. |
kindling/smart/client.go |
Wraps smart transport DialContext with bypass.DialContext. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- bypass.go: fix httpConnect to set URL on http.Request (was nil, would fail at runtime); honor ctx deadline during CONNECT handshake and clear it afterward; export ProxyPort constant to centralize the port number - amp.go: rename hasSemicolon → hasPort for clarity; pass through network arg instead of hard-coding "tcp"; use context with timeout for TLS handshake instead of context.Background() - boxoptions.go: use bypass.ProxyPort instead of duplicated literal 14985 - bypass_test.go: add unit tests for proxy routing, direct fallback, context cancellation, and bad CONNECT status Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… conn - Rewrite tests to use httptest.Server instead of manual TCP proxy - Use testify require/assert instead of raw t.Fatalf - Fix bufio.NewReader buffering: wrap conn so buffered bytes aren't lost - Wait for both copy directions in mock proxy before closing - Add slog.Debug logging when falling back to direct dial Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
Author
|
OK merging! |
The bypass proxy on port 14985 now handles routing kindling traffic outside the VPN tunnel, so the old mechanisms are no longer needed: - Remove inline direct domain rule set (iantem.io, cloudfront.net, etc.) - Remove Lantern process path regex matching for desktop platforms - Remove directTag rule set from split tunnel routing rule Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
bypasspackage (bypass/bypass.go): Stateless dialer that routes TCP connections through a local HTTP CONNECT proxy at127.0.0.1:14985. Falls back to direct dial when the proxy isn't running (VPN off), so behavior is unchanged outside the VPN.vpn/boxoptions.go): Adds aTypeMixedinbound on127.0.0.1:14985taggedbypass-in, with a routing rule that sends all bypass-in traffic to thedirectoutbound (protected socket on the physical interface).kindling/fronted/amp.go): Replacestls.Dial()withbypass.Dial()for the TCP layer, then wraps with TLS on top — preserving AMP's domain fronting while routing the underlying connection through the bypass proxy.kindling/smart/client.go): Wraps the smart transport'sDialContextwithbypass.DialContextin both the initial creation and lazy-dialing paths.Why
On mobile (Android/iOS), process-based VPN bypass isn't available. Kindling's HTTP requests (config fetches, server discovery via AMP/smart transport) get captured by the TUN device and loop through the VPN tunnel. The current workaround is a domain suffix list (
inlineDirectRuleSet), but this is fragile and incomplete (e.g., AMP's Google domains are missing). This PR routes all kindling connections through a local bypass proxy in the VPN process that uses sing-box's protecteddirectoutbound, eliminating domain-list maintenance.Test plan
go build ./...passes (verified)go vetclean on all affected packages (verified)go test ./kindling/...passes (verified)127.0.0.1:14985, make a kindling request and confirm it exits the physical interface🤖 Generated with Claude Code