Compatibility
Two integration suites in test/interop check Citrus. Both run real binaries of other projects against Citrus built from the branch, and both run in CI on every PR that touches transports, TLS, listeners or outbounds.
- mihomo clients → Citrus server. Official mihomo releases as the client, the way the apps of Mikan users work (Mikan, ClashFest, Clash Verge and the like).
- Xray ↔ Citrus. Xray v26.10.10 in both directions: a Citrus client against an Xray server and an Xray client against a Citrus server.
The binaries are pinned by version and SHA-256, so a swapped release fails the job.
mihomo clients
Section titled “mihomo clients”CI runs two mihomo releases: v1.19.32 (the one the panel targets and the latest stable release) and v1.19.31. v1.19.24 (April 2026) was run once locally and is not in CI.
Each case sends a GET and a 256 KiB POST through the tunnel to a local echo server and checks the SHA-256 of the body. Where the protocol carries UDP, datagrams of 16, 512 and 1200 bytes make a round trip. The server log confirms the traffic went through the Citrus inbound. For VLESS, Trojan, multi-user Shadowsocks 2022 and Hysteria2 the test also reads GET /traffic/users: the traffic is recorded for the user who sent it and nobody else.
| Citrus inbound ← mihomo outbound | TCP | UDP | Stats | v1.19.32 | v1.19.31 | v1.19.24 |
|---|---|---|---|---|---|---|
| VLESS + REALITY + Vision, classic X25519 | ✓ | ✓ | ✓ | pass | pass | pass |
VLESS + REALITY + Vision, client with support-x25519mlkem768 |
✓ | ✓ | pass | pass | pass | |
VLESS + REALITY + Vision, server with mldsa65-seed, classic client |
✓ | ✓ | pass | pass | pass | |
same, client with support-x25519mlkem768 |
✓ | ✓ | pass | pass | pass | |
REALITY with require-x25519mlkem768 and mldsa65-seed, client with ML-KEM |
✓ | pass | pass | pass | ||
REALITY with require-x25519mlkem768, classic client |
— | refused, as it should be | refused | refused | ||
| VLESS XHTTP stream-one over TLS (h2) | ✓ | ✓ | pass | pass | pass | |
| VLESS XHTTP packet-up over TLS (h2) | ✓ | ✓ | pass | pass | pass | |
| VLESS XHTTP packet-up over TLS (HTTP/1.1) | ✓ | ✓ | pass | pass | pass | |
| VLESS WebSocket + TLS | ✓ | ✓ | pass | pass | pass | |
| VLESS gRPC + TLS | ✓ | ✓ | pass | pass | pass | |
| VLESS Vision + TLS | ✓ | ✓ | pass | pass | pass | |
| Trojan TLS | ✓ | ✓ | ✓ | pass | pass | pass |
Shadowsocks 2022 (2022-blake3-aes-128-gcm), single user |
✓ | ✓ | pass | pass | pass | |
Shadowsocks 2022, multi-user (EIH, iPSK:uPSK) |
✓ | ✓ | ✓ | pass | pass | pass |
| Hysteria2 | ✓ | ✓ | ✓ | pass | pass | pass |
| TUIC v5 | ✓ | ✓ | pass | pass | pass | |
| AnyTLS | ✓ | ✓ | pass | pass | pass | |
| TrustTunnel over HTTP/2 | ✓ | ✓ | pass | pass | pass | |
| TrustTunnel over QUIC | ✓ | ✓ | pass | pass | pass | |
| Snell v3 | ✓ | ✓ | pass | pass | pass | |
| Snell v4 | ✓ | ✓ | pass | pass | fail (client) | |
| Snell v5 | ✓ | ✓ | pass | pass | fail (client) | |
| Mieru, TCP transport | ✓ | ✓ | pass | pass | pass | |
| Mieru, UDP transport | ✓ | ✓ | pass | pass | pass | |
| Sudoku | ✓ | ✓ | pass | pass | pass | |
| HTTP with authentication | ✓ | pass | pass | pass | ||
| SOCKS5 with authentication | ✓ | ✓ | pass | pass | pass | |
| mixed with authentication, as HTTP | ✓ | pass | pass | pass | ||
| mixed with authentication, as SOCKS5 | ✓ | ✓ | pass | pass | pass | |
| SOCKS5 with a wrong password | — | refused, as it should be | refused | refused |
No incompatibility on the Citrus side was found.
What subscriptions should account for
Section titled “What subscriptions should account for”- Snell v4 and v5 need a recent client. mihomo v1.19.24 knows only v1–v3 and refuses its own config with
version: 4. From v1.19.31 on, v4 and v5 work. The mihomo client defaults toversion: 1and the Citrus server toversion: 4, so a subscription has to state the version, and should pick v3 for older clients. require-x25519mlkem768admits only clients with ML-KEM. A client withoutsupport-x25519mlkem768sends only a classic X25519 key share, and a server withrequire-x25519mlkem768: truewill not let it in. That is by design. Turn the requirement on only when every client can do X25519MLKEM768: Citrus does by default, mihomo withsupport-x25519mlkem768: true.mldsa65-seedon the server does not bother older clients. mihomo clients have nomldsa65-verifyand simply do not check the extra signature.
Outside the matrix
Section titled “Outside the matrix”- Protocols Citrus removed (see below): connections with them will fail.
- Protocols Citrus has no inbound for: SSH, WireGuard, MASQUE, OpenVPN, Tailscale, EasyTier, ZeroTier, gost relay. In Citrus these are outbounds only, or servers of other projects.
desync: not a protocol but a DIRECT outbound adapted to networks with DPI filtering, so there is nothing to interoperate with, see Desync outbound.- ShadowQUIC: Citrus has the inbound, but it is not in the matrix yet.
The core’s User-Agent
Section titled “The core’s User-Agent”Citrus sends its own HTTP requests — subscriptions, proxy-providers, rule-providers, geodata — with the User-Agent from global-ua. By default it is two tokens separated by a space:
clash.meta/<version> citrus/<version>For example, clash.meta/v1.0.0-beta.3 citrus/v1.0.0-beta.3. The clash.meta/ token comes first, so panel rules (Remnawave, Marzban, 3x-ui and others) that recognise a mihomo client by the start of the string keep working. A panel tells Citrus from mihomo by the second token:
\bcitrus/(\S+)The first group is the core version. When global-ua is set in the config, it is sent as it is, without the citrus/ token.
The suite runs against Xray v26.10.10. Keys come from Xray itself (xray x25519, mldsa65, uuid, tls ech), and the REALITY target is a local TLS 1.3 server with a chain over 4 KB, so the ML-DSA-65 signature fits in the certificate.
| Case | Variants |
|---|---|
Citrus client → Xray server: VLESS + Vision + REALITY with mldsa65Seed |
chrome, firefox, safari, ios fingerprints; a wrong mldsa65-verify must fail |
Xray client → Citrus server: mldsa65-seed and require-x25519mlkem768 |
chrome, firefox; a wrong mldsa65Verify must fail |
| Citrus client → Xray server: XHTTP (REALITY, h2) | packet-up, stream-one, stream-up, a path with ?query, x-padding-bytes: "0-0" |
| Xray client → Citrus server: XHTTP | packet-up, GET uplink, data in headers |
XHTTP over HTTP/3 (TLS, alpn: [h3]) in both directions |
packet-up, stream-one, stream-up; Xray client also with GET uplink and data in headers |
ECH in both directions (Xray echServerKeys, Citrus ech-key) |
the right and a wrong config; the wrong one must fail, which proves ECH was really used |
| VLESS + gRPC over REALITY in both directions |
Checked separately, not in CI: 64 MiB uploads and downloads over XHTTP stream-one, stream-up and packet-up with Xray in both roles.
Removed protocols
Section titled “Removed protocols”Citrus dropped protocols that a VPN service does not need and that have a modern replacement.
| Removed | Use instead |
|---|---|
| VMess (with the mKCP, mekya and TLSMirror transports) | VLESS: Vision, REALITY, VLESS Encryption, WebSocket, gRPC and XHTTP transports |
| ShadowsocksR | Shadowsocks with an AEAD cipher or 2022-blake3-* |
| Shadowsocks stream ciphers (rc4-md5, aes-ctr, aes-cfb, chacha20, chacha20-ietf, xchacha20, table, rc4) | AEAD (aes-128-gcm, aes-256-gcm, chacha20-ietf-poly1305) or 2022-blake3-* |
| v2ray-plugin | VLESS or Trojan over WebSocket and TLS |
| Hysteria v1 | Hysteria2 |
TUIC v4 (token) |
TUIC v5 (uuid and password) |
| mKCP | QUIC-based protocols: Hysteria2, TUIC v5 |
How old configs and subscriptions behave is covered in migrating from mihomo.
