main
23
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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. |
||
|
|
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> |
||
|
|
7e3e0ab7d2 |
fix(node): fall through share exporters when ClientYAML is empty
mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. fix(node): fall through share exporters when ClientYAML is empty mui.BuildShares / protocol.ExportShares could succeed with URIs but empty ClientYAML; connection-check then aborted with client YAML unavailable and never reached converter.ExportClientYAML. Prefer any non-empty YAML across tiers, then legacy converter. |
||
|
|
d0b53dcc9e |
feat(listeners): verify client connectivity and expose diagnostics
Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. feat(listeners): verify client connectivity and expose diagnostics Inspect listener readiness and test exported client endpoints with isolated Mihomo clients. Preserve public addresses, ports and protocol settings, report diagnostic stages, and repair generated access SNI fields. |
||
|
|
da5a0f67d1 |
fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback
Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. |
||
|
|
0754e6a9d6 |
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
fix(format-mismatch): TUIC v5 URI UUID key, cluster JWT auth doc, visual port type, access token entropy
Hunt for format-mismatch bugs (like the backup zip vs raw db bug) found
4 issues across the codebase. All fixed.
== Critical 1: TUIC v5 share URI uses Username instead of UUID ==
uri_credentials.go ClientURIsWithCredentials built the users map for
TUIC keyed by credential.Username, but the server-side compiler
(asUsersMapUUID) keys by UUID. Official Mihomo TUIC v5 spec requires
users: {UUID: PASSWORD}. The URI tuic://alice:pass@host failed auth
because the server looked up users['alice'] not users['UUID'].
Fix: split 'tuic' from the 'anytls/hysteria2/mieru' case; use UUID
as key (fallback to Username when UUID empty), matching asUsersMapUUID.
== Critical 2: Cluster APIToken sent as Bearer JWT to remote panel ==
cluster/service.go sends 'Authorization: Bearer <api_token>' but the
remote panel's RequireAuth middleware expects a signed JWT (HS256).
Opaque tokens fail jwt.Parse → 401 on all auth-protected endpoints.
Fix: documented that api_token must be a valid JWT obtained from
POST /api/v1/auth/login on the remote panel. Added comments at both
Bearer header sites. (Full opaque-token auth path requires a separate
middleware on the remote — tracked as future feature.)
== Medium 3: VisualConfig ProxyEntry.Port string → quoted YAML ==
visual.go ProxyEntry.Port was string type, causing yaml.Marshal to
emit port: '8080' (quoted). Mihomo expects port: 8080 (integer).
Changed to interface{} so JSON/YAML can carry int or string.
== Low 4: AccessToken entropy inconsistency ==
router/access_token.go used 24 bytes (192-bit) for token generation,
but node/api.go CreateClientAccess used only 16 bytes (128-bit).
Aligned both to 24 bytes (192-bit, NIST SP 800-63A minimum).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- gofmt : clean
|
||
|
|
c817650597 |
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
fix(config): P2+P3+P4 audit fixes — client config, schema, m-ui bridge
Comprehensive audit against official Mihomo docs (Meta-Docs-main).
This commit addresses all remaining P2 (Medium) and P3/P4 (Low)
findings, completing the full audit fix campaign.
== P2 Medium (client config gaps) ==
P2-1: VLESS native share URI missing security=tls for non-Reality TLS
share_modules.go BuildShare only set security=reality; plain TLS
had no security param (clients default to none=plaintext).
Added else-if: security=tls when Node.TLS && no Reality.
P2-2: m-ui VLESS URI emitted non-standard security=shadow-tls/res-tls/jls
mui/protocol/vless.go leaked wrapper types into URI. Fixed: map all
TLS wrappers (shadow-tls/res-tls/jls) to security=tls; reality stays
reality; none omitted. Wrapper detail preserved in client YAML.
P2-3: m-ui decodeHy2 dropped alpn — client YAML ALPN always empty
mui/bridge.go Added shared decodeALPN helper (handles []interface{},
[]string, bare string). Wired into decodeHy2/decodeVMess/decodeTrojan/
decodeVLESS. Added ALPN field to domain VLESSSpec/VMessSpec/TrojanSpec.
P2-4: m-ui classicClientProxy (VMess/Trojan) missing ALPN field
mui/protocol/classic_stream.go Added ALPN yaml field + populated
from node.VMess.ALPN / node.Trojan.ALPN.
P2-5: m-ui vlessClientProxy missing ALPN field
mui/protocol/vless.go Added ALPN yaml field + populated from
node.VLESS.ALPN.
P2-6: sing-box TUIC v4 token not handled
converter/singbox.go TUIC case only read uuid+password. Added token
reading ([]interface{}/[]string/string) → sing-box native token field;
falls back to uuid+password for v5.
P2-7: sing-box VLESS missing encryption mapping
converter/singbox.go Added p[encryption] → ob[encryption] when non-empty.
== P3 Low (schema completeness + client gaps) ==
P3-1: ResTLS restls-script not copied to client restls-opts
converter/client.go resTLSClientOptions now copies restls-script.
P3-2: JLS only exports first user — documented as design limitation
(per-credential JLS user matching requires explicit binding table;
single-JLS-user deployments are correct). Added doc-comment.
P3-3: WS ws-headers not in schema — Host header never exportable
Added ws-headers to top-level Fields for vmess/vless/trojan, aligning
with copyTransport which already reads src[ws-headers].
P3-4: VMess tlsmirror-config steps missing name/headers/wait-time field
Added 3 missing steps.* sub-fields to match tls.md 12-field list.
P3-5: VMess users not in NestedFields — documented as design choice
(map-form vs array-form shape difference would break map-form
validation). Added explanatory comment.
P3-6: Hysteria2 URI up/down inconsistent between rows vs map users
uri_builders.go First branch now reads up/down from listener config,
matching second branch.
P3-7: SS mux-option over-broad — removed 7 proxy-side sing-mux fields
(enable, protocol, max-connections, min-streams, max-streams,
statistic, only-tcp) from ALL 7 protocols with mux-option. Kept
padding + brutal.*/brutal-opts.* (listener-documented fields).
P3-8: m-ui bridge only supports 5/12 protocols — documented as follow-up
(requires implementing 7 protocol decoders; large feature).
P3-9: Three-tier fallback inconsistency — documented as follow-up
(design issue requiring output-contract unification).
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- gofmt : clean (on all modified files)
|
||
|
|
58f69e3f9d |
fix(backend): XFF-aware rate limiting, host trust, error sanitization
- M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. fix(backend): XFF-aware rate limiting, host trust, error sanitization - M-3: clientIdentifier now trusts X-Forwarded-For (right-most valid IP) only when the direct RemoteAddr is loopback/private/link-local (typical same-host reverse proxy). Public exposure still rejects spoofed XFF, restoring per-client granularity behind nginx/caddy. - LOW-10: node ExportNodeURI fell back to c.Request.Host (client controlled) even when publicURL was configured. Now publicURL wins over client headers when set. - LOW-11: Replace direct err.Error() in 500/502 responses with generic messages; real errors logged server-side via log.Printf. Prevents leakage of DB paths, SQL fragments, remote server responses. 4xx business messages (invalid id, not found) preserved. |
||
|
|
6bbea65368 | fix(ci): remove unused imports in node/api.go | ||
|
|
3fc0503960 | feat(node): prefer m-ui BuildShares for URI/client YAML export | ||
|
|
708ffd0471 | feat(node): ExportNodeURI via typed protocol registry BuildShare | ||
|
|
e0a0afc577 | feat(share): ExportNodeURI (uri/qr/client_yaml) + ExportClientYAML | ||
|
|
dc62de00a3 | feat(share): node share (uri/qr/client_yaml) + Access Profile in URIs | ||
|
|
b36df8f029 | fix: audit — access-token export uses PublicHost; robust IPv6 URI host | ||
|
|
6b3a09a09c |
update
update update update update update update update update update update update |
||
|
|
96153d4531 |
Add files via upload
Add files via upload Add files via upload Add files via upload Add files via upload Add files via upload Add files via upload Add files via upload Add files via upload Add files via upload Add files via upload Add files via upload |
||
|
|
79fc469e11 | feat(node): Access Profile public_host overrides URI export host | ||
|
|
7e80804323 | fix: expose node-only routes without duplicate CRUD | ||
|
|
664885ce2a | fix: expose node-only routes without duplicate CRUD | ||
|
|
4d05dcf56f | fix(node): resolve share host via public_url / loopback fallback | ||
|
|
6c0a7ac649 | fix(node): URI export empty hint; ensure non-nil uris | ||
|
|
6ac6b31a39 | update | ||
|
|
5b4cb63a73 |
fix: align listener generation with mihomo official spec
- Port now supports mihomo ports format (single/range/list) - Add explicit RoutingMark field support - Auto-propagate UDP flag for vmess/vless/trojan/anytls/trusttunnel - Update validator to accept port ranges - Sync visual config and client converter port types - Rename module from dzx941 to kazeyukiro - Fix missing ExpiresAt in JWTClaims - Fix nodeDTO Port type to string - Fix frontend tsconfig and add missing dependencies |