Prizrak-Core
Prizrak-Core is the proxy core that powers Prizrak-Box on every platform. It is based on mihomo (formerly Clash.Meta).
Configurations written for mihomo work in Prizrak-Core unchanged. On top of that, the core adds features that mihomo does not have; they are described below.
- Source code: legiz-ru/Prizrak-Core
- Download the core: latest release, all versions on the Releases page
- Docker:
ghcr.io/legiz-ru/prizrak-core
Where to use it
Section titled “Where to use it”Prizrak-Core ships with Prizrak-Box, but it also works on its own. It is a standalone binary compatible with mihomo, so it can be used anywhere mihomo is used.
- Prizrak-Box. The core is built in, nothing to set up.
- Standalone. Download the archive for your platform from the releases and run it like regular mihomo:
mihomo -d <folder with config.yaml>. The Docker image works too. - TrayClash. A very lightweight Windows app, suitable for Windows 7 devices.
- OpenWrt-exodus. A tool for routers: OpenWrt, Asus, Keenetic, MikroTik. To install Prizrak-Core specifically, set a special variable during installation; see the OpenWrt-exodus documentation for which one.
Differences from mihomo
Section titled “Differences from mihomo”| Feature | mihomo | Prizrak-Core |
|---|---|---|
smart group (ML-based node selection) | — | ✅ |
| uTLS library | metacubex/utls v1.8.8 | prizrak-utls: up-to-date uTLS, Chrome 155 / Firefox 148 / Safari 26.3 |
| mux.cool multiplexing (Xray Mux, XUDP) | — | ✅ client and server |
| REALITY with ML-DSA-65, in sync with Xray | — | ✅ |
XHTTP: HTTP/2 flow governor (h2-flow) | — | ✅ (off by default) |
| HTTP/2: 32 KiB send buffer instead of 512 KiB | — | ✅ |
freedom: DIRECT with TLS fragmentation and socket options (Xray-core freedom) | — | ✅ |
Smart group
Section titled “Smart group”The smart group type comes from vernesong/mihomo. The group picks a node by weights computed from how the nodes actually perform: latency, connection success rate and traffic. Weight prediction by a LightGBM model can be enabled.
Since v1.19.33 the group also learns where each node leads and which sites refuse it:
- Node exit. The group finds out the country and network (ASN) where a node’s traffic comes out. To do that it sends a request through the node to
cloudflare.com/cdn-cgi/trace,api.ip.sboripwho.is, the next one if the previous does not answer. - Site responses. If a site refuses a node (blocked for its region, an error page,
403, a connection without any answer), the group remembers it and sends this site through other nodes. - No hasty verdicts. A region or a network is marked as refused only after several independent nodes from it fail. One refusal decides nothing, and a node that answers again clears the record.
Configuration details are on the Smart Proxy Group page.
uTLS: prizrak-utls
Section titled “uTLS: prizrak-utls”uTLS is the library that makes the TLS fingerprint (ClientHello) look like a real browser. It decides which browser a connection looks like for client-fingerprint and REALITY.
mihomo uses metacubex/utls v1.8.8. Prizrak-Core uses its own fork legiz-ru/prizrak-utls (branch v1.9.0-mod-meta-mldsa-chr155). It consists of three parts:
- Base: the metacubex
v1.9.0-mod-metabranch. It is a newer line than v1.8.8 in mihomo and carries all metacubex changes. - Up-to-date upstream refraction-networking/utls: sync with
crypto/tlsfrom Go 1.27.1, the Chrome 155 fingerprint, fixes for compressed certificate decompression, GREASE and PSK fixes, MLKEM1024 key exchange. The fork still builds with Go 1.20. - REALITY ML-DSA-65: see REALITY.
Which browser client-fingerprint means
Section titled “Which browser client-fingerprint means”client-fingerprint | mihomo | Prizrak-Core |
|---|---|---|
chrome | Chrome 133 | Chrome 155 |
firefox | Firefox 120 | Firefox 148 |
safari | Safari 16.0 | Safari 26.3 |
ios, android, edge, 360, qq | unchanged | unchanged |
In Prizrak-Core the firefox and safari fingerprints also carry the post-quantum X25519MLKEM768 key exchange, as real browsers of these versions do. In mihomo only chrome has it.
mux.cool (Xray Mux)
Section titled “mux.cool (Xray Mux)”Prizrak-Core supports mux.cool multiplexing compatible with Xray. It is the same protocol as mux in Xray-core: TCP streams and UDP/XUDP sessions share TCP connections. The implementation comes from MetaCubeX/mihomo#2980; it is more extensive than the one in Xray-core and fully compatible with it.
- Client. Enabled per proxy, separately from
smux. Do not enable both. - Server. VLESS and VMess inbounds accept mux.cool from Xray clients.
- XUDP. Dedicated UDP sessions, with control over their number and a policy for UDP/443.
- Connection limits. You can limit how many TCP connections are opened in total, how many sessions run on each at once and how many sessions a connection carries over its lifetime, after which it is replaced with a new one.
proxies: - name: vless-mux type: vless # ... mux.cool: enabled: true # max-carriers: 0 # maximum TCP connections, 0 means no limit # max-concurrency: 8 # active sessions per connection # max-connections: 128 # sessions over a connection's lifetime before rotation # xudp-concurrency: 4 # dedicated XUDP sessions per connection, 0 shares the pool # xudp-proxy-udp443: reject # reject | allow | skip: what to do with UDP/443To enable mux.cool for Remnawave subscription users, use the Remnawave host mapper; see mux.cool Multiplexing on the Remnawave integration page.
REALITY: ML-DSA-65 and Xray compatibility
Section titled “REALITY: ML-DSA-65 and Xray compatibility”REALITY is aligned with current Xray-core. The implementation comes from MetaCubeX/mihomo#2983.
- ML-DSA-65. Verification of the post-quantum signature on the REALITY certificate, as in Xray. The client sets the public key
mldsa65-verify, the server setsmldsa65-seed. - ML-KEM as in Xray. REALITY always keeps the fingerprint of the selected browser, including
X25519MLKEM768. Thesupport-x25519mlkem768option is no longer needed and is ignored. For older servers, set an older fingerprint explicitly, for examplechrome120. - New client options (named as in Xray):
password: alias ofpublic-key;spider-x;show;master-key-log.
- New server options:
target: alias ofdest;type;xver;min-client-ver/max-client-ver;mldsa65-seed;show;master-key-log.
proxies: - name: vless-reality type: vless # ... client-fingerprint: chrome reality-opts: public-key: <X25519 public key> short-id: <short id> mldsa65-verify: <ML-DSA-65 public key, base64url> # optionalTo add mldsa65-verify for Remnawave subscription users, use the Remnawave host mapper; see REALITY ML-DSA-65 on the Remnawave integration page.
XHTTP and HTTP/2
Section titled “XHTTP and HTTP/2”Two improvements based on ideas by Medium1992 from mihomo-proxy-ros.
HTTP/2 flow governor (h2-flow)
Section titled “HTTP/2 flow governor (h2-flow)”The client side of an HTTP/2 flow governor for XHTTP in h2 mode. The server sends data only as fast as the client reads it. Without the governor the server fills large HTTP/2 windows (4 MiB per stream in Go) and data nobody reads piles up in memory. The server needs no setup: the governor uses standard HTTP/2 flow control and works with any server.
How it works:
- Window by consumption. The governor shows the server a 64 KiB window, caps the frame size at 16 KiB and measures RTT with its own PING once a second. The server gets credit back only while unread data stays below what the client reads in a couple of round trips.
- Fast start. A new stream starts with a 256 KiB window or the window the connection recently needed. Until the first RTT is measured, the window opens with every credit.
- Path model from
min_rtt. On Linux and Android the governor reads TCP_INFO from the kernel (rtt,min_rtt,rttvar) and holds a download window to at most1.25 × highest read rate × min_rtt. On other systems it uses its PING measurements. - Works with a server-side governor. The same governor exists on the server side in Jolymmiles/Xray-core (the
h2Flowoption). A governed client announces the default 65535 window, which tells the server that a governor sits on the other side too, so the server does not hold back what it sends. The governor can therefore be on the client, on the server or on both: downloads do not stall and throughput stays the same.
Off by default, enabled per proxy with an h2-flow block:
xhttp-opts: h2-flow: enabled: true max-stream-receive-window: 16777216 # receive window per stream, bytes max-connection-receive-window: 33554432 # receive window per connection, bytesenabledturns the governor on.- Receive windows:
- optional, from 65535 bytes to 1 GiB;
- unset, Go’s default windows apply (4 MiB per stream);
- with the governor they are a ceiling and the real window follows reading;
- without it they are fixed windows.
h2-flow-control: truestill works as a shorthand forh2-flow.enabled: true. When both are set,h2-flow.enabledwins.- Downloads through
download-settingsuse the sameh2-flowblock.
Works only in h2 mode; it has no effect for h3 and http/1.1.
To enable h2-flow for Remnawave subscription users, use the Remnawave host mapper; see XHTTP Flow Control on the Remnawave integration page.
HTTP/2 send buffer: 32 KiB
Section titled “HTTP/2 send buffer: 32 KiB”Long-lived HTTP/2 streams keep their send buffer open for the whole connection: XHTTP in stream-one/stream-up modes and gRPC. Its size is reduced from 512 KiB to 32 KiB through the legiz-ru/prizrak-http fork on top of metacubex/http v0.1.8. With many concurrent tunnels this saves noticeable memory at the same speed. Nothing to configure.
freedom: TLS fragmentation and socket options
Section titled “freedom: TLS fragmentation and socket options”Since v1.19.33-r2 there is a proxy type freedom. It is DIRECT, the connection goes straight to the site, plus the fragment and sockopt settings of the Xray-core freedom outbound. Two fragmentation modes are taken from sing-box (tls_fragment, tls_record_fragment).
Fragmentation splits the TLS ClientHello so that the site name (SNI) never travels in one piece. It helps against DPI that matches the ClientHello as a whole. Use freedom through rules or groups only for the sites that need it.
proxies: - name: FRAGMENT type: freedom fragment: packets: tlshello length: "1-1" interval: "0" sockopt: tcp-congestion: bbr tcp-keep-alive-idle: 300 tcp-keep-alive-interval: 60 tcp-user-timeout: 10000 custom-sockopt: - { opt: "1", type: int, value: "1" }
rules: - DOMAIN-SUFFIX,example.com,FRAGMENTThis is the same as the Xray outbound below:
{ "protocol": "freedom", "settings": { "fragment": { "packets": "tlshello", "length": "1-1", "interval": "0" } }, "streamSettings": { "sockopt": { "customSockopt": [{ "opt": "1", "type": "int", "value": "1" }], "tcpcongestion": "bbr", "tcpKeepAliveIdle": 300, "tcpKeepAliveInterval": 60, "tcpUserTimeout": 10000 } }, "tag": "FRAGMENT"}The usual DIRECT options also work: interface-name, routing-mark, ip-version, tfo, mptcp. UDP goes out as with DIRECT.
fragment
Section titled “fragment”| Parameter | Values | What it does |
|---|---|---|
packets | tlshello, "N-M" or empty | tlshello rewrites the first ClientHello into several TLS records. "1-3" cuts the first three writes of the connection into TCP segments, empty cuts every write |
length | "N" or "N-M", bytes | Size of a piece. Required with split: length |
interval | "N" or "N-M", ms | Pause between pieces. With tlshello and 0 all records go out in one write |
max-split | "N" or "N-M" | At most this many pieces, the last one takes the rest. Empty or 0 means no limit |
split | length (default) or sni | sni: one random cut inside every label of the site name but the top-level one, as in sing-box; length is not needed then |
wait | interval (default) or ack | ack: every piece goes in its own TCP segment and the next one waits until the server acknowledged it, as in sing-box |
fallback-delay | ms, 500 by default | The pause for wait: ack where the acknowledgement can’t be seen |
- Ranges work as in Xray: the upper bound is not included, so
"100-200"gives 100 to 199. - A large ClientHello (with post-quantum keys) often comes in several writes.
freedomholds it until the record is complete and only then cuts it. Xray in this case sends it uncut. - After the fragmented part the connection works at full speed: data is copied between the sockets directly again.
- With
wait: ackthe acknowledgement is read on Linux, Android, macOS and iOS. On Windows and other systemsfallback-delayis used. An acknowledgement that comes back faster than 20 ms is taken for one from a middlebox, not from the server, andfallback-delayis waited too. For nearby servers lower it, otherwise every piece costs 500 ms.
sockopt
Section titled “sockopt”| Parameter | Values | What it does |
|---|---|---|
tcp-congestion | bbr, cubic, … | TCP congestion control. Linux and Android only |
tcp-user-timeout | ms | How long sent data may stay unacknowledged before the connection is closed. Linux and Android only |
tcp-keep-alive-idle | seconds | Idle time before the first keep-alive probe. A negative value turns keep-alive off |
tcp-keep-alive-interval | seconds | Interval between keep-alive probes |
custom-sockopt | list | Any socket option, as customSockopt in Xray |
custom-sockopt fields:
opt: option number;value: its value;type:int(default) orstr;level:6(TCP) by default;system:linux,windows,darwinor empty for every system;linuxalso applies on Android;network:tcp,tcp4,tcp6or empty.
Numbers can be decimal or 0x…. On Windows only int values work.
An option the system or the kernel does not support, for example bbr on many Android phones, is skipped with one warning in the log. The connection works without it.