Skip to content

feat: local bypass proxy for kindling traffic#341

Merged
myleshorton merged 4 commits intomainfrom
feat/bypass-proxy-for-kindling
Feb 25, 2026
Merged

feat: local bypass proxy for kindling traffic#341
myleshorton merged 4 commits intomainfrom
feat/bypass-proxy-for-kindling

Conversation

@myleshorton
Copy link
Contributor

Summary

  • New bypass package (bypass/bypass.go): Stateless dialer that routes TCP connections through a local HTTP CONNECT proxy at 127.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 mixed inbound (vpn/boxoptions.go): Adds a TypeMixed inbound on 127.0.0.1:14985 tagged bypass-in, with a routing rule that sends all bypass-in traffic to the direct outbound (protected socket on the physical interface).
  • AMP dialer (kindling/fronted/amp.go): Replaces tls.Dial() with bypass.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.
  • Smart transport (kindling/smart/client.go): Wraps the smart transport's DialContext with bypass.DialContext in 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 protected direct outbound, eliminating domain-list maintenance.

Test plan

  • go build ./... passes (verified)
  • go vet clean on all affected packages (verified)
  • go test ./kindling/... passes (verified)
  • VPN integration: start tunnel, verify mixed inbound listening on 127.0.0.1:14985, make a kindling request and confirm it exits the physical interface
  • Verify fallback: with VPN off, kindling requests still work via direct dial
  • Test on Android/iOS to confirm bypass works without process-based matching

🤖 Generated with Claude Code

…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>
Copilot AI review requested due to automatic review settings February 25, 2026 17:11
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 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 bypass package 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 to direct.
  • 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>
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

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.

Copy link
Collaborator

@garmr-ulfr garmr-ulfr left a comment

Choose a reason for hiding this comment

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

LGTM

… 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>
@myleshorton
Copy link
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>
@myleshorton myleshorton merged commit ae521fa into main Feb 25, 2026
2 checks passed
@myleshorton myleshorton deleted the feat/bypass-proxy-for-kindling branch February 25, 2026 18:39
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.

3 participants