fix: enable XTLS vision flow for VLESS+XHTTP+vlessenc in UI and share links (#5157) (#5185)

* fix: enable XTLS vision flow for VLESS+XHTTP+vlessenc in UI and share links (#5157)

* fix: enable xtls-rprx-vision flow for VLESS XHTTP with vlessenc encryption (#5157)

The flow selector was hidden and the vless:// link omitted flow= because:
1. The backend gate (inboundCanEnableTlsFlow) only accepted tcp+tls/reality.
2. The PR #5185 frontend check used `encryption === 'vlessenc'`, which never
   matches — the stored value is a generated ML-KEM dotted string, not the CLI
   subcommand name.

Fix: extend inboundCanEnableTlsFlow to also return true for XHTTP when a
non-none vlessenc encryption/decryption value is present. Update all three
call-sites (inbound.go TlsFlowCapable field, client_crud.go clientWithInboundFlow,
inbound_clients.go copy-flow path) and the sub/service.go link generator.
Scope is XHTTP-only: TCP without tls/reality is intentionally excluded.

Add inbound_protocol_test.go covering the new and existing gate combinations,
extend client_flow_isolation_test.go with xhttp+vlessenc cases, and add
frontend tests for canEnableTlsFlow with real ML-KEM key values.

---------

Co-authored-by: rqzbeh <rqzbeh@users.noreply.github.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
Rouzbeh†
2026-06-11 12:04:02 +02:00
committed by GitHub
parent eee652c4a5
commit c7a76e9626
12 changed files with 239 additions and 31 deletions
+19
View File
@@ -445,6 +445,20 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
return buildVmessLink(obj)
}
// vlessEncryptionEnabled reports whether the VLESS inbound settings enable
// VLESS-level encryption (vlessenc / ML-KEM). When on, the encryption/decryption
// fields hold a generated dotted string (e.g. "mlkem768x25519plus.native.0rtt.<key>");
// "none" or empty means off. The value is never the literal "vlessenc" — that is
// the `xray vlessenc` CLI subcommand name, not a stored value.
func vlessEncryptionEnabled(settings map[string]any) bool {
for _, key := range []string{"encryption", "decryption"} {
if v, ok := settings[key].(string); ok && v != "" && v != "none" {
return true
}
}
return false
}
func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
if inbound.Protocol != model.VLESS {
return ""
@@ -484,6 +498,11 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
}
default:
params["security"] = "none"
// VLESS encryption (vlessenc / ML-KEM) carries XTLS Vision over XHTTP
// without transport TLS.
if streamNetwork == "xhttp" && len(clients[clientIndex].Flow) > 0 && vlessEncryptionEnabled(settings) {
params["flow"] = clients[clientIndex].Flow
}
}
externalProxies, _ := stream["externalProxy"].([]any)