nativeToken auth, TCP/UDP tunnel semantics, reverse publish, and resumable logical streams.
Configuration
Unknown fields fail closed. Native carriers, mux, resume, reverse publish, and routing live in the same FileConfig.
Configuration
Describe inbounds, outbounds, and security in one config. Start with native, then REALITY and protocol choice.
v0.5.0 · native + raw + reality
native + raw + reality + carrier.mode=autoThis is the recommended automatic stack in v0.5.0: one listen address, dual Reality carriers, QUIC preferred, TCP fallback, and optional resumable TCP streams. carrier.mode selects auto/tcp/quic while security.type stays reality. Without mux, Reality remains TCP-only.
Force TCP with carrier.mode=tcp, or force QUIC with carrier.mode=quic.
nativeToken auth, TCP/UDP tunnel semantics, reverse publish, and resumable logical streams.
rawRequired base transport for automatic dual carriers. Do not stack ws / h2 / h3 in this mode.
realityCamouflage keys/SNI/dest. Pair with carrier.mode for auto, TCP-only, or QUIC-only selection.
mode=autov0.5.0 generator path: Reality or TLS with carrier.mode=auto on one address; outbound carrier.prefer is adaptive by default.
mux.enabledMux enables dual-carrier and pooling. Optional mux.resume preserves eligible TCP flows across carrier replacement.
security.reality + carrier.mode="auto" + mux.enabled
Binds TCP and UDP on one address, prefers QUIC, falls back to Reality TCP with backoff, and probes to restore QUIC preference.
carrier.mode="tcp"
Uses only the Reality TCP carrier (security.type stays reality). Choose this when UDP is intentionally unavailable.
carrier.mode="quic" + mux.enabled
Uses the dedicated QUIC pool without TCP fallback. The server listen port must be reachable over UDP.
Automatic dual carriers need type=native, transport.type=raw, mux enabled, security.type=reality, and carrier.mode=auto (default for generators). Missing mux keeps ordinary TCP-only Reality.
Set carrier.mode to auto (QUIC-first with TCP fallback), tcp, or quic. security.type remains reality in all three cases. quic and auto require mux.enabled.
The inbound binds TCP and UDP to the same host:port. Clients race the same address over both carriers without a second listen entry.
Outbounds prefer the Reality QUIC carrier. When UDP is blocked or unhealthy, they fall back to Reality TCP with jittered exponential backoff, then probe to restore QUIC preference.
Both carriers share the same REALITY keys, short IDs, SNI, and dest. The camouflage destination should support HTTPS over TCP and HTTP/3 over UDP.
TCP streams, UDP relay, and reverse carriers follow the same automatic carrier policy when carrier.mode=auto.
carrier.mode=tcp forces Reality TCP only. carrier.mode=quic forces the dedicated QUIC pool without TCP fallback. security.type remains reality.
Top-level fields include log, resources, inbounds, outbounds, route, and dns. Unknown fields are rejected; resources.resumable_buffer_budget controls the shared replay-buffer budget.
inbound.address and outbound.address are both host:port arrays. Multiple addresses race as candidate entry points; use balance for independent nodes.
Components link through tags; via chains and balance members are checked for missing refs and cycles.
Load → Validate → Compile → Start. Listening begins only after validation succeeds.
FileConfig route
route.default_outbound is required. rules run in declaration order and the first match selects its outbound. mixed, socks5, and platform TUN use this router for every flow. A native inbound sends accepted flows to the default outbound until route_mode is rules.
Rules keep file order. A match stops the search. A rule whose only field is outbound matches every remaining flow and hides later rules.
inbound, network, principals, app, and the destination group combine with AND. Values inside one array are OR. domains, domain_regexes, domain_suffixes, ips, ip_cidrs, and ip_ranges are one destination group: any hit matches. An empty group adds no constraint.
mixed, socks5, and platform TUN always use the compiled router. On a native inbound, omit route_mode or set default to keep default_outbound for every accepted flow. route_mode rules selects per TCP flow and per fixed-destination UDP association. The embedder supplies application identity; it is not added to native, mux, or UDP frames. A rule with app does not match when that identity is missing.
An outbound with type blackhole rejects TCP and discards UDP. The flow does not continue to direct or another outbound.
dns.outbound pins intercepted DNS to one outbound that supports TCP and UDP, bypassing route rules. If that outbound is unavailable, DNS fails closed. With dns.fake_ip.enabled, A and AAAA answers use the configured fake-IP ranges (198.18.0.0/15 and fc00::/18 when omitted) and later flows to those addresses are restored to the domain before route selection. Other DNS messages follow the configured route. dns.strategy is prefer_ipv4, prefer_ipv6, ipv4_only, or ipv6_only. TUN DNS on TCP or UDP port 53 enters this same router unless it is pinned.
Unknown fields are rejected. Matchers are the lists on this page. geosite: and geoip: prefixes are rejected. A URI carries one tunnel endpoint; route rules and via chains stay in JSON. via chains the dial of that outbound and is cycle-checked. The Android app uses Full Tunnel and does not edit these rules.
default_outboundrulesrules[].outboundinboundnetworkdomainsdomain_suffixesdomain_regexesipsip_cidrsip_rangesappapp.idsapp.id_prefixesapp.platformsapp.attributesusers[].principalroute_modeprincipalsThese objects pass tcptun config check on v0.5.0. The split example still needs a real REALITY public key; the examples catalog fills one in the browser.
client-route-split.jsonads.example is blackholed, RFC1918 prefixes and internal.example go direct, and every other flow uses the native outbound. Replace the REALITY public key before config check.
{
"log": { "level": "info" },
"inbounds": [
{
"tag": "local",
"type": "mixed",
"address": ["127.0.0.1:1080"],
"network": ["tcp", "udp"]
}
],
"outbounds": [
{
"tag": "proxy",
"type": "native",
"address": ["proxy.example.com:9443"],
"token": "change-me",
"transport": { "type": "raw" },
"carrier": { "mode": "auto", "prefer": "quic", "udp_mode": "auto" },
"mux": { "enabled": true },
"security": {
"type": "reality",
"server_name": "example.com",
"public_key": "REPLACE_WITH_SERVER_PUBLIC_KEY",
"short_id": "abcd1234",
"spider_x": "/"
}
},
{ "tag": "direct", "type": "direct" },
{ "tag": "block", "type": "blackhole" }
],
"route": {
"default_outbound": "proxy",
"rules": [
{ "domain_suffixes": ["ads.example"], "outbound": "block" },
{ "ip_cidrs": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"], "outbound": "direct" },
{ "domain_suffixes": ["internal.example"], "outbound": "direct" }
]
},
"dns": {}
}client-route-app.jsonOrdinary CLI flows have no application identity, so both rules miss and traffic uses direct. An embedder that returns com.example.reader on Android with attribute profile=work selects direct. IDs that start with com.example.game. select block.
{
"log": { "level": "info" },
"inbounds": [
{
"tag": "local",
"type": "mixed",
"address": ["127.0.0.1:1080"],
"network": ["tcp", "udp"]
}
],
"outbounds": [
{ "tag": "direct", "type": "direct" },
{ "tag": "block", "type": "blackhole" }
],
"route": {
"default_outbound": "direct",
"rules": [
{
"app": {
"ids": ["com.example.reader"],
"platforms": ["android"],
"attributes": { "profile": ["work"] }
},
"outbound": "direct"
},
{
"app": { "id_prefixes": ["com.example.game."] },
"outbound": "block"
}
]
},
"dns": {}
}server-route-principal.jsonremote-access uses route_mode rules. Authenticated user alice may open TCP/UDP to the two prefixes through direct. Every other accepted flow uses deny. This shows the route fields; it does not export a home LAN.
{
"log": { "level": "info" },
"inbounds": [
{
"tag": "remote-access",
"type": "native",
"address": ["0.0.0.0:9443"],
"network": ["tcp", "udp"],
"users": [{ "principal": "alice", "id": "change-me" }],
"transport": { "type": "raw" },
"mux": { "enabled": true },
"route_mode": "rules"
}
],
"outbounds": [
{ "tag": "direct", "type": "direct", "network": ["tcp", "udp"] },
{ "tag": "deny", "type": "blackhole" }
],
"route": {
"default_outbound": "deny",
"rules": [
{
"inbound": ["remote-access"],
"principals": ["alice"],
"network": ["tcp", "udp"],
"ip_cidrs": ["192.168.50.0/24", "fd12:3456:789a::/64"],
"outbound": "direct"
}
]
},
"dns": {}
}Native
Supports TCP / UDP and mux, with transport options raw / ws / h2 / h3. Server users[].id and client token must match.
users[].idtokenServer users[].id and client token must match.
address is a host:port array. Multiple addresses race as candidate entry points for the same service; they are not balance.
native + raw + mux + security.type=reality + carrier.mode=auto prefers QUIC, falls back to TCP, and shares one camouflage identity on both carriers.
mux.resume=true on both carrier.mode=auto peers can preserve an eligible TCP logical stream across carrier replacement (v0.3.0+).
Prefer native + raw + mux. Forced TLS / REALITY / ws / h2 / h3 add flexibility but cost more.
Server publish + client expose can hang NAT-side TCP/UDP services on edge listeners (requires mux).
Native QUIC
native + raw + reality + carrier.mode=quicForced QUIC is still the same layers: native is the tunnel protocol, raw is the transport, and security.type=reality stays REALITY. Select the dedicated QUIC pool with carrier.mode=quic and mux.enabled. There is no TCP fallback.
Carry TCP streams and UDP DATAGRAMs together without managing TLS certificates.
nativeHandles token auth, TCP / UDP tunnel semantics, and reverse publish.
rawRequired base transport for QUIC mode; do not stack ws / h2 / h3.
realityProtects the handshake with REALITY keys and site parameters; no cert deploy needed.
mode=quicUses a dedicated QUIC pool for streams and UDP DATAGRAMs. Requires mux.enabled.
{
"log": { "level": "info" },
"inbounds": [
{
"tag": "server",
"type": "native",
"address": ["0.0.0.0:9443"],
"network": ["tcp", "udp"],
"users": [{ "id": "change-me" }],
"transport": { "type": "raw" },
"mux": { "enabled": true }
}
],
"outbounds": [
{ "tag": "direct", "type": "direct" }
],
"route": { "default_outbound": "direct", "rules": [] },
"dns": {}
}The server exposes a native inbound; the client forwards a local mixed inbound to a native outbound. Replace the sample credentials and addresses to run.
Common field overview.
tagbothtypebothaddressbothnetworkbothtransportbothsecuritybothmuxbothaddressserverusers[].idserverpublishserversecurity.cert/keyserveraddressclienttokenclientsecurity.server_nameclientexposeclientcarrier.modebothcarrier.preferclientcarrier.udp_modeclientcarrier.*_receive_windowbothmux.max_sessionsclientmux.max_streams_per_sessionclientmux.warm_sparesclientmux.resumebothmux.resume_timeoutbothmux.resume_buffer_sizebothEnable mux for short-connection workloads. Prefer native + raw + mux for peak throughput.
Set mux.enabled=true. enabled is required and must be true; omit the mux field to disable it.
Reuses physical connections. Unreachable targets are not reported as success to the local proxy early.
native + raw + mux + security.type=reality + carrier.mode=auto prefers QUIC, falls back to Reality TCP with bounded backoff, then probes to restore QUIC preference.
mux.resume keeps eligible native TCP logical streams alive while an automatic Reality carrier is replaced. Enable matching settings on both peers.
carrier.mode=quic uses a UDP/QUIC connection pool and requires native + raw + mux.enabled; security.type may be tls or reality.
reliable uses streams; auto prefers DATAGRAM with fallback; datagram does not degrade. DATAGRAM supports fragmentation, recovery, and adaptive FEC.
"mux": {
"enabled": true,
"max_sessions": 4,
"max_streams_per_session": 128,
"warm_spares": 1
}v0.3.0 continuity
Add resume to matching carrier.mode=auto mux blocks to preserve an eligible TCP logical stream while its physical QUIC or TCP attachment is replaced.
native + raw + security.type=reality + carrier.mode=auto + mux only. UDP, reverse publish, and forced tcp/quic-only modes are not resumable.
Set mux.resume=true on both endpoints running v0.3.0+. There is no silent downgrade when only one peer enables it.
resume_timeout defaults to 15s (100ms–5m). resume_buffer_size defaults to 4 MiB (64 KiB–64 MiB).
The registry is process-local. Use one unique outbound address whose TCP and UDP paths reach the same server process.
"carrier": { "mode": "auto" },
"mux": {
"enabled": true,
"resume": true,
"resume_timeout": "15s",
"resume_buffer_size": 4194304
}Hang NAT-side TCP/UDP services on tunnel server listen ports. Configure publish on the server and expose on the client.
Only native + raw, and mux must be enabled.
Server publish and client expose service names must match, and network must match as well (default tcp).
The client local target is not sent to the server; the server can only open allowlisted services.
QUIC reverse publish needs matching TLS or REALITY plus carrier.mode=quic (or auto) on both ends; TLS servers need cert/key.
Generate → validate → start.
Writes server.json and client.json.
tcptun config native --server proxy.example.com --port 9443Does not listen; useful after editing a config.
tcptun config check --config server.jsonWrites matching REALITY configs with carrier.mode=quic and mux enabled.
tcptun config native --quic --server proxy.example.com --port 9443Start the server first, then the client.
tcptun --config server.json
tcptun --config client.jsonExports URIs from tunnel outbounds; multiple addresses become multiple URIs.
tcptun uri export --config client.json --output client.uriREALITY
Configured under security for native + raw. Pair with carrier.mode for auto, TCP-only, or QUIC-only.
transport must be raw and cannot be combined with ws / h2 / h3.
Plain reality cannot stack with security.type=tls; choose exactly one security type.
Works with native tunnel endpoints. mixed and socks5 are unsupported for URI export.
Server private_key pairs with client public_key; short_id must match on both ends.
On native + raw + mux + security.type=reality + carrier.mode=auto, dual carriers enable QUIC-first with TCP fallback. Without mux, Reality stays TCP-only.
carrier.mode=tcp forces Reality TCP only. carrier.mode=quic forces the dedicated QUIC pool (no TCP fallback).
typeprivate_keyserver_namesshort_idsdestmax_time_difftypepublic_keyserver_nameshort_idspider_x{
"log": { "level": "info" },
"inbounds": [
{
"tag": "server",
"type": "native",
"address": ["0.0.0.0:9443"],
"network": ["tcp", "udp"],
"users": [{ "id": "change-me" }],
"transport": { "type": "raw" },
"security": {
"type": "reality",
"private_key": "REPLACE_WITH_SERVER_PRIVATE_KEY",
"server_names": ["example.com"],
"short_ids": ["abcd1234"],
"dest": "example.com:443",
"max_time_diff": "30s"
},
"carrier": { "mode": "auto" },
"mux": { "enabled": true }
}
],
"outbounds": [
{ "tag": "direct", "type": "direct", "network": ["tcp", "udp"] }
],
"route": { "default_outbound": "direct", "rules": [] },
"dns": {}
}Writes paired server.json and client.json; run tcptun uri export if you need URIs.
tcptun config native --server proxy.example.com --port 9443 --server-name example.com --dest example.com:443Generates matching REALITY configs for native on both ends.
tcptun config native --server proxy.example.com --port 9443 --server-name example.com --dest example.com:443Generates matching REALITY configs with carrier.mode=quic and mux enabled.
tcptun config native --quic --server proxy.example.com --port 9443 --server-name example.com --dest example.com:443Validate keys and fields first, then start.
tcptun config check --config server.json && tcptun --config server.jsonNative + raw + mux + security.type=reality with carrier.mode=auto is automatic QUIC-first with TCP fallback. Force a single carrier with carrier.mode=tcp or carrier.mode=quic. tcptun config native --quic generates the latter. Legacy security.type=reality-quic / mux.mode=quic aliases are not the current scheme.
Compare
Tunnel endpoints are native. mixed and socks5 remain for local proxy hops.
| Protocol | Credential | Interop | Default security | Mux | Best for |
|---|---|---|---|---|---|
native | token ↔ users[].id | tcptun only | raw + REALITY | Private mux, recommended | Throughput / reverse publish |
{
"tag": "proxy",
"type": "native",
"address": ["proxy.example.com:9443"],
"token": "change-me",
"transport": { "type": "raw" },
"mux": { "enabled": true }
}tcptun config native --server … --port …