Skip to content

meek: add a first-class meek server inbound#283

Merged
myleshorton merged 5 commits into
mainfrom
fisk/meek-inbound
Jun 27, 2026
Merged

meek: add a first-class meek server inbound#283
myleshorton merged 5 commits into
mainfrom
fisk/meek-inbound

Conversation

@myleshorton

@myleshorton myleshorton commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

What

Makes meek a first-class sing-box inbound, so meek-servers can be provisioned through the lantern-cloud track/bandit pipeline (one origin VM per route) instead of only the hand-deployed standalone cmd/meek-server.

Why now

A meek bandit track needs ≥1 route to be assignable — the bandit catalog reads arms from vps_routes and bails when there are zero launch configs, so a routeless connect-only track is never assigned (verified: a 0-route meek_0.0.1 track returned meek in 0/6 /config-new probes). A first-class inbound gives meek real, provisionable routes, and is also the mechanism to stand meek-servers up behind additional CDNs (CloudFront/Aliyun).

Data path

sequenceDiagram
    autonumber
    participant C as spark client<br/>meek outbound
    participant CDN as CDN edge<br/>Akamai/CF/Aliyun
    participant TLS as Caddy<br/>TLS term
    participant IN as meek inbound<br/>inbound.go
    participant SK as loopback SOCKS5<br/>handleSocks
    participant R as sing-box router
    participant D as destination

    C->>CDN: HTTPS POST, fronted SNI + per-CDN inner Host
    CDN->>TLS: forward to origin
    TLS->>IN: plain HTTP meek-v1 poll
    Note over IN: meek Server pipes session bytes<br/>to Upstream = loopback SOCKS5
    IN->>SK: dial loopback, per session
    Note over C,SK: client opened the session<br/>with a SOCKS5 CONNECT
    SK->>SK: terminate CONNECT, extract dst
    SK->>R: RouteConnectionEx dst
    R->>D: dial via configured outbound
    D-->>C: bytes flow back through the poll loop
Loading

Design

  • Reuses the existing meek-v1 Server unchanged (sessions, seq-dedup, holdoff). The inbound just serves it over a sing-box listener and swaps what its Upstream points at.
  • Plain HTTP on the listener; TLS stays at Caddy/the CDN. The inbound doesn't terminate TLS — fronting + TLS are handled in front, exactly as the standalone server is deployed today.
  • Drops microsocks. The bundled meek outbound opens every session with a SOCKS5 CONNECT, so the inbound runs a tiny in-process SOCKS5 acceptor on loopback as the meek server's upstream (handleSocks): it terminates the CONNECT, extracts the destination, and hands the post-CONNECT stream to router.RouteConnectionEx. So the data plane is native sing-box — routing rules, the configured outbound, and metrics all apply.
  • Same constant.TypeMeek ("meek") as the outbound — inbound and outbound live in separate registries, mirroring samizdat.

Changes

  • option/meek.goMeekInboundOptions (ListenOptions + meek tunables + http.Server timeouts).
  • protocol/meek/inbound.goInbound adapter + the SOCKS5-terminate-and-route glue.
  • protocol/register.go — register the meek inbound (registerInbounds).
  • protocol/meek/inbound_test.go — a SOCKS5 CONNECT routes to the right destination and pipes bytes; a non-CONNECT command is refused, not routed.

Testing

go build ./... clean; go vet ./protocol/meek/ clean; go test ./protocol/meek/ ./protocol/ pass (incl. the new tests and the register.go init that collects supportedProtocols).

Follow-ups (not in this PR)

  • lantern-cloud pcfg.generateMeek to emit this inbound's launch config (non-empty) so the meek track provisions real origin routes → makes meek-akamai-free assignable.
  • Per-CDN inner host in the client (flint scanner + spark) so each CDN fronts to its own meek host.
  • Stand up CloudFront + Aliyun meek deployments; retire the standalone cmd/meek-server once tracks provision origins.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UNCcmviAbCgJtG32Ry3ia2

Summary by CodeRabbit

  • New Features
    • Added a new Meek inbound mode with configurable request body limits, response holdoff, and session timeouts, plus HTTP read/write/idle timeouts.
    • Enforced Meek inbound relay authentication via an auth token, with an explicit option to allow unauthenticated operation.
  • Tests
    • Added unit tests validating auth-token requirements and SOCKS5 CONNECT routing (including payload round-trip verification), plus tests ensuring non-CONNECT commands are rejected.

Make meek a registered sing-box inbound so meek-servers can be provisioned
through the track/bandit pipeline (one origin VM per route) instead of the
hand-deployed standalone cmd/meek-server — and so a meek track has real routes
to contribute bandit arms.

The inbound serves the existing meek-v1 Server (HTTP polling) on a sing-box
listener (plain HTTP — a CDN/Caddy terminates TLS + fronting in front). The
bundled meek outbound opens each session with a SOCKS5 CONNECT, so the inbound
runs a tiny in-process SOCKS5 acceptor on loopback as the meek server's
upstream: it terminates the CONNECT and routes the stream via the sing-box
router. This drops the external microsocks dependency — routing rules, the
configured outbound, and metrics now apply to meek traffic natively.

- option: MeekInboundOptions (ListenOptions + meek tunables + http timeouts)
- protocol/meek/inbound.go: Inbound adapter + SOCKS5-terminate-and-route glue
- protocol/register.go: register the meek inbound
- tests: SOCKS5 CONNECT routes to the right destination + pipes bytes;
  non-CONNECT is refused, not routed

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9beSsYGzUaBhRK5ULmtGr
Copilot AI review requested due to automatic review settings June 26, 2026 17:28
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b96b1db6-f77d-451c-b34b-0576163d093b

📥 Commits

Reviewing files that changed from the base of the PR and between 9cbd702 and 7081a7b.

📒 Files selected for processing (1)
  • protocol/meek/inbound.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • protocol/meek/inbound.go

📝 Walkthrough

Walkthrough

Adds Meek inbound configuration, registers the inbound protocol, implements the HTTP and loopback SOCKS bridge, and adds tests for SOCKS5 CONNECT routing and rejection paths.

Changes

Meek inbound registration and routing

Layer / File(s) Summary
Options and registration
option/meek.go, protocol/meek/inbound.go, protocol/register.go
Adds MeekInboundOptions and registers the Meek inbound protocol in the inbound registry.
Inbound setup and lifecycle
protocol/meek/inbound.go
Defines the inbound state, constructs the HTTP server and loopback SOCKS listener, starts both, accepts loopback SOCKS connections, and closes owned listeners.
SOCKS CONNECT routing
protocol/meek/inbound.go
Handles SOCKS5 auth and CONNECT requests, preserves buffered bytes, and routes accepted tunnels with inbound context.
SOCKS handling tests
protocol/meek/inbound_test.go
Adds a mock router plus tests for auth-token validation, CONNECT routing, non-CONNECT rejection, and SOCKS reply parsing.

Sequence Diagram(s)

sequenceDiagram
  participant Inbound
  participant httpServer as http.Server
  participant loopbackSocks as loopback SOCKS listener
  participant meekServer as meek server
  participant router as adapter.ConnectionRouterEx
  Inbound->>httpServer: Serve meek handler
  httpServer->>meekServer: handle HTTP requests
  Inbound->>loopbackSocks: accept SOCKS connections
  loopbackSocks->>router: RouteConnectionEx after CONNECT
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A bunny hopped through meek new trails,
Through SOCKS and tunnels, bytey sails 🐇
The inbound hummed, the router spun,
CONNECT was caught, and tests were won.
I twitch my nose — the burrow glows.

Possibly related PRs

  • getlantern/lantern-box#265: Introduced meek server logic that this PR’s inbound adapter builds on for HTTP forwarding and routing.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a first-class meek server inbound.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fisk/meek-inbound

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

Copilot AI 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.

Pull request overview

Introduces a first-class meek inbound to lantern-box/sing-box so meek servers can be provisioned via the standard track/bandit pipeline (instead of relying on the standalone cmd/meek-server deployment model).

Changes:

  • Registers a new "meek" inbound protocol alongside existing inbounds.
  • Adds MeekInboundOptions to configure a plain-HTTP meek-v1 server endpoint and HTTP server timeouts.
  • Implements the meek inbound adapter, including an in-process loopback SOCKS5 acceptor that terminates CONNECT and routes via RouteConnectionEx, plus tests for the SOCKS5 routing/refusal behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
protocol/register.go Registers the meek inbound in the inbound registry.
protocol/meek/inbound.go Adds the meek inbound implementation and loopback SOCKS5-to-router glue.
protocol/meek/inbound_test.go Adds tests validating CONNECT routing and non-CONNECT refusal.
option/meek.go Adds MeekInboundOptions for configuring meek inbound + HTTP timeouts.

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

Comment thread protocol/meek/inbound.go
Comment thread protocol/meek/inbound.go
Comment thread protocol/meek/inbound.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
protocol/meek/inbound_test.go (1)

90-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add one pipelined CONNECT-plus-payload test.

bufferedConn is here to preserve bytes already read past the SOCKS request, but the current success test sends app data only after the CONNECT handshake completes. A client can coalesce the CONNECT request and first tunneled bytes into one TCP write; that is the path most likely to regress here, and it is still untested.

Also applies to: 152-176

🤖 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 `@protocol/meek/inbound_test.go` around lines 90 - 100, The current inbound
SOCKS test only verifies app data sent after the CONNECT handshake, so it misses
the bufferedConn path where CONNECT and tunneled payload arrive in one TCP
write. Add a new test around the existing inbound test flow in connect handling
to send the SOCKS CONNECT request plus initial payload together, then verify the
payload is preserved and read back correctly after readConnectReply and the
routed conn setup. Use the existing bufferedConn, WriteRequest, and
readConnectReply flow as the reference points for where to extend coverage.
🤖 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 `@protocol/meek/inbound.go`:
- Around line 112-118: The meek inbound provisioning path currently passes
options.AuthToken into NewServer without enforcing it, which allows the default
empty-token test mode. Update the inbound setup in the meek server creation flow
to reject empty auth_token values for provisioned/public origins, or add an
explicit opt-in flag for no-auth mode and wire that through NewServer. Use the
NewServer call in inbound.go and the AuthToken field in ServerConfig as the main
points to adjust.
- Around line 151-156: The SOCKS accept loop in acceptSocks currently exits on
any Accept error, which can leave the inbound half-alive while the HTTP side
keeps running. Update acceptSocks on Inbound to only return when the listener is
actually shutting down, and for unexpected socksLn.Accept failures, log the
error and continue the loop instead of permanently stopping. Use the existing
acceptSocks and socksLn symbols to keep the change localized.

---

Nitpick comments:
In `@protocol/meek/inbound_test.go`:
- Around line 90-100: The current inbound SOCKS test only verifies app data sent
after the CONNECT handshake, so it misses the bufferedConn path where CONNECT
and tunneled payload arrive in one TCP write. Add a new test around the existing
inbound test flow in connect handling to send the SOCKS CONNECT request plus
initial payload together, then verify the payload is preserved and read back
correctly after readConnectReply and the routed conn setup. Use the existing
bufferedConn, WriteRequest, and readConnectReply flow as the reference points
for where to extend coverage.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4ff2f0f5-2ed1-4d65-86b3-0fd52d5e3517

📥 Commits

Reviewing files that changed from the base of the PR and between d2f270e and 4464158.

📒 Files selected for processing (4)
  • option/meek.go
  • protocol/meek/inbound.go
  • protocol/meek/inbound_test.go
  • protocol/register.go

Comment thread protocol/meek/inbound.go Outdated
Comment thread protocol/meek/inbound.go
… auth)

- Route meek-server logs through the inbound's sing-box logger via
  log.NewLogHandler (matches WATER) instead of slog.Default(), so levels/routing
  stay consistent. (Copilot)
- acceptSocks: only return on net.ErrClosed; log + continue on transient Accept
  errors so a temporary failure can't permanently kill routing while the HTTP
  endpoint keeps serving. (Copilot + CodeRabbit)
- Require auth_token by default — a meek inbound is a public/fronted relay into
  sing-box, so an empty token is an open relay. New allow_unauthenticated opt-in
  for deliberate no-auth (test/private). Added a test. (CodeRabbit, security)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9beSsYGzUaBhRK5ULmtGr
@myleshorton myleshorton requested a review from Copilot June 26, 2026 23:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@protocol/meek/inbound_test.go`:
- Around line 30-35: The opt-in test path leaks live sockets because NewInbound
returns an open inbound after creating the loopback SOCKS listener and TCP
listener. Capture the returned inbound from NewInbound in inbound_test.go and
register cleanup to close it when AllowUnauthenticated is true, using the
NewInbound result instead of discarding it so the test does not leave listeners
open.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b41db719-5c35-43ba-b4b5-f27a47878e1f

📥 Commits

Reviewing files that changed from the base of the PR and between 4464158 and 441ee67.

📒 Files selected for processing (3)
  • option/meek.go
  • protocol/meek/inbound.go
  • protocol/meek/inbound_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • protocol/meek/inbound.go
  • option/meek.go

Comment thread protocol/meek/inbound_test.go Outdated

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread protocol/meek/inbound_test.go Outdated
Comment thread protocol/meek/inbound_test.go Outdated
Comment thread protocol/meek/inbound_test.go
- TestNewInbound_RequiresAuthToken: capture + t.Cleanup the inbound on the
  allow_unauthenticated path so it doesn't leak the listeners it opens.
- TestInbound_HandleSocks_RoutesConnect: bounded select on the router channel so
  a routing failure fails fast instead of hanging to the test timeout.
- TestInbound_HandleSocks_RejectsNonConnect: make `routed` an atomic.Bool —
  it's written by the handler goroutine and read by the test (data race under
  -race). Verified with `go test -race`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9beSsYGzUaBhRK5ULmtGr

Copilot AI 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.

Pull request overview

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

Comment thread protocol/meek/inbound_test.go
…ound 3)

Add a default case so an unexpected address type in the CONNECT reply fails the
test instead of silently reading 0 bytes and passing on a malformed reply.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9beSsYGzUaBhRK5ULmtGr

Copilot AI 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.

Pull request overview

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

Comment thread protocol/meek/inbound.go
…d 4)

Add a capped backoff (failures*10ms, max 1s, reset on success) so a persistent
non-close Accept error (e.g. FD exhaustion) can't spin the loop hot and flood
the log, while transient errors still recover.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9beSsYGzUaBhRK5ULmtGr

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@myleshorton myleshorton merged commit 203e7ff into main Jun 27, 2026
5 checks passed
@myleshorton myleshorton deleted the fisk/meek-inbound branch June 27, 2026 14: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.

2 participants