Skip to content

[Bug] Model discovery blocked by destination policy under Clash/Mihomo IPv6 fake-ip (fdfe:dcba:9876::/48) #3462

Description

@tanrenjun

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

  1. Start Clash Verge / Mihomo with enhanced-mode: fake-ip and fake-ip-range6: fdfe:dcba:9876::1/64 on a dual-stack IPv6 network.
  2. Start OpenCodex: ocx start --port 10100 with HTTP proxy configured to http://127.0.0.1:7897.
  3. Configure an external provider such as opencode-go with baseUrl: https://opencode.ai/zen/v1.
  4. Open the OpenCodex dashboard (http://localhost:10100/#models) or run ocx sync-cache.
  5. 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

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingproxyHTTP proxy, routing, reverse-proxy / management auth

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions