Client or integration
OpenCodex dashboard
Area
Proxy and routing
Summary
When running OpenCodex behind Clash Verge / Mihomo (Clash.Meta) with Fake-IP mode enabled on a dual-stack IPv6 network, domain resolution for remote providers (e.g. opencode.ai) returns Mihomo's standard IPv6 Fake-IP address range (fake-ip-range6: fdfe:dcba:9876::1/64). Outbound management requests like model discovery are blocked before transmission because provider URL hostname opencode.ai resolves to private-network address (fdfe:dcba:9876::7e). This causes the web dashboard to permanently display the warning badge Discovery failed and degrades to static presets.
Normal chat/completions through the proxy succeed because the inference path relies on upstream proxy tunneling (CONNECT), whereas the management discovery path strictly enforces local DNS pre-flight safety checks via resolvePublicAddresses(), causing an observable inconsistency between inference and discovery.
Reproduction
- Start Clash Verge / Mihomo with enhanced-mode: fake-ip and fake-ip-range6: fdfe:dcba:9876::1/64 on a dual-stack IPv6 network.
- Start OpenCodex: ocx start --port 10100 with HTTP proxy configured to http://127.0.0.1:7897.
- Configure an external provider such as opencode-go with baseUrl: https://opencode.ai/zen/v1.
- Open the OpenCodex dashboard (http://localhost:10100/#models) or run ocx sync-cache.
- Observe that discovery fails with: Provider model discovery for "opencode-go" was blocked by destination policy: provider URL hostname opencode.ai resolves to private-network address (fdfe:dcba:9876::7e).
Version
2.42.0
Operating system
macOS 26.6.2
Provider and model
opencode.ai / opencode-go
Logs or error output
[opencodex] Provider model discovery for "opencode-go" was blocked by destination policy: provider URL hostname opencode.ai resolves to private-network address (fdfe:dcba:9876::7e)
Additional context & Root Cause Analysis
In src/lib/destination-policy.ts, isBenchmarkDnsAnswer() validates the IPv4 benchmark space 198.18.0.0/15 (introduced in #1748), but omits the de-facto community standard Mihomo IPv6 Fake-IP ULA prefix fdfe:dcba:9876::/48 (RFC 4193).
Why fdfe:dcba:9876::/48?
In Mihomo (formerly Clash.Meta), fake-ip-range6: fdfe:dcba:9876::1/64 is the official community default and is adopted by virtually all modern Clash Verge Rev profiles and subconverters. Because it counts down (FE -> DC -> BA -> 98 -> 76), it is easily distinguishable and poses near-zero risk of overlapping with real enterprise ULA deployments.
Security Consideration:
isBenchmarkDnsAnswer() is only evaluated when allowBenchmarkAddresses is enabled (i.e. proxyConfigured && !noProxyMatches(parsed)). Therefore, admitting this IPv6 prefix will NOT weaken SSRF protections for direct connections or local network targets; it only applies when the request rides the configured outbound proxy.
Proposed Fix:
In src/lib/destination-policy.ts:
function isBenchmarkDnsAnswer(address: string, assessment: DestinationAssessment | null): boolean {
if (assessment?.kind === "private" && assessment.detail === "benchmark address") return true;
if (isIP(address) !== 6) return false;
+ // Support Clash / Mihomo standard fake-ip-range6 (fdfe:dcba:9876::/48)
+ if (address.toLowerCase().startsWith("fdfe:dcba:9876:")) return true;
if (assessment?.kind !== "private" || assessment.detail !== "non-global address") return false;
const hextets = ipv6Hextets(normalizeHostname(address));
Checks
Client or integration
OpenCodex dashboard
Area
Proxy and routing
Summary
When running OpenCodex behind Clash Verge / Mihomo (Clash.Meta) with Fake-IP mode enabled on a dual-stack IPv6 network, domain resolution for remote providers (e.g. opencode.ai) returns Mihomo's standard IPv6 Fake-IP address range (fake-ip-range6: fdfe:dcba:9876::1/64). Outbound management requests like model discovery are blocked before transmission because provider URL hostname opencode.ai resolves to private-network address (fdfe:dcba:9876::7e). This causes the web dashboard to permanently display the warning badge Discovery failed and degrades to static presets.
Normal chat/completions through the proxy succeed because the inference path relies on upstream proxy tunneling (CONNECT), whereas the management discovery path strictly enforces local DNS pre-flight safety checks via resolvePublicAddresses(), causing an observable inconsistency between inference and discovery.
Reproduction
Version
2.42.0
Operating system
macOS 26.6.2
Provider and model
opencode.ai / opencode-go
Logs or error output
Additional context & Root Cause Analysis
In
src/lib/destination-policy.ts,isBenchmarkDnsAnswer()validates the IPv4 benchmark space198.18.0.0/15(introduced in #1748), but omits the de-facto community standard Mihomo IPv6 Fake-IP ULA prefixfdfe:dcba:9876::/48(RFC 4193).Why
fdfe:dcba:9876::/48?In Mihomo (formerly Clash.Meta),
fake-ip-range6: fdfe:dcba:9876::1/64is the official community default and is adopted by virtually all modern Clash Verge Rev profiles and subconverters. Because it counts down (FE -> DC -> BA -> 98 -> 76), it is easily distinguishable and poses near-zero risk of overlapping with real enterprise ULA deployments.Security Consideration:
isBenchmarkDnsAnswer()is only evaluated whenallowBenchmarkAddressesis enabled (i.e.proxyConfigured && !noProxyMatches(parsed)). Therefore, admitting this IPv6 prefix will NOT weaken SSRF protections for direct connections or local network targets; it only applies when the request rides the configured outbound proxy.Proposed Fix:
In
src/lib/destination-policy.ts:function isBenchmarkDnsAnswer(address: string, assessment: DestinationAssessment | null): boolean { if (assessment?.kind === "private" && assessment.detail === "benchmark address") return true; if (isIP(address) !== 6) return false; + // Support Clash / Mihomo standard fake-ip-range6 (fdfe:dcba:9876::/48) + if (address.toLowerCase().startsWith("fdfe:dcba:9876:")) return true; if (assessment?.kind !== "private" || assessment.detail !== "non-global address") return false; const hextets = ipv6Hextets(normalizeHostname(address));Checks