The core lowercases an outbound's protocol id before it resolves the handler,
so an outbound spelled "Loopback" still is the loopback outbound. Both
readers that keep a loopback outbound's inboundTag in step with the inbound
it names compared the id exactly, so such an outbound was skipped: renaming
or deleting that inbound left settings.inboundTag pointing at a tag that no
longer exists, and traffic returning through the loopback outbound arrives
under a tag no routing rule can match (infra/conf/loopback.go:15 carries the
tag, proxy/loopback/loopback.go:43 uses it as the inbound identity).
The probe lane's "nothing to test here" gate had the same exact comparison,
so a "Freedom"/"Blackhole" outbound reported the vaguer "No testable
endpoint" where the canonical spelling reports "Outbound has no testable
endpoint" — the two spellings took different paths to the same rejection.
Both readers now compare case-insensitively; the outbound package reuses its
existing equalsAnyFold helper rather than adding a second one. The service
reads the config template an operator edits, so a case variant is reachable
there; server.go's GetDefaultLogOutboundTags scans the embedded config.json
instead, whose protocols are canonical by construction, so it is left as is
and no test can tell a case-insensitive read there from an exact one.
* fix(outbound): read the probe protocol id and transport name like the core
The probe lane gate and the endpoint extractor behind it compared both
strings exactly, so a template the core is running was probed as something
else. With mode=tcp an outbound spelled "WireGuard" stayed in the dial-only
TCP lane, where extractOutboundEndpoints matched no case and the caller got
"No testable endpoint" for an outbound that is passing traffic.
The core lowercases a protocol id (infra/conf/loader.go) and a transport
name (TransportProtocol.Build) before it resolves either, and resolves both
"kcp" and "mkcp" to mKCP, so both readers now normalise the same way.
The panel no longer reaches the lane gate itself — the browser now sends
http for these outbounds — but the endpoint documents "tcp" for fast
dial-only probes with UDP-transport outbounds still probed over HTTP, and
that promise has to hold for direct API callers too.
* fix(outbound): read the batch probe protocol id like the core
Review of #6526 found that folding "WireGuard"/"AmneziaWG" into the UDP lane
newly routed those spellings onto two readers in buildBatchTestConfig that
still compared the id exactly. A case-variant WireGuard outbound therefore
reached the temp probe instance without noKernelTun -- which on Linux creates
a kernel TUN device alongside the live panel's own -- and a case-variant
AmneziaWG entry was appended raw, rejecting the whole temp config and
degrading the batch to serial per-item retries.
Both readers now fold the id the way the core does (infra/conf/loader.go
lowercases it before the protocol is resolved).