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-utilhelper 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:
HTTPUpgrade¶
WebSocket handshake without WebSocket framing — once upgraded the stream is raw bytes. Cheaper than ws at scale.
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 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:
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:
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-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:
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
h2orxhttp.
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 link aggregation¶
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 from198.18.0.0/16to prevent leaks; only the domain name travels through the VPN. redir-host-addresstransparently 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_INTERVALseconds, - 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:
- 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. - Pins
OUTBOUND_INTERFACE— exportsOUTBOUND_INTERFACE=<ifname>,<ip-list>so every Leaf outbound binds new sockets to that physical interface. The string is consumed by the core on eachconnect(). - Rewrites DNS — swaps the active interface's DNS servers to
1.1.1.1+8.8.8.8(vianetsh/scutil/resolvconf) so lookups outside the VPN keep working, and caches the originals so they can be restored on shutdown. - Adds TUN routes — based on
TUN_BYPASS_LAN: - On — installs
PUBLIC_IPV4_RANGES/PUBLIC_IPV6_RANGES(all public-addressable space, split around RFC 1918 / ULA to skip LAN). - Off — installs two 0.0.0.0/1 + 128.0.0.0/1 catch-all halves so RFC1918 also gets forced through the tunnel.
- Always adds the fake-IP route —
198.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-routeevent stream — listens forRouteChange::Add/Delete/Changeon the system table. If the system's default route moves (new gateway, new interface), the manager re-runs steps 1-3 and callsRuntimeManager::force_health_check_all_failovers()so everyfailovergroup re-evaluates.- Polling task (
ROUTE_POLL_INTERVALseconds, default 3) — cross-checks the kernel's view of valid interfaces against the manager's cached default. Covers kernel edge cases where thenet-routechannel 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_INTERFACEback to0.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 bysetUnderlyingNetworks(). See Android SDK — Network-switch auto-reconnect for the Android equivalent.
10. Notable features beyond the usual¶
- TCP log shipping —
logoutput = tcp://log-host:9999streams structured logs straight into your aggregator. - HC (Health-Check) listener — a tiny HTTP liveness endpoint with
hc-path,hc-request,hc-responsefor 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 handling —
TRUSTED_PROXIES,REAL_IP_HEADER,REAL_IP_RECURSIVE— identical semantics to Nginx. - Hot reload —
POST /api/v1/runtime/reloadre-parses the config without dropping the TUN device. - Config testing —
leaf-ipcexposestest_configso SDKs can validate a generated.confbefore 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¶
- Architecture — how SDK + IPC + core fit together.
- Proxy Groups & Naming — conventions for select / failover / country subgroups.
- Runtime HTTP API — live control of everything on this page.
- Questions? [email protected].