Skip to content
NewsGitHubDownload

Configuration

The full reference is docs/config.yaml in the repository: every key with example values. Its comments are still mostly in Chinese, as upstream.

The config format is shared with mihomo, so the mihomo documentation on keys and the controller API applies too.

Keys What they set
mixed-port, port, socks-port, redir-port, tproxy-port Local proxy ports.
allow-lan, bind-address, authentication, lan-allowed-ips LAN access and authentication.
mode, log-level, ipv6, find-process-mode General behaviour.
log-format, log-timestamp-format Log format: text (the default) or json, one object per line for Loki and ELK.
tcp-concurrent, tcp-connect-timeout Outbound TCP dialing: race all resolved IPs; total connect budget in milliseconds (default 5000, range 100–600000).
external-controller, external-ui, secret The controller API and web UI; a network address needs a secret, see Controller security.
user-stats Per-user traffic accounting for inbounds (on by default), see User traffic.
user-limits Limits of inbound users: speed, connections, addresses, quota, expiry, see User limits.
metrics, health-without-auth Prometheus metrics (GET /metrics) and the /healthz, /readyz probes, see Metrics and health.
egress-guard Keeps the users of the server inbounds away from the private, local and cloud metadata addresses through DIRECT (on by default), see Server without root.
tun The TUN device and network stack. On Windows see TUN on Windows.
dns, hosts DNS servers (DNSCrypt v2 sdns:// stamps included), fake IP, policies for DNS queries.
dns.serve-expired, dns.serve-expired-ttl, dns.serve-expired-client-timeout Serving expired cache entries while they refresh (RFC 8767). By default an expired entry is answered at once and refreshed in the background, as before; a TTL and client timeout give RFC 8767 behaviour.
sniffer Domain detection from traffic (TLS, HTTP, QUIC) and BitTorrent detection for the PROTOCOL rule; skip-threshold sets after how many failures a destination is no longer sniffed.
proxies, proxy-groups, proxy-providers Outbound proxies, selection groups and remote proxy sets; type: chain groups for cascades, see Chains; type: desync for direct connections in networks with DPI filtering, see Desync outbound.
rules, sub-rules, rule-providers Routing rules and remote rule sets.
listeners, tunnels Extra inbounds, including server protocols. TCP listeners can listen on a unix socket: listen: unix:/path, with the mode set by unix-mode. They also take proxy-protocol: true: every TCP connection must then start with a PROXY protocol v1 or v2 header, whose client address becomes the source address (logs, rules, per-user IP limits). Only the peers in proxy-protocol-trusted (loopback by default) may send it; anything else is closed. UDP is not affected.
geox-url, geo-auto-update Where and how often to update geodata.

With auto-route on Windows, the core’s own connections go out bound to the egress interface: the default interface with auto-detect-interface (on by default), or the interface of interface-name. Windows ignores that binding when Forwarding or WeakHostSend is on for the interface, and the connections loop back into the TUN: nothing works, DNS fails and the log fills with loopback rejects. Internet Connection Sharing and Mobile Hotspot turn Forwarding on for the shared uplink and may leave it on after they stop.

The core does not change these flags, since turning them off stops the sharing. It checks them on the egress interfaces (never on the TUN itself), for IPv4 and IPv6, on every network change and every 5 seconds, and reports when they turn on: one warning in the log, naming the interface, the flags and the family, and a tun.forwarding event with state: on, so an app can offer to fix it. When the flags are off again, or the interface is no longer the egress one, it logs an info line and sends state: cleared. To fix it, turn both flags off as administrator:

Set-NetIPInterface -InterfaceAlias "Wi-Fi" -Forwarding Disabled -WeakHostSend Disabled

Sharing that connection through Mobile Hotspot or ICS stops working until the flags are turned on again.

PUT /rules with the body {"rules": ["DOMAIN,a.com,DIRECT", "MATCH,PROXY"]} replaces the top level rules without a config reload. The lines are written as in rules: and parsed by the same code: targets are proxies and groups, and RULE-SET, SUB-RULE and logic rules work. The whole list is validated before the swap; a bad line gives 400 with the error and leaves the rules unchanged. Proxies of proxy-providers cannot be targets (as in the config): use a group. sub-rules and rule-providers stay as they are.

The replacement lives in memory only: the next config reload (PUT /configs, systemctl reload, a restart) brings back the rules of the file. The response is {"count": N, "ephemeral": true}.

citrus -t -f ~/.config/citrus/config.yaml

The command parses the config and exits without starting anything. The packaged service re-reads the config on systemctl reload citrus.

PUT /configs (with or without force=true) and systemctl reload citrus apply the access policy of the new config at once: allow-lan, bind-address, skip-auth-prefixes, lan-allowed-ips, lan-disallowed-ips, authentication, the built-in ss-config and tuic-server. Without force=true the built-in port, socks-port, redir-port, tproxy-port and mixed-port listeners keep the ports they run on (a client may have changed them with PATCH /configs); they are rebound when allow-lan or bind-address changed.