Skip to content

feat(reverse): reverse proxy — bridge + portal (Mux.cool over VLESS-Rvs) - #3095

Draft
LOVECHEN wants to merge 3 commits into
MetaCubeX:Alphafrom
LOVECHEN:reverse-proxy
Draft

feat(reverse): reverse proxy — bridge + portal (Mux.cool over VLESS-Rvs)#3095
LOVECHEN wants to merge 3 commits into
MetaCubeX:Alphafrom
LOVECHEN:reverse-proxy

Conversation

@LOVECHEN

@LOVECHEN LOVECHEN commented Aug 8, 2026

Copy link
Copy Markdown

Summary

Adds an Xray-compatible reverse proxy to mihomo — both roles: bridge (NAT side; dials out and lands traffic to its local network) and portal (public side; accepts reverse connections and serves user traffic), without an external tunnel. New listener types reverse-bridge / reverse-portal and a reverse-portal outbound. Supports TCP / REALITY / Vision / UDP (full-cone).

Addresses #190 (currently not_planned) — opening for discussion.

Why

mihomo can currently only be a client to Xray's reverse proxy via an external tunnel. This lets mihomo interoperate with Xray's reverse proxy in both roles, and run a pure mihomo↔mihomo reverse stack. Verified against Xray-core 26.7.28 in every role-swap combination (bridge ∈ {mihomo, Xray} × portal ∈ {mihomo, Xray}).

Suggested commit structure

  1. transport/muxcool — Xray Mux.cool wire protocol (frame codec + server/client workers + UDP full-cone + buffered pipe), stdlib-only, with unit tests. mihomo's sing-mux is a different, wire-incompatible protocol, so a real Mux.cool impl is required. Standalone package, no wiring.
  2. VLESS Rvs command + DialReverseCommandRvs (0x04) (address-less, like Mux) in the client encoder + DstAddr.Rvs; DialReverse outbound method that opens a VLESS-Rvs stream reusing the full transport chain (REALITY/TLS/ws/grpc/Vision). This is the capability a bridge uses to dial a portal.
  3. reverse bridge + portal + wiring
    • listener/inbound/reverse_bridge.go: reverse-bridge listener (no port); resolves a referenced VLESS outbound, DialReverse to the portal, runs a Mux.cool ServerWorker, feeds each substream into tunnel.HandleTCPConn (UDP lands via dialer.DialContext); reconnects on drop.
    • listener/inbound/reverse_portal.go (+ reverse_portal_tls.go): reverse-portal listener; accepts VLESS-Rvs (optional REALITY / plain TLS 1.3 / Vision auto-detected from client flow); runs a Mux.cool ClientWorker + heartbeat.
    • listener/reverse/portal.go: runtime registry (tag → active tunnels) with a round-robin, IsActive-filtered picker.
    • adapter/outbound/reverse_portal.go: reverse-portal outbound; routes user traffic to an active tunnel by tag (TCP DialContext + UDP ListenPacketContext).
    • adapter/outbound/base.go: expose the auto-close wrapper's inner adapter (so the bridge can get the concrete *Vless).
    • listener/parse.go, adapter/parser.go: register the two listener types + the outbound.

Configuration reference

reverse-bridge listener (no port; dials out): name, type: reverse-bridge, portal (a proxies: VLESS outbound name used to dial the portal).

reverse-portal listener (accepts reverse connections): name, type: reverse-portal, listen/port, uuid, tag (links to the outbound), plus one security layer — reality-config: {dest, private-key, short-id: [], server-names: []} or tls: true (+ certificate/private-key, PEM path or inline; empty → self-signed) or none. Precedence: reality > tls > none. flow is auto-detected (empty or xtls-rprx-vision; Vision needs TLS/REALITY underneath).

reverse-portal outbound: name, type: reverse-portal, tag (matches the listener's tag).

# ---- Portal (public) ----
mixed-port: 10800
allow-lan: true
proxies:
  - { name: rvout, type: reverse-portal, tag: rvpool }
listeners:
  - { name: rp, type: reverse-portal, listen: 0.0.0.0, port: 10000, uuid: <uuid>, tag: rvpool,
      reality-config: { dest: www.cloudflare.com:443, private-key: <priv>, short-id: ["01ab"], server-names: [www.cloudflare.com] } }
rules: [ MATCH, rvout ]

# ---- Bridge (intranet) ----
proxies:
  - { name: toportal, type: vless, server: <portal>, port: 10000, uuid: <uuid>, network: tcp,
      tls: true, servername: www.cloudflare.com, client-fingerprint: chrome,
      reality-opts: { public-key: <pub>, short-id: "01ab" } }
listeners: [ { name: rb, type: reverse-bridge, portal: toportal } ]
rules: [ MATCH, DIRECT ]

Testing

End-to-end against real Xray-core 26.7.28 — all four quadrants for TCP and REALITY; UDP full-cone (single/multi-target) both directions; Vision (xtls-rprx-vision); plus go test -race for transport/muxcool.

Notes for maintainers

Sizeable feature for a not_planned issue — happy to split (bridge-only first, or gate behind a build tag) or adjust the config surface. UDP lands via direct dial (no XUDP GlobalID; Xray's reverse path doesn't use it either). Reverse accounts require empty or Vision flow.


Self-contained transport/muxcool package implementing Xray's Mux.cool multiplexing
wire protocol: frame codec, server/client workers, UDP (full-cone) and a buffered pipe,
with unit tests. mihomo's sing-mux is a different, wire-incompatible protocol; a real
Mux.cool implementation is required for interop with Xray mux-based features (reverse
proxy in the following commits). Standalone package, no wiring.
Add VLESS Rvs command (0x04, address-less like Mux) to the client encoder and DstAddr.Rvs;
add a DialReverse outbound method that opens a VLESS-Rvs stream reusing the full transport
chain (REALITY/TLS/ws/grpc/Vision). This is the capability a reverse-proxy bridge uses to
dial a portal. No user-facing config on its own.
Xray-compatible reverse proxy, both roles, over Mux.cool + VLESS-Rvs:
- reverse-bridge listener: dials a referenced VLESS outbound to the portal, runs a
  Mux.cool ServerWorker, feeds substreams into tunnel (UDP via dialer.DialContext).
- reverse-portal listener (+tls): accepts VLESS-Rvs (REALITY / plain TLS / Vision
  auto-detected), runs a Mux.cool ClientWorker + heartbeat.
- reverse portal runtime registry + round-robin picker; reverse-portal outbound.
- register the two listener types and the outbound.
Supports TCP/REALITY/Vision/UDP(full-cone). Addresses MetaCubeX#190.
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.

1 participant