Orange configuration.
Every option, every default, every example. Orange uses TOML — this page is the authoritative reference for the engine behind it.
Overview
Orange uses TOML format for configuration. The configuration file controls all aspects of the network engine including DNS resolution, proxy services, routing rules, and TUN mode settings.
Quick start
Copy the bundled example to start from a known-good baseline, then edit as needed.
cp example-config.toml config.toml
General
[general]
Global settings for the Orange engine including logging and IP address family policy.
error | warn | info | debug | traceerror: production, info: default, debug: development, trace: profiling
~ expands to the user home directory. The directory is created automatically if missing.dual: both A + AAAA, RFC 8305 default order (IPv6 first, parallel IPv4 after 250ms).prefer-ipv4: dual-stack resolve, force IPv4 first for connect.prefer-ipv6: dual-stack resolve, prefer IPv6 primary IP.ipv4: A records only, IPv4 only.ipv6: AAAA records only, IPv6 only.dns.rules host overrides are explicit user mappings and are not constrained by ip_version. URL host literals (IPv4/IPv6 written directly in URLs) that conflict with the policy are rejected.DNS service
[dns]
Core DNS server settings — listener, concurrency, and cache. Other DNS concerns are covered in their own sections: Reverse Cache, Rate Limit, Bootstrap, Local Network, DoT, DoH, Rules, and Fake-IP.
false for iOS/macOS NetworkExtension sandbox.IP:Portfalse if Orange is the system DNS.dns.rules > hosts file > upstream DNS. Empty string disables.Default:
"/etc/hosts" on macOS/Linux; disabled on iOS/Android sandbox.DNS reverse cache
[dns.reverse_cache]
optional
IP-to-domain reverse lookup cache. Used by TUN mode to recover the original hostname from a destination IP so that domain-based routing rules and logging still work after DNS has been resolved. Set either threshold to 0 to disable.
budget = global_max / ip_count, and each IP keeps min(per_ip_max, budget). FFI memory-constrained environments: 2000. Set to 0 to disable.DNS rate limit
[dns.rate_limit]
optional
Defends against DNS amplification attacks by limiting requests per client IP over a sliding time window.
window_ms = 1000 caps each IP at 200 queries/second.DNS bootstrap
[dns.bootstrap]
DNS servers used to resolve infrastructure hostnames (upstream proxies, DoH/DoT providers, WireGuard endpoints). Bootstrap queries are forced to bypass the TUN tunnel and go out on the physical interface, preventing circular routing. Static hostname-to-IP mappings for infrastructure are now unified under [dns.rules].
IP (port 53) or IP:Port. Defaults include both IPv4 and IPv6 addresses for NAT64 / IPv6-only networks.DNS local network
[dns.local_network]
optional
Opts local-network domains (mDNS, LAN suffixes, service discovery) out of upstream DNS so they stay on the local link and do not leak to external resolvers. Matched queries return NoError + empty Answer (not NXDOMAIN) so that RFC 2308 negative caching does not block subsequent mDNS attempts.
.local) matches anything ending in .local; mid-string pattern match (._dns-sd.) matches anything containing the substring. Set to [] to disable.DNS over TLS (DoT)
[dns.dot]
optional
Exposes the DNS service over TLS, typically on port 853. Requires a valid TLS certificate and private key in PEM format.
DNS over HTTPS (DoH)
[dns.doh]
optional
Exposes the DNS service over HTTPS, typically on port 443. Standard query path is /dns-query (RFC 8484).
/dns-query.DNS rules
[dns.rules]
Unified DNS routing table. The same rule set applies to every inbound (TUN, HTTP, SOCKS). Shorthand syntax covers reject and simple host mappings; the full { ... } form is required for DNS upstreams, conditional matches, and rewrites. Infrastructure hostnames (upstream proxies, DoH/DoT providers, WireGuard endpoints) should use { host = "..." } mappings here so they resolve without a circular lookup.
google.com.*.cn but not cn itself.cn and every *.cn.cn category.Rule values can be a shorthand string ("reject" or a literal IP hosts mapping), a single full-form object, or an array of objects evaluated top-to-bottom. Conditions use the when object; today the supported field is src (CIDR, supports negation with ! and multi-value with ,).
# Shorthand — reject "*.ads.com" = "reject" # Shorthand — hosts mapping "example.com" = "127.0.0.1" # Full form — hosts mapping "example.com" = { host = "127.0.0.1" } # DNS upstream "@geosite:cn" = { to = ["223.5.5.5"] } # Multiple upstreams (concurrent) "example.com" = { to = ["8.8.8.8", "1.1.1.1"] } # With specific outbound "*.internal" = { to = ["10.0.0.53"], via = "wg-home" } # Conditional rules (by source IP) "*.corp" = [ { when = { src = "10.0.0.0/8" }, to = ["10.0.0.53"] }, { when = { src = "!10.0.0.0/8" }, reject = true }, ] # Suffix rewrite — ask the router for .loc while the client queries .local # rewrite requires a wildcard pattern (*.local / +.local) and must start with '.' "*.local" = [ { when = { src = "192.168.0.0/16" }, to = ["192.168.1.2"], rewrite = ".loc" }, ] # Default upstream (required) "default" = { to = ["8.8.8.8"] }
Fake-IP
[dns.fake-ip]
optional
Assigns virtual IPs from 198.18.0.0/15 to hostnames so that clients can start a connection immediately; Orange then recovers the hostname at the TUN/proxy boundary to make routing decisions. Best suited to TUN transparent proxy mode. IPv4 only — AAAA queries return empty responses, and the 198.18.0.0/15 range must not be used for anything else.
~ expands to user home.0 disables the timer; saves still happen when changes accumulate.last_access write-backs. Larger values reduce IO pressure.blacklist: every hostname uses Fake-IP except those matched by rules.whitelist: only hostnames matched by rules use Fake-IP.mode. Supports exact (example.com), suffix (.local), suffix+root (+.example.com), and @geosite:tag. Default blacklist mode already skips localhost, *.local, *.lan, *.home.arpa, and PTR ranges.Proxy core
[proxy]
Top-level settings that apply across every inbound proxy: shared authentication, domain-resolution policy for IP-based rule matching, and the routing-rule match cache. Inbound listeners (HTTP, SOCKS5, Mixed), TLS, Physical interface binding, Upstreams, Upstream Health Check, and Routing Rules each have their own section.
username:password. When unset all inbound proxies are open (default).Strongly recommended when any proxy binds to a non-localhost address.
resolve_ip = true can override.HTTP proxy
[proxy.http]
HTTP/HTTPS proxy on a single port. Handles HTTP forward proxy, HTTPS CONNECT tunnels, and WebSocket upgrades from the same listener.
IP:Port.[proxy.http.pool]Optional connection reuse to reduce handshake overhead to upstream HTTP/HTTPS proxies.
SOCKS5 proxy
[proxy.socks]
RFC 1928 SOCKS5 implementation focused on the CONNECT command. Supports domain, IPv4, and IPv6 targets. Authentication is either none or RFC 1929 username/password (configured via [proxy] auth). The BIND command is not supported.
IP:Port.Mixed proxy
[proxy.mixed]
Serves HTTP and SOCKS5 on the same port. Orange sniffs the first byte: 0x05 routes into the SOCKS5 handler, anything else is treated as HTTP (including CONNECT tunnels). The mixed listener can run alongside dedicated HTTP/SOCKS5 listeners. Routing rules apply uniformly to every inbound.
IP:Port.TLS
[proxy.tls]
TLS settings that apply to upstream encrypted connections.
Physical interface binding
[proxy.bypass]
Prevents routing loops in TUN mode by binding upstream connections to the physical network interface rather than the TUN device. Only relevant when TUN mode is active; HTTP/SOCKS-only deployments do not need this. On macOS/iOS the recommended alternative is Swift-side excludedRoutes.
en0 (WiFi/Ethernet); Linux reads /proc/net/route; iOS falls back to system protection; Android needs manual config or VpnService.protect().auto_detect. Use ifconfig to list interfaces (typically en0 is 4 or 5).auto_detect. Use ip link show to list interfaces (e.g. eth0, wlan0, enp0s3).Upstreams
[[proxy.upstreams]]
Define the upstream proxies Orange can route traffic through. Each upstream is referenced from [[proxy.rules]] by its name. The primary types are https and wireguard; trojan and shadowsocks (SIP022) are also available for specific deployments. Every upstream can override the global health check with its own health_check block or disable probing entirely via health_check = { disabled = true }.
[[proxy.upstreams]] type = "https" name = "http-us" [proxy.upstreams.config] addr = "gateway.example.com:443" auth = "user:pass" # H2 connection pool (optional) [proxy.upstreams.config.h2] soft_max_age_secs = 120 # Soft TTL, no new requests after idle_timeout_secs = 30 # Close idle connections max_streams = 100 # Max concurrent streams per conn max_conns = 2 # Max H2 connections per upstream
Encrypted TLS transport authenticated by password. Available for deployments that already use a compatible upstream.
[[proxy.upstreams]] type = "trojan" name = "trojan-01" [proxy.upstreams.config] addr = "gateway.example.com:443" password = "your-password-here"
Encrypted transport with a pre-shared key (SIP022). Available for deployments that already use a compatible upstream.
[[proxy.upstreams]] type = "shadowsocks" name = "ss-01" [proxy.upstreams.config] addr = "gateway.example.com:8388" method = "2022-blake3-aes-256-gcm" psk = "your-base64-encoded-psk"
[[proxy.upstreams]] type = "wireguard" name = "wg-home" [proxy.upstreams.config] endpoint = "vpn.example.com:51820" publicKey = "xxxxxxxxxxx=" privateKey = "yyyyyyyyyyy=" address = "10.0.0.2/24" allowedIPs = "0.0.0.0/0,::/0" dns = "10.0.0.1" # mode = "auto" # auto (default) | userspace | kernel # mtu = 1420 # Default 1420. PPPoE: 1412. Range: 576-9000.
Kernel mode requires privileges: Linux needs CAP_NET_ADMIN and the ip command; macOS needs sudo, ifconfig, and route; Windows is not supported. Userspace mode has no such requirements but needs separate route setup.
# Override target and timeout health_check = { url = "http://cp.cloudflare.com", timeout_secs = 3 } # WireGuard probe uses IP:Port health_check = { url = "1.1.1.1:443" } # Disable probing for this upstream health_check = { disabled = true }
chmod 600.Upstream health check
[proxy.health_check]
Periodically probes each upstream and exposes the result through /health, Prometheus metrics, and logs. The values here form the global default; any upstream can override them inline (see the Upstreams section).
http://www.gstatic.com/generate_204. For WireGuard, an IP or IP:Port (default port 443).Routing rules
[[proxy.rules]]
Decides what each connection does: stay direct, take a tunnel, or be rejected. Rules can match on domain, IP CIDR, GeoSite tag, or GeoIP tag, and can be filtered by a when object covering source IP, port, inbound type, and ALPN.
example.com), +.example.com, *.example.com, and @geosite:tag.when.port for ranges).when wrapper.resolve_domain_for_ip_rules switch.when = { ... }!) and multi-value (,).tcp | udp | tun | h3 | mixed.h2 | http/1.1 | h3.# Exact domain match — office intranet [[proxy.rules]] domain = "api.internal.corp" to = "wg-office" # Wildcard domain — home network [[proxy.rules]] domain = "*.home.lab" to = "wg-home" # Reject ad subdomains (does not match the apex) [[proxy.rules]] domain = "+.ads.example.com" reject = true # Conditional: home network direct, others via WireGuard [[proxy.rules]] domain = "*.home.lab" when = { src = "192.168.1.0/24" } direct = true [[proxy.rules]] domain = "*.home.lab" to = "wg-home" # GeoSite match — block ads [[proxy.rules]] geosite = "category-ads-all" reject = true # GeoIP private IPs direct [[proxy.rules]] geoip = "private" direct = true # SSH direct [[proxy.rules]] port = 22 direct = true # Force resolve so IP/GeoIP rules can match this domain [[proxy.rules]] domain = "audit.example" resolve_ip = true to = "http-us" # Fallback (no match condition) — auto moved to end [[proxy.rules]] direct = true
+. outranks *. of the same length. Across rule types (domain vs geosite, ip_cidr vs geoip) configuration order applies. Fallback rules without a match condition are automatically moved to the end of the list.TUN device
[tun]
System-level network gateway over a virtual interface. Captures all OS-level traffic and feeds it into the routing engine. Requires admin privileges on Linux/macOS and is not supported on Windows. SNI/HTTP sniffing lives in its own section.
["0.0.0.0/0"] captures all IPv4. macOS benefits from a split-range alternative to avoid default-route conflicts.ffi_output_backlog_hwm. FFI memory-constrained: 1048576 (1 MB).SNI / HTTP sniffing
[tun.sniff]
Extracts TLS SNI or HTTP Host from the first bytes of a TCP connection so that domain-based routing rules still work when only an IP destination is visible. Sniffing requires the client to speak first; enabling it on server-first protocols (MySQL, SMTP, SSH) introduces timeout delays. The default port allow-list keeps it limited to HTTP/HTTPS-style traffic.
443), a range (8080-9999), or a comparison expression (<1000, <=1024, >1000, >=1024). Mix forms freely.Timeouts
[timeouts]
Connection timeout settings.
Health API
[health]
HTTP endpoints for health, metrics, live streams, and configuration reload. Target: Kubernetes liveness/readiness probes, load balancer checks, monitoring systems, and real-time dashboards.
/health — JSON health status, including per-upstream latency and availability./metrics — Prometheus scrape endpoint. Requires prometheus-exporter feature./api/metrics/live — Server-Sent Events stream of live metrics (push model, long-lived connection)./reload — Trigger configuration hot-reload without restarting the engine.{
"status": "ok",
"upstreams": [
{
"name": "http-us",
"type": "https",
"latency_ms": 120,
"average_latency_ms": 115,
"available": true,
"last_check_ago_secs": 15
}
]
}
curl http://127.0.0.1:9898/health | jq curl http://127.0.0.1:9898/metrics curl -N http://127.0.0.1:9898/api/metrics/live
127.0.0.1 or a private network, and keep them off the public internet.Metrics live stream
[health.metrics_live]
optional
Tunes the Server-Sent Events push cadence for /api/metrics/live. Orange only computes and broadcasts when at least one subscriber is attached — no background work happens while idle. Subscribers that cannot handle 1 Hz (mobile apps in background, embedded devices) should throttle or pause on the client side; raise interval_ms only when that is not an option.
Tracing
[tracing]
Real-time request lifecycle tracing. Traces are pushed to UI clients over WebSocket so dashboards can observe every proxied request as it flows through the engine.
Tracing WebSocket
[tracing.websocket]
optional
Controls how trace events are aggregated before being delivered to WebSocket subscribers. Tighter flush intervals reduce end-to-end latency; larger batches reduce frame overhead.
Memory pressure
[memory_pressure]
Adaptive protection for memory-constrained environments (e.g. iOS NetworkExtension's ~50 MB limit). A background monitor polls the Rust allocator and triggers tiered mitigations when thresholds are crossed, preventing the process from being killed by the OS.
[memory_pressure] block disables pressure management (desktop default). When present, at least one non-zero threshold must be set and thresholds must be strictly increasing. Pressure-level changes log a warn-level message for diagnostics. This block does not support hot-reload; restart is required to apply changes.warning_bytes.critical_bytes.[memory_pressure] warning_bytes = 31457280 # 30 MB critical_bytes = 39845888 # 38 MB emergency_bytes = 46137344 # 44 MB check_interval_ms = 5000
Dataset
[dataset]
GeoSite and GeoIP datasets for large-scale domain/IP classification. Supports local files and HTTP URLs with automatic caching.
[dataset] geosite = ["./data/sites.dat"] geoip = ["./data/ips.dat"]
.orange/dataset-cache/. Delete cache to force re-download.CLI: use
orange dataset --config config.toml to inspect loaded datasets.