Skip to content

Leaf Core & Protocols

The Leaf core is the closed-source Rust proxy engine that powers every Surf Shield SDK and node. This page documents the proxies, transports, multiplexing layers, and DPI-bypass techniques it exposes so you can make informed decisions when designing your client and your backend node configuration.

The Leaf core itself is closed source. The SDKs, sample clients, and leaf-util helper library are open source under Apache 2.0. See Distribution & Registries for where binaries, JARs, and crates are published.


1. Why a custom proxy core?

Commercial VPN stacks (OpenVPN, WireGuard) are trivially fingerprinted and blocked by modern DPI systems. Leaf combines:

  • Multiple proxy protocols (Trojan, VMess, VLESS, Shadowsocks, Stealth, HTTP, SOCKS) over tunable transports (raw TCP/UDP, TLS, REALITY, WebSocket, HTTPUpgrade, HTTP/2, gRPC, QUIC, xHTTP).
  • Active obfuscation layers (TLS Client-Hello fragmentation, fake-host, XTLS Vision, REALITY).
  • Two multiplexing dialects (AMux for TCP-based, QUIC for UDP-based) and full H2Mux over HTTP/2.
  • Dynamic outbound groups (select, failover, url-test, speedtest, tryall, random, chain, retry, mptp) to keep connectivity in hostile networks.
  • Protocol-level sniffing and rule-based routing (GEOIP, Geosite, PROTOCOL, DOMAIN, DOMAIN-SUFFIX, IP-CIDR, REMOTE-DNS).
  • First-class support for TUN inbound (L3 VPN) on Android, Windows (Wintun), macOS, Linux.

2. Proxy protocols

Protocol Transport Notes
Trojan TLS-only Looks like plain HTTPS — default choice behind CDNs.
VMess TLS / plain Supports the new VMess packet format used by modern xray/v2ray.
VLESS REALITY / TLS / xHTTP Stateless, lower overhead than VMess, uses UUID auth.
Shadowsocks raw / obfs / plugin AES / ChaCha20-IETF-Poly1305 ciphers (AWS-LC accelerated).
Stealth bespoke TLS mimicry Home-grown protocol: performs a real TLS handshake with Google / Cloudflare, then switches to the tunnel. Indistinguishable from a browser session on the wire.
HTTP / HTTPS tunnel TLS / plain Supports CONNECT and basic auth; useful as a fallback on restricted networks.
SOCKS raw Inbound for user apps; rarely used outbound.
Direct / Drop / Redirect Routing outcomes for rules.

Every protocol can be combined with every transport below.


3. Transports

TLS

All TLS stacks accept:

  • sni, alpn, insecure (skip cert verification when pinning downstream),
  • Inline / file-based certificates (Rustls or OpenSSL depending on build feature),
  • TLS fragmentation (fragment=true) — see DPI bypass layer.

REALITY

A Trojan / VMess / VLESS wrapper that replaces the standard TLS Client Hello with one that is byte-identical to a target site (e.g. www.google.com). The remote peer replays the handshake to the real site on mismatches, so passive scanners see only genuine traffic. Options:

Trojan_Reality = trojan, 1.2.3.4, 443, password=..., \
                 reality=true, reality-public-key=<X25519 base64>, \
                 reality-short-id=<hex>, reality-sni=www.google.com

WebSocket (ws) and ws-ed

Standard RFC 6455. ws-ed (early-data) front-loads the first 56–2560 bytes into the WebSocket handshake request, saving one RTT:

ws-ed=true, ws-ed-host=..., ws-ed-path=/chat, ws-ed-ed-range=56-2560

HTTPUpgrade

WebSocket handshake without WebSocket framing — once upgraded the stream is raw bytes. Cheaper than ws at scale.

httpupgrade=true, httpupgrade-host=..., httpupgrade-path=/chat

HTTP/2 (h2)

Full HTTP/2 tunnel, including push and multiplexed streams. Combined with mux-* options (see Multiplexing) it yields high throughput over a single TLS session. Useful behind CDNs that prefer HTTP/2.

h2=true, h2-host=..., h2-path=/chat, h2-method=PUT

H2 pure (no CDN) works only when the Leaf node terminates TLS itself.

gRPC

Packs data into gRPC message frames. Great for egress that must traverse a CDN that inspects content types:

grpc=true, grpc-service-name=<name>

xHTTP

Chunked HTTP/1.1 upload/download stream with configurable padding and gRPC-style headers. Balanced between gRPC (compatibility) and HTTPUpgrade (cost):

xhttp=true, xhttp-mode=stream-one, xhttp-padding-bytes=100-500, \
xhttp-no-grpc-header=false, xhttp-path=/chat

QUIC

Native multiplexed UDP transport. When paired with Trojan / VMess / VLESS it eliminates head-of-line blocking and reduces handshake overhead:

quic=true, quic-max-streams=32, quic-concurrency=8, \
quic-max-recv-bytes=0, quic-max-lifetime=7200

FakeTCP (faketcp)

Sends TCP segments with spoofed flags so the connection looks like a long-lived TCP flow to stateful firewalls but is actually carried over UDP. Extremely effective in markets that throttle TCP-over-TLS.

MPTP (Multi-Path Transport Protocol)

See Outbound groups — an outbound group type.


4. DPI bypass layer

TLS Client Hello fragmentation

The most effective anti-SNI filtering trick in high-censorship networks:

fragment=true, fragment-packets=0-1, fragment-length=27-87, fragment-interval=1-5
  • fragment-packets — which TCP packets to split (0-1 = first handshake packet).
  • fragment-length — random byte range per fragment.
  • fragment-interval — random delay (ms) between fragments.

Bypasses classifiers that reassemble the first ≤1500 bytes looking for SNI matches.

Fake host fragmentation

Combined with httpupgrade / xhttp / h2: the outgoing Host: header is replaced by a benign domain at TCP-segment boundaries. Requires fragment-fakehost=true, optional fragment-host1-domain=..., fragment-host2-domain=....

XTLS Vision (Stealth protocol)

The Stealth protocol wraps your payload in a real TLS handshake whose SNI points at www.google.com (or similar). After the handshake it swaps into raw tunnel mode while still emitting periodic genuine-looking TLS records. Unlike REALITY it does not need a live target server to replay handshakes.

Protocol sniffing + PROTOCOL rules

PROTOCOL_SNIFFING=true lets the sniffer classify plaintext flows as tls, http, bittorrent, utp. You can then route on it:

PROTOCOL, bittorrent, DIRECT
PROTOCOL, http, Proxy

Zero-overhead; runs on a small prefix of the stream.

Randomised SNI casing

sni values are re-cased per-connection (WwW.GoOgLe.CoM) to defeat exact-match filters — done transparently inside the panel's subscription generator.

Inline SNI Proxy

The sniproxy inbound peeks at TLS SNI / HTTP Host without consuming bytes, then routes the unmodified stream to the correct backend inbound (tls, trojan, stealth). Gives you a single public port (443) with zero-overhead multi-protocol multiplexing.


5. Multiplexing

Two styles, chosen per-outbound:

  • AMux — reuses a pool of TCP sub-streams to amortise handshake cost. Focused on lowering latency for bursty HTTP / WebSocket workloads.
  • QUIC — UDP-native, stream-multiplexed, zero head-of-line blocking. Best for throughput.
  • H2Mux — pipelines HTTP/2 streams over a single TLS connection. Best when the transport is h2 or xhttp.

Tunables (see the Panel's default generator settings):

; AMux
amux=true, amux-max=16, amux-con=4, amux-max-recv=0, amux-max-lifetime=7200

; H2Mux
mux-max-streams=64, mux-concurrency=8, mux-max-recv-bytes=0, mux-max-lifetime=7200

; QUIC
quic-max-streams=32, quic-concurrency=8, quic-max-recv-bytes=0, quic-max-lifetime=7200

6. Outbound groups (runtime selectable)

Type Behaviour
select Manual pick from a list; change live via the Runtime API.
failover Active/standby list ordered by rolling RTT + EWMA smoothing, bias preference, hysteresis.
url-test Periodically probes generate_204 and elects the fastest. No automatic failover during the window.
speedtest Runs real bandwidth tests at check-interval and elects the fastest.
tryall Races every candidate in parallel; first successful TCP handshake wins.
random Uniform random pick per connection (round-robin variant coming).
retry Tries the same outbound N times before giving up.
chain Pipes traffic through multiple outbounds in sequence (e.g. Trojan → SOCKS egress).
mptp Multi-Path Transport Protocol — aggregates multiple actor sub-links into one reliable transport with per-link RTT, retransmission, and flow control, powered by the aggligator crate. Lets a single user saturate a throttled network by summing multiple small pipes.

Every group exposes management endpoints under /api/v1/app/outbound/... (see Runtime HTTP API).

Failover tunables (panel defaults)

Generated by the Panel into each group:

fail-timeout=4, health-check=true, check-interval=300, failover=true, \
health-check-timeout=10, health-check-delay=500, \
health-check-active=600, health-check-attempts=1, \
health-check-success-percentage=100, \
health-check-prefer-bias-ms=150, health-check-demote-fail-streak=2, \
health-check-promote-margin-ms=150, health-check-promote-consecutive=2, \
health-check-ewma-alpha=0.5, \
fallback-cache=true, cache-size=256, cache-timeout=60, \
health-check-tcp-url=https://www.gstatic.com/generate_204, \
health-check-udp-dns=8.8.8.8, \
last-resort=<tag>
mptp-io-write-size=4096, \
mptp-send-buffer=4194304, mptp-recv-buffer=4194304, \
mptp-send-queue=256, mptp-recv-queue=256, \
mptp-no-link-timeout=60, mptp-link-ping-timeout=30, \
mptp-link-unacked-limit=8388608, mptp-link-max-ping=10000

Under-the-hood MPTP runs on top of the aggligator crate: you get automatic link monitoring, retransmission, and dynamic failover across TCP and UDP links.


7. Routing

Rules

[Rule]
REMOTE-DNS, 1.1.1.1:53, DIRECT         ; route by DNS resolver used
PROTOCOL, bittorrent, DIRECT           ; by sniffed protocol
GEOIP, cn, DIRECT                      ; by resolved country
GEOSITE, category-ads, DROP            ; by domain category
DOMAIN-SUFFIX, example.com, Proxy
IP-CIDR, 10.0.0.0/8, DIRECT
FINAL, Proxy

When routing-domain-resolve = true, domains are resolved before rule evaluation so GEOIP can match IP-based rules even on CNAMEs. DNS_HIJACK_IPS lets you skip poisoned answers.

DNS layer

  • Local DNS server (dns-interface, dns-port) accepts UDP+TCP.
  • Remote servers are configured per [[Dns]] section and can be DoH, DoT, DoQ, TCP, UDP.
  • Fake-IP mode (always-fake-ip = *) answers every query from 198.18.0.0/16 to prevent leaks; only the domain name travels through the VPN.
  • redir-host-address transparently redirects all LAN DNS traffic (port 53) to your chosen server while rewriting source IPs so clients are none the wiser.

8. Node-side health & telemetry

Each node runs a Leaf instance with the api-server feature on 127.0.0.1:9090. In production it also talks to the panel through the Web API Client (WEB_API_URL, WEB_API_NODE_ID, WEB_API_KEY) to:

  • fetch users and their quotas every WEB_API_UPDATE_INTERVAL seconds,
  • upload per-user byte counts and online IPs,
  • push CPU / RAM / uptime / bandwidth metrics.

The panel uses this stream to drive its live dashboards and trigger billing.


9. Auto-Routing (desktop TUN management)

On Windows / macOS / Linux the Leaf core includes an AutoRouting module that owns the system routing table while the VPN is up. Its job is to make sure that the packets sent by applications land in the TUN device, that traffic Leaf re-originates goes out the real network interface, and that everything survives live Wi-Fi ↔ Ethernet ↔ cellular switches. SDK integrators get all of this transparently — you do not call anything from this module directly.

What it installs on start

When run_leaf() is called with a TUN inbound, the manager:

  1. Picks the current default route — gathers all routes with a non-TUN gateway, sorts by the platform-appropriate metric (Linux → metric, Windows → ifindex, macOS → any), and keeps the first match.
  2. Pins OUTBOUND_INTERFACE — exports OUTBOUND_INTERFACE=<ifname>,<ip-list> so every Leaf outbound binds new sockets to that physical interface. The string is consumed by the core on each connect().
  3. Rewrites DNS — swaps the active interface's DNS servers to 1.1.1.1 + 8.8.8.8 (via netsh/scutil/resolvconf) so lookups outside the VPN keep working, and caches the originals so they can be restored on shutdown.
  4. Adds TUN routes — based on TUN_BYPASS_LAN:
  5. On — installs PUBLIC_IPV4_RANGES / PUBLIC_IPV6_RANGES (all public-addressable space, split around RFC 1918 / ULA to skip LAN).
  6. Off — installs two 0.0.0.0/1 + 128.0.0.0/1 catch-all halves so RFC1918 also gets forced through the tunnel.
  7. Always adds the fake-IP route198.18.0.0/16 → TUN gateway, so that DNS answers from the core's fake-IP pool flow into the tunnel.

What it does while running

Two parallel watchers stay alive:

  • net-route event stream — listens for RouteChange::Add/Delete/Change on the system table. If the system's default route moves (new gateway, new interface), the manager re-runs steps 1-3 and calls RuntimeManager::force_health_check_all_failovers() so every failover group re-evaluates.
  • Polling task (ROUTE_POLL_INTERVAL seconds, default 3) — cross-checks the kernel's view of valid interfaces against the manager's cached default. Covers kernel edge cases where the net-route channel misses an event (notably on Windows + some Linux carriers).

Interface validation

is_valid_interface() filters out anything that is not a genuine physical uplink:

  • Must be is_up() && is_running() && is_physical().
  • Must not be a TUN device or named starting with TUN_DEVICE_NAME.
  • Must have an IPv4 address (or IPv6 when ENABLE_IPV6=true).
  • Must be is_oper_up() or (on Linux) operstate=unknown + carrier=up (handles VMs and certain virtio interfaces).

What it tears down on stop

revert_routing_changes() is called during stop_leaf:

  • Deletes every TUN route the manager added (tracked by added_routes).
  • Clears OUTBOUND_INTERFACE back to 0.0.0.0,::.
  • Restores the original DNS servers on Linux/macOS, or clears them on Windows.

Integrator-visible knobs

All of these are already exposed through LeafPreferences:

Preference Effect
bypass_lan Drives TUN_BYPASS_LAN; flips the route set between public-only and catch-all.
bypass_lan_in_core Moves the LAN split from the route table to the router inside Leaf (useful when you cannot delete system routes, e.g. on heavily managed Windows fleets).
enable_ipv6 Gates the IPv6 route installation.
prefer_ipv6 Pushes IPv6 to the front of the default-route picker.

Environment-only tunables (for advanced integrations — change before starting the core):

Variable Default Purpose
TUN_DEVICE_NAME utun225 / tun0 / Leaf Tun Device name the manager looks for.
TUN_IPV4_GATEWAY / TUN_IPV6_GATEWAY 172.31.0.1 / fd00:1234:5678::1 Must match the addresses Leaf configures inside the TUN.
ROUTE_POLL_INTERVAL 3 Poller cadence in seconds.
OUTBOUND_INTERFACE managed Set automatically; overrideable only if you disable AutoRouting entirely.

On Android, auto-routing is not used — route installation is done by VpnService.Builder.addRoute() and underlying-network tracking by setUnderlyingNetworks(). See Android SDK — Network-switch auto-reconnect for the Android equivalent.


10. Notable features beyond the usual

  • TCP log shippinglogoutput = tcp://log-host:9999 streams structured logs straight into your aggregator.
  • HC (Health-Check) listener — a tiny HTTP liveness endpoint with hc-path, hc-request, hc-response for load balancers.
  • Webserver inbound — Pingora-based HTTP reverse proxy with routing by host/path/method/protocol, TLS termination, dynamic backend header injection (${REAL_IP}, ${SOURCE_IP}).
  • Real-IP handlingTRUSTED_PROXIES, REAL_IP_HEADER, REAL_IP_RECURSIVE — identical semantics to Nginx.
  • Hot reloadPOST /api/v1/runtime/reload re-parses the config without dropping the TUN device.
  • Config testingleaf-ipc exposes test_config so SDKs can validate a generated .conf before starting the core.

11. Where to configure what

Lever Who sets it How
Which protocols & ports exist per node Operator (panel admin) nodes_settings → inbound types enabled per node
Which proxies each client sees Panel (generates the .conf per client) Panel Configuration Generator
Default SDK behaviour (speedtest / try-all) App developer updateSubscription(tls, fragment, clientId, enable_speedtest, enable_try_all, cb)
Live outbound pick & DNS mode End user / SDK Runtime HTTP API + LeafPreferences

12. Further reading