93 Commits
Author SHA1 Message Date
kazeyukiro a46b3b010b feat(listener): HTTP/SOCKS inbounds with client share and Clash proxies
Re-apply on post-theme baseline (52e848a): schema, compile modules,
http:// and socks5:// share URIs, and client subscription YAML
(type: http / socks5). Fixes gofmt for CI.
2026-10-07 16:42:27 +00:00
風雪and風雪 f18419da8b fix(listener): allow TCP and UDP listeners to share a port (#91)
The endpoint conflict guard in ensureEndpointAvailable (and its batch
counterpart ensureBatchEndpointsAvailable) compared only port ranges and
bind addresses — it never looked at the L4 protocol. A TCP listener on
443 and a UDP listener on 443 are independent sockets at the OS level and
Mihomo serves both fine, but 3m-ui rejected the second one with
"conflicts on 0.0.0.0:443". So you could not run e.g. VLESS (TCP) and
TUIC/Hysteria2 (UDP) on the same port.

Add protocol.ListenerTransports, which maps a listener's protocol kind to
the L4 transport(s) its inbound binds (UDP for tuic / hysteria /
hysteria2 / shadowquic / mieru; TCP for everything else). The proxy `udp`
toggle is UDP relay over an established connection and is intentionally
ignored — it does not open a UDP listening socket. The conflict check now
also requires the two listeners to share an L4 transport, so a TCP+UDP
pair on the same address:port no longer collides while two TCP or two UDP
listeners still do.

Tests:
- protocol.ListenerTransports unit tests (udp/tcp/case-insensitive).
- listener.listenersShareTransport unit tests.
- End-to-end: a TCP+UDP pair on 443 coexists and the UDP listener stays on
  443 through Create (it is not silently reassigned to a free port), while
  two TCP or two UDP on 443 still conflict.

Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
2026-10-05 16:51:18 +08:00
kazeyukiro 615b199888 fix(ci): set Listener ID via embedded BaseModel in export/share 2026-10-03 01:33:53 +00:00
kazeyukiro aac7517172 fix(protocol): add NodeModel.ID for certstore-backed share TLS 2026-10-03 01:25:39 +00:00
kazeyukiro 842fd72ea4 refactor(export): unify TLS profile for URI, share and subscription
Introduce internal/export as the single cert/SNI/skip-cert resolver.
ClientURIs, DecodeNodeModel, resolveShareTLS and Clash proxy TLS helpers
all consume BuildProfileFromConfig so formal IP certs always emit sni and
subscriptions no longer diverge from share links on certificate identity.
2026-10-03 01:25:14 +00:00
kazeyukiro 05816736a0 fix(share,uri): load certstore PEM and always emit HY2 sni
DecodeNodeModel now sets listener ID and materializes certificate paths
or certstore PEM. resolveShareTLS expands paths and never leaves SNI
empty on IP hosts. HY2 share/URI default alpn=h3 and force sni=host
when unresolved so Loon no longer sees bare host:port links.
2026-10-03 01:14:45 +00:00
kazeyukiro b36f930099 fix(sub,share,uri): unify SNI/skip-cert across all protocol exports 2026-10-02 15:15:34 +00:00
kazeyukiro 91a9f924a4 fix(share): emit HY2 SNI for IP hosts (formal cert path) 2026-10-02 15:03:42 +00:00
kazeyukiro 32c6aee335 fix(sub): stop forcing skip-cert-verify for formal certificates
Rewrite DecideClientSkipCertVerify: CA-signed / non-self-signed PEMs never
force insecure; domain endpoints without embedded PEM rely on system CAs;
IP-only without PEM still skips. URI export loads certstore PEM, prefers
SNI as verify identity, and HY2/VLESS share paths use the same decision.
2026-10-01 08:59:43 +00:00
kazeyukiro bbe10da416 fix(release,hy2): sane auto release notes; Loon-friendly HY2 share links
Release workflow: previous stable tag only, merge-base range, first-parent
log, noise filter, and per-section caps so notes stay readable.

Hysteria2 share/URI: resolve SNI from access profile/public host, emit
allowInsecure alongside insecure, encode userinfo via url.User so Loon
and similar clients can complete TLS.
2026-10-01 08:41:28 +00:00
風雪 96dc3b38a2 style: align VMess test fields 2026-09-30 22:06:49 +08:00
風雪 e75a5847d7 style: format VMess changes 2026-09-30 22:04:42 +08:00
風雪 fcd5fb8dee test: preserve VMess per-user alterId 2026-09-30 22:00:11 +08:00
風雪 caff433bcc fix: preserve VMess per-user alterId on compile 2026-09-30 21:59:34 +08:00
kazeyukiro 8507b8636c fix(sub): audit URI and YAML export for CDN access fields
Fill security=tls, sni, fp, and ws host from AccessSNI/PublicHost for
domain+WS share links and client YAML; default fingerprint chrome.
2026-09-30 08:28:16 +00:00
kazeyukiro d2a80f5f80 fix(protocol): strip ws-headers from VLESS/VMess listeners
Mihomo inbound only supports ws-path for WebSocket; headers are
client-side. Keep ws-headers in panel config for share/export.
2026-09-30 08:01:44 +00:00
kazeyukiro fec9733baa docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
docs: remove third-party panel name references from copy
Drop  and external m-ui product names from docs, UI strings,
and code comments. Keep functional descriptions of server egress and WARP.
2026-09-29 15:51:57 +00:00
kazeyukiro af01f1f237 fix(share): emit VLESS WS host in URI when ws-headers empty 2026-09-28 14:51:08 +00:00
kazeyukiro f9f5c66fed fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
fix(ci,sub): gofmt + align sing-box TUN with official schema
Use official Tun address example (172.18.0.1/30 + ULA IPv6) and
keep auto_route/strict_route/stack per sing-box docs.
2026-09-28 14:39:28 +00:00
kazeyukiro b664e57117 fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
fix(share): CDN VLESS/WS export Host + path defaults
When ws-headers Host is empty, client URI/YAML/subscription now fall
back to SNI or public host. Always emit WS path (default /). Panel
gains an explicit WebSocket Host field for CDN setups.
2026-09-28 14:27:44 +00:00
風雪and風雪 35f4c521c3 fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
fix(vless): stop exporting flow on transports that cannot carry it (#87)
* fix(vless): stop exporting flow on transports that cannot carry it
The form promised that flow is cleared for ws/grpc/xhttp and the tooltip still
says so, but exported links kept carrying flow=xtls-rprx-vision. Two separate
reasons, both fixed.
The clear came too late to matter. formValuesToConfig wrote `flow` into the
config inside the protocol switch, and only afterwards did the "Vision is
TCP-only" rule run — where it mutated the form state, not the config it had
already been copied into. The transport is now resolved before anything writes,
and the drop also removes a flow inherited from the previous config, so changing
a listener's transport actually takes effect.
The URI generators never asked. Every one of them emitted whatever flow it found:
- node: vlessURIs decided flow before resolving the transport, so the type it
had just computed was irrelevant to the decision. It now resolves the
transport first. ClientURIsWithCredentials also merged the listener-level
flow onto every user on protocol alone, which reintroduced the value the
form had dropped.
- protocol: the share URI and the client YAML both wrote spec.Flow unguarded,
even though TransportSpec knows the network. Same for the server-side
compile, which pushed the listener default onto users — Mihomo rejects that
combination outright, so this one takes the listener down.
- mui: BuildVLESSShare set flow unconditionally, and bridge.go injected the
listener default into users before it was known to be applicable.
- converter: copyTransport resolved `network` but left a stale flow behind.
The rule is now one predicate per representation: TransportSpec.CarriesFlow,
VLESSHandlerSpec.CarriesFlow, and protocol.TransportCarriesFlow for raw configs.
TCP/raw keeps Vision; ws, grpc, xhttp, mkcp and mekya never emit it.
Tests pin each path in both directions — a stale flow disappears on ws/grpc/
xhttp, and a genuine TCP listener keeps its flow. The frontend one runs the real
form builder and fails, in three of five cases, against the previous ordering.
* fix(uri): carry vmess ws Host and align hysteria2 TLS flags across user shapes
Auditing the remaining exporters after the flow fix turned up two more places
where the link disagreed with the listener.
vmessURIs built its JSON by hand instead of going through transportParams, so a
ws listener that pins a Host header exported only the path: the client had
nothing to send as Host and the listener was unreachable through the link, even
though the client YAML built from the same config worked. It also dropped
allowInsecure, which vless and trojan both carry — a listener whose certificate
the client cannot verify exported a link that failed the handshake. The repeated
tlsParams() calls were hoisted while touching this.
hysteria2URIs reads users either as rows or as a map, and the two branches
disagreed: one asked certutil.ShouldSkipCertVerify and emitted only `insecure`,
the other asked clientSkipCert and emitted `insecure` plus `allowInsecure`. The
same listener therefore produced different links depending on how its users were
stored. Both now use clientSkipCert, the helper intended for share URIs, and
emit both keys.
Not fixed, for the record: an SS listener with simple-obfs exports a bare ss://
link with no `plugin=` entry, so the obfs configuration is lost. The client YAML
does translate it (plugin: obfs). Fixing the URI means committing to a SIP002
plugin encoding that varies between clients, which is worth deciding separately
rather than guessing at here.
* i18n: complete zh-CN and guard against the locale regressions already seen
zh-CN was missing seven listener hints that en.ts declares, so those tooltips
rendered in English for the primary UI language. Translated: client_auth_cert,
client_auth_type, ech_key, enable_pure_downlink, mux, padding and table_type.
The rest of the catalogue is deliberately left alone. Seventeen locales each lack
65 keys that en.ts has; the provider already resolves those from en.ts, so they
degrade to English rather than breaking, and filling 1100 strings with
unreviewed machine translation would be worse than the fallback. The test prints
the per-locale gap so it stays visible.
The new locale test guards the regressions this directory has actually hit: a
duplicated key inside one object (which breaks the type check), and a key renamed
in en.ts leaving an orphan behind. It also asserts zh-CN stays complete, since a
gap there reads as a defect rather than as translation lag.
* i18n: declare the 24 keys the UI asks for but no locale ever defined
Twenty-four keys were referenced from components without being declared in any
locale, so t() fell through to its hardcoded fallback. Where a fallback existed
that merely meant the string could never be translated; where none existed —
protocolHint, access_sniHint, jlsHint, shadowTlsHint and the rest — the tooltip
either rendered the raw key, "listeners.protocolHint", or disappeared entirely,
because fieldTip() suppresses a value that equals the key it looked up.
en.ts takes the text the code was already falling back to, so nothing changes on
screen; the strings just become translatable. The few without a fallback were
written from the surrounding context — the field they annotate, its placeholder,
and the wording of the neighbouring hints. trafficMultiplierHint is the exception:
its fallback was Chinese, which is wrong for the English catalogue, so it now has
a real English string.
zh-CN gets all twenty-four, which keeps it complete against en.ts.
The locale test now reads through the TypeScript AST. It previously parsed line by
line and these files contain minified regions — several "key":"value" pairs on one
line — so a whole block of keys was invisible to it, which is how this gap stayed
hidden. It also gained the assertion that would have caught the bug directly:
every dotted key handed to t() or fieldTip() must be declared in en.ts.
The remaining languages still lag, now by 89 keys rather than 65, and still fall
back to English; the test prints the per-locale figure.
* fix(uri): drop the vmess allowInsecure field the reference client ignores
The vmess skip-certificate field was added on internal consistency alone —
vless and trojan carry allowInsecure in their query strings, so vmess looked
like an oversight. Checked against the client that actually parses these links,
it is not one.
v2rayN's VmessQRCode model declares exactly: v, ps, add, port, id, aid, scy,
net, type, host, path, tls, sni, alpn, fp. There is no skip-certificate field
at all — neither allowInsecure nor insecure — so the field was dead weight in a
payload that v2rayN reads field by field. v2rayNG reads `insecure` instead, and
Xray-core has removed allowInsecure in favour of pinnedPeerCertSha256, so there
is no spelling that the reference clients agree on. Emitting one would have
produced a link that looks configured but is not.
The ws `host` fix stands: v2rayN's own documentation lists "ws host" as the
meaning of that field, so dropping it broke ws listeners that pin a Host.
The conformance test now pins the emitted payload to v2rayN's field set, so a
future field copied over from another protocol fails instead of being silently
ignored by every client.
Deliberately left alone, both confirmed against the official scheme and worth
deciding separately:
- hysteria2 links carry up/down. The Hysteria 2 URI specification is explicit
that the parameters "should never include client modes ... or bandwidth
values". Removing them changes behaviour for anyone relying on the link, so
it is flagged rather than removed. `insecure` is the official parameter;
allowInsecure is kept alongside it purely for clients that read that name.
- an SS listener with simple-obfs still exports a bare ss:// link with no
plugin entry, losing the obfuscation the client YAML does translate.
Also noted while checking: tuicURIs defaults congestion_control to bbr where
sing-box documents cubic as its default. Ours is set explicitly, which is
valid, but the comment claiming it is what "most TUIC clients" use is not
sourced.
* fix(uri): keep vmess allowInsecure after all
Reverts the revert. Dropping it was correct on the evidence and wrong on the
consequences: v2rayN ignores the field, v2rayNG spells it `insecure`, and
Xray-core has moved to pinnedPeerCertSha256 — but the clients that do read
allowInsecure are precisely the ones whose handshake fails without it, and an
ignored extra key costs the others nothing. Removing it would have traded a
working link for a tidy payload.
The conformance test keeps its guard, with allowInsecure recorded as an
explicit, justified exception rather than a silent inclusion: the field set is
still pinned to v2rayN's model, so any further field copied over from another
protocol has to argue its way in.
* fix(uri): restore the vmess ws Host in registry YAML, and stop dropping share links
Two leftovers from auditing the exporters end to end.
The registry's VMess compiler built ws-opts with the path only, so a listener
that pins a Host exported a client config with nothing to send as Host. The
vless YAML path in the same file already nests it under `headers` per the mihomo
wiki, so this just mirrors that — and the converter has always carried it, which
is how the difference went unnoticed.
The node URI share endpoint also had a tier that could not be reached. It walks
three tiers — m-ui, the registry, then the legacy builders — and the second one
gates the third on having neither a URI nor client YAML. But the registry
implements URI export for a different protocol set than the legacy builders: it
compiles vmess, snell, shadowquic, mieru, sudoku and trusttunnel to client YAML
without ever emitting a URI, so those listeners arrived at the third tier with a
YAML in hand and the condition already false, and reported no link at all even
though ClientURIsWithCredentials exports one. The third tier now runs whenever
the URI is still missing, and only fills in YAML that earlier tiers did not
already provide.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
2026-09-28 00:36:07 +08:00
kazeyukiro 4b21501deb feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
feat(ui): in-panel field help tooltips + i18n (wiki-aligned)
Expand protocol capability descriptions from Mihomo Meta concepts,
auto-attach Form.Item tooltips on listeners/users/settings, and fill
listeners/users/settings *Hint keys across all locale packs.
2026-09-27 02:56:05 +00:00
kazeyukiro 0ad79c46af fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
fix(share): derive SNI from formal cert when Access SNI is blank
TUIC already fell back to public host / connect host. Now also:
- PreferredSNIFromCert / ResolveClientSNI in certutil
- AnyTLS + TUIC share use cert SAN/CN when SNI unset
- anytls URI export uses same resolution so LE certs verify without skip
2026-09-22 00:40:00 +08:00
kazeyukiro 8bf6835ac2 fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
fix(share): AnyTLS skip-cert only when cert is self-signed or host mismatches
Do not hardcode insecure=1. Use DecideClientSkipCertVerify so Let's Encrypt
(and other formal certs) verify when SNI/public host matches the certificate.
2026-09-22 00:25:00 +08:00
kazeyukiro 14889ffc61 revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
revert(share): AnyTLS URI back to previous panel format
Keep PathEscape password, sni/fp/insecure/allowInsecure and idle-session
query params instead of the strict anytls-go official scheme.
2026-09-22 00:15:00 +08:00
kazeyukiro 446caa37d4 fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
fix(share): AnyTLS URI per official anytls-go scheme
anytls://[password@]host[:port]/?sni=...&insecure=1#name
- password as URI userinfo (percent-encoded)
- only official query keys: sni, insecure
- fragment for display name
- subscription ExportShare now emits URI (was YAML-only)
2026-09-21 23:55:00 +08:00
kazeyukiro 8c5e21adc4 fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
fix(protocol): use official users[].username for VLESS/VMess (not name)
MetaCubeX VlessUser is {username, uuid, flow}. The previous "name" key is
ignored by mihomo so inboundUser stayed empty and panel showed "-".
username is what connections metadata.inboundUser reports.
2026-09-21 23:45:00 +08:00
kazeyukiro 64417694a9 fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
fix(protocol): set users[].name for VLESS/VMess so Mihomo reports inboundUser
Without name, connections API leaves inboundUser empty and the panel cannot
attribute VLESS/VMess traffic to a ProxyUser (shows "-" on 节点1). AnyTLS/HY2
already key users by username and were fine. Also set name on Trojan users.
2026-09-21 23:25:00 +08:00
kazeyukiro f26fde0431 fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
fix(uri): TUIC/HY2 share links for NekoBox — host-aware skip-cert, defaults
- DecideClientSkipCertVerify using connect host (IP vs domain vs SAN)
- Default congestion_control=bbr, udp_relay_mode=native, alpn=h3
- Default SNI to domain host when unset; emit allow_insecure + allowInsecure
2026-09-19 23:15:00 +08:00
kazeyukiro adc35e3fe9 fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
fix(tuic): always apply smart skip-cert-verify for v4 and v5 export
- v4 (token) and v5 (uuid) share the same copyTUICOpts tail that calls
applyClientSkipCertVerify last — no longer omit the flag on either path
- Do not copy server-side skip-cert-verify; decide from cert PEM / certstore
- Share/URI path uses DecideClientSkipCertVerify instead of brittle string Contains("3m-ui")
2026-09-17 22:35:00 +08:00
kazeyukiro dbc3a25507 fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
fix(tuic-v5): merge panel UUIDs into inbound; keep sub auth consistent
Root cause vs user config/sub:
- Inbound had autofill UUID only (8398d58a…)
- Subscription exported panel admin UUID/password (6c96e7d2…)
- Client auth could never match
- asUsersMapUUID: union Config users + bound panel UUID→password
- Export tuic-v5: prefer panel credentials when bound (not autofill-only)
- TUIC client: skip-cert-verify for panel self-signed (config or certstore)
2026-09-17 00:25:00 +08:00
kazeyukiro 05bf9a270e chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
chore: align MetaCubeX audit — mihomo v1.19.31, TUIC modes, VMess schema
Per wiki.metacubex.one / KIMI audit:
- Bump bundled mihomo to v1.19.31 (amd64-v1 / arm64 digests verified)
- TUIC: panel keeps tuic-v4|tuic-v5; legacy "tuic" auto-detects token-only→v4 else v5
(YAML type always "tuic" per official inbound docs)
- UI: token field only on tuic-v4; tuic-v5 does not accept token in form
- VMess listener schema: drop allow-insecure (not in official VMess listener fields)
- Docs: version references → v1.19.31
2026-09-16 23:50:00 +08:00
Z User b221a2d731 fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
fix(tuic): unblock v5 listener creation + complete v4 URI params
Audit against official MetaCubeX wiki found 6 TUIC issues. Fixes:
CRITICAL-1 (node/validator.go:150): mapUserProtocols missing 'tuic-v5'
tuic-v5 listeners with wiki-standard 'users: {UUID: pwd}' map were
rejected with 'field users does not accept an object'. This ran
BEFORE validateProtocolSpecific (which would pass), so creating a
tuic-v5 listener via the panel failed at validation time.
Fix: add 'tuic-v5' to mapUserProtocols. tuic-v4 stays out (v4 uses
token, any users on v4 is rejected by schema).
HIGH-2 (node/uri_builders.go:299-313): v4 URI emitted zero query params
tuicURIs v4 path returned 'tuic://token@host:port#name' with NO
alpn / sni / congestion_control / udp_relay_mode / allow_insecure.
Fix: build shared params map ONCE (covers v4 token + v5 uuid paths).
HIGH-3 (protocol/compile_modules.go:141-148): v4 compiler dropped
extra tokens
'token: [T1, T2, T3]' was reduced to 'token: [T1]' at compile time,
silently dropping T2/T3 even though mihomo accepts multi-token v4.
Fix: new allStringTokens() helper preserves every non-empty token.
MEDIUM-4 (node/uri_builders.go): URI query param naming
tuicURIs used hyphen-case (congestion-controller, udp-relay-mode)
but mihomo URI parser expects snake_case. BuildShare already used
snake_case — only the user_sub URI path was wrong.
Fix: map hyphen-case listener keys to snake_case URI keys
(congestion_control, udp_relay_mode, max_udp_relay_packet_size,
bbr_profile, allow_insecure).
MEDIUM-6 (protocol/share_modules.go:633-634): BuildShare skipCert
too aggressive
any cert + empty SNI forced skipCert=true, breaking formal
(Let's Encrypt) cert listeners when operator forgot SNI.
Fix: default SNI to server host; only skipCert for panel self-signed
PEMs (O=3m-ui marker) or explicit skip-cert-verify: true.
LOW-7 (mihomo/config/listener_schema_registry.go:178-182): tuic-v4
schema whitelisted 'users'
v4 is token-only per wiki; symmetric to tuic-v5 omitting 'token'.
Fix: remove 'users' from tuic-v4 Fields.
Tests: 4 new regression tests in node/validator_test.go covering
tuic-v5 map users (accept), tuic-v4 token array (accept),
tuic-v4-with-users (reject), tuic-v5-with-token (reject).
Full suite 23/23 packages PASS.
2026-09-16 14:49:45 +00:00
kazeyukiro 87409bf5e4 fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
fix(tuic): hard-split v4 token vs v5 users paths (stop killing v5)
- tuic-v5: never prefer leftover token; use Config users or panel UUID/password
- tuic-v4: token from Config only
- Decode/Share: do not replace v5 users with token credentials
- credentialsFromListenerConfig split by protocol
2026-09-16 16:35:00 +08:00
kazeyukiro cd65594294 fix(tuic): prefer Config token when classifying generic tuic as v4 for share 2026-09-16 16:10:00 +08:00
kazeyukiro 2cd92bd1e7 fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
fix(tuic): subscription token from Config; formal certs verify
- BuildShare/Decode: TUIC v4 token only from listener Config, never panel password
- listenerToProxies: refuse tuic-v4 without Config token
- skip-cert-verify only for panel self-signed (O=3m-ui / ShouldSkipCertVerify),
not for operator formal certificates
2026-09-16 16:05:00 +08:00
kazeyukiro 236e58cfff fix: import strings in compile_modules for TUIC token helper 2026-09-16 14:05:00 +08:00
kazeyukiro 3f0adba4cd fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
fix: TUIC v4 empty token + stop shadowing multi-user protocol creds
- Seed/autofill/compile: empty token [] is missing; mint a real v4 token
- Subscription: only prefer Config auth for listener-native protocols
(tuic*, ss, snell, sudoku); anytls/shadowquic keep panel user creds
2026-09-16 14:00:00 +08:00
kazeyukiro 5f61445354 chore: gofmt autofill and share_modules 2026-09-16 12:20:00 +08:00
kazeyukiro 133adbcdfb feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
feat(share): client YAML for all panel listener protocols
Generate Mihomo proxies YAML per MetaCubeX docs (wiki.metacubex.one):
- VMess, Snell, Sudoku, AnyTLS, ShadowQUIC, Mieru, TrustTunnel
- Hysteria2 now includes ClientYAML alongside URI
- Decode extracts snell psk / sudoku key for export
YAML-first for protocols without a stable share URI scheme.
2026-09-16 12:15:00 +08:00
kazeyukiro ded3ab3ed7 feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
feat(share): TUIC v4/v5 client URI and Mihomo proxy YAML
Align with MetaCubeX inbound (type tuic) and proxies/tuic docs:
- v4: tuic://TOKEN@host with token-only client YAML
- v5: tuic://uuid:password@host with uuid+password YAML
- Decode token[] and UUID map users for export
Refs: wiki.metacubex.one config inbound tuic-v4/v5 and proxies/tuic
2026-09-16 12:10:00 +08:00
kazeyukiro a6d32adeae fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
fix(tuic): align v4/v5 seed+autofill+compile with MetaCubeX wiki
Inbound docs use type: tuic for both; v4 auth is token[], v5 is users UUID→password.
- Seed quick-create with alpn/h3 + congestion bbr; v4 token vs v5 users
- Autofill no longer defaults every TUIC to v5 users map
- Compile pure v5 strips token (outbound wiki: token is V4-only)
Also add GitHub issue templates (bug/feature) and docs contact links.
2026-09-16 12:05:00 +08:00
風雪 d5629d0247 security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
security: remove unverified script downloads
2026-09-14 12:31:46 +08:00
freephilx 0e5af8a096 feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
feat(listeners)!: select REALITY targets with server-side checks
BREAKING CHANGE: REALITY create and update requests require an explicit reality-config.dest; implicit Microsoft and SNI-derived destinations are no longer supplied.
2026-09-08 22:47:32 +08:00
Z User 4a81280b95 fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
fix(cert): unify self-signed generation, multi-SAN, export skip-cert-verify
- Single certutil.GenerateSelfSigned (Org=3m-ui, 10y, DNS+IP SANs)
- autofill + config ensure both call certutil; drop duplicate generators
- protocol decode uses ShouldSkipCertVerify for all SkipCert fields
- Generator/autofill fold PublicHost/AccessSNI into SAN hints
- Document listener-certs must be backed up with the DB
2026-09-05 15:43:23 +00:00
Z User 1d439d4136 fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
fix(config): strip more client-only keys from listeners; normalize TUIC token
- Do not passthrough grpc-user-agent/ping-interval/client TUIC hints into
inbound YAML (official listeners only document grpc-service-name / ws-path).
- Export TUIC v4 token as a string for clients (listener uses []string).
2026-09-04 14:19:44 +00:00
Z User fbca88a534 fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
fix(config): align listener/client fields with MetaCubeX wiki (round 2)
Complete field-level audit against https://wiki.metacubex.one/ for 8 protocols
(shadowsocks/vmess/vless/trojan/hysteria2/tuic-v4/tuic-v5/sudoku).
HIGH (8):
- VLESS listener: strip client-only `encryption` field (server uses `decryption`)
- mux-option: drop undocumented `brutal-opts` from all 9 listener schemas (keep `brutal` map)
- vmess/vless/trojan listener: drop fabricated top-level `ws-headers` (client uses ws-opts.headers)
- 3m-ui share: nest ws-opts.Host under headers map per proxies-transport
- hysteria2 listener: drop `handshake-timeout`/`max-idle-time` (client/TUIC only)
- m-ui hysteria2Listener: drop 6 quic-go fields (cwnd/udp-mtu/initial-stream-receive-window/...)
- tuic-v5 listener: drop `token` (v4-only per wiki)
- sudoku listener: drop httpmask.path_root/tls/host/multiplex (wiki uses dash path-root only)
MEDIUM (11):
- VMess listener: strip top-level `alterId` (per-user form in users[] only)
- VMess/VLESS/Trojan listener: drop undocumented top-level `udp`
- SS kcptun: emit plugin:kcptun + plugin-opts from kcp-tun block (m-ui + converter paths)
- m-ui hysteria2ClientProxy: add ports/hop-interval/obfs-min/obfs-max/name-cert-verify/fingerprint/handshake-timeout
- m-ui hysteria2RealmConfig: drop client-side `proxy` (listener-only)
- m-ui classicClientProxy (VMess/Trojan): add global-padding/authenticated-length/fingerprint/name-cert-verify/tlsmirror-opts/smux
- m-ui vlessClientProxy: add fingerprint/name-cert-verify/smux
- m-ui websocketClient: add headers map (was only path)
- converter TUIC v4+v5: add ip + name-cert-verify copy
- converter mekya-opts: add polling-interval-initial + h2-pool-size
- converter hysteria2: add ports/hop-interval/obfs-min-packet-size/obfs-max-packet-size
- converter applyClientWrappers: add tlsmirror-opts branch
LOW:
- Update TestGenerateListenersUsesModelTLS to assert wiki-correct VLESS (no top-level tls)
- SS client: add udp-over-tcp/udp-over-tcp-version/ip-version/smux (both paths)
- grpc-opts: add grpc-user-agent/ping-interval/max-connections/min-streams/max-streams (both paths)
- ech-opts: pass-through in copyClientTLS (both paths)
- Schema whitelist: add mekya-config.polling-interval-initial/h2-pool-size + grpc-opts 5 fields
(client-only metadata accepted by panel; mihomo ignores unknown listener fields)
Verification:
- go build ./... → PASS
- go vet ./... → clean
- go test ./... → 16/16 packages PASS
Refs: wiki-audit-1 (33 findings) + wiki-audit-2 (4 R-M follow-ups) in worklog.
2026-09-03 15:14:39 +00:00
Z User d393114a3b fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
fix(config): align listener/client fields with MetaCubeX wiki
- Client SS type is "ss" (not listener name shadowsocks)
- Drop TUIC/Hy2 server-only keys from client export
- Strip simple-obfs enable from SS plugin-opts
- Do not emit top-level tls on VLESS/VMess/Trojan listeners
- Strip client-only keys (sni, skip-cert-verify, *-opts) from listener YAML
2026-09-02 15:12:32 +00:00
Z User 4854f18753 feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
feat: split TUIC into TUIC v4 and TUIC v5 in protocol selector
The protocol dropdown now shows 'TUIC v4' and 'TUIC v5' as separate
options instead of a single 'tuic'. This makes it clear which
authentication mode the listener uses:
- TUIC v4: token-based auth (token: ["..."])
- TUIC v5: UUID+password auth (users: {UUID: PASSWORD})
Implementation:
- Frontend: PROTOCOLS array splits 'tuic' into 'tuic-v4' and 'tuic-v5'
Select shows them as separate options. Token field only shows for v4.
- Backend: tuic-v4 and tuic-v5 are accepted as valid protocol names
in schema registry, compiler registry, autofill, and mihomo listener
protocol list. Generator normalizes tuic-v4/tuic-v5 to 'tuic' for
Mihomo (which only has one 'tuic' type).
- Compiler: TUICCompiler gets a 'kind' field. tuic-v4 compiler ensures
token is present and removes users. tuic-v5 compiler uses users map.
- Existing 'tuic' protocol still works (backward compatible).
- autofill: tuic-v4 and tuic-v5 both call autofillTUICUsers (which
correctly handles token-vs-users logic).
Verification: go build/vet/test PASS, npm run build PASS.
2026-08-31 15:04:36 +00:00
Z User 4190024234 fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
2026-08-31 14:28:21 +00:00