Skip to content

Redirect to another inference server fails open when the original server is unreachable #1595

Closed

Description

@huang195

Summary

Redirecting a session to another inference server (inference-router pins, agentop server) silently depends on the original server being reachable. The number-one reason to redirect away from a server — it is down or being decommissioned — is exactly the case where the redirect cannot apply.

What happens

The redirect happens inside the TLS bridge: the client CONNECTs to its configured server, bridgeServe forges a cert, decrypts the request, and only then does the router move it (core/plugins/inferencerouter/plugin.go:362, applied at core/listener/forwardproxy/server.go:576). But before forging, bridgeServe probes the original authority with a HEAD reachability/cert check (core/listener/forwardproxy/server.go:1055-1075):

  • Probe fails (original server down, DNS gone, cert expired) → the bridge falls back to a plain opaque tunnel (TunnelOriginUnverified).
  • An opaque tunnel never reaches the pipeline → the router never runs → the proxy dials the dead original → the client gets 502 dial_failed.

So the fallback that was sound when bridging was observability-only ("agent's own e2e TLS still reaches origin") is fail-open on the wrong axis now that routing rides on the bridge.

Secondary: per-CONNECT probe cost

The probe is unconditional per CONNECT, and clients that don't pool CONNECT tunnels (Node/undici — Claude Code) open one per request. On a redirected session that means one HEAD round-trip to the server you redirected away from, per model request (budget 10s, upstreamVerifyTimeout), unrecorded in the session timeline. Cheap mitigation: memoize the probe per host for a few seconds — the engine's skip/memory structures are a natural place to hang it.

Directions worth considering

  • Fail closed for hosts that carry router pins: if a CONNECT target has sessions pinned away from it, a failed probe should not silently disable routing. The listener can't know a session's pin at CONNECT time, but the plugin store (~/.cortex/plugin-state.json) is readable.
  • Probe the pin target instead of the CONNECT authority when one exists for the connecting process.
  • At minimum: surface the fallback. Today TunnelOriginUnverified renders as an opaque tunnel row and the 502 says dial_failed with no hint that a redirect was configured and never applied.

Found while investigating the orphan CONNECT rows fixed in #1594.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions