Skip to content

Route first-party Claude Desktop traffic through agentgateway without third-party mode #182

Open

Description

@ilackarms

Problem

Today, agentdesktop sends Claude Desktop traffic to agentgateway in third-party inference mode. To do this, it writes inferenceProvider: "gateway" and inferenceGatewayBaseUrl to the managed settings (reconcile.rs#L158).

In third-party mode, Claude Desktop does not sign in to an Anthropic account. The claude.ai chat history, connectors, and organization-managed settings (Claude Team and Enterprise) do not apply in this mode. If an organization uses these features, it cannot put agentgateway in the path, because its users lose the features.

Goal

  • Claude Desktop stays signed in to claude.ai (first-party mode).
  • Claude Desktop sends its Anthropic traffic through agentgateway. agentgateway applies its policies: authentication, guardrails and DLP, audit, and telemetry.
  • agentdesktop does not replace or change the claude.ai login of the user.

Proposed approach

  1. Local intercepting proxy. The daemon listens on loopback for HTTPS CONNECT requests. The current LLM proxy is a reverse proxy with fixed routes, and it refuses CONNECT (llm_proxy.rs#L794). The new proxy can reuse its credential cache and its tunnel relay.
  2. Proxy selection with a PAC file. The daemon serves a PAC file. The PAC file sends only the allowlisted Anthropic hosts to the proxy, with no DIRECT fallback. All other hosts connect DIRECT. The host allowlist is a configuration value. There are two ways to apply the PAC file. As the system's automatic proxy configuration, it also steers browsers. With Claude's managed key egressProxyPacUrl, it steers only Claude Desktop.
  3. TLS termination for allowlisted hosts only. The proxy issues leaf certificates from a device-local CA, or from a CA that the admin supplies. Name constraints limit the CA to the allowlisted domains. For other hosts, the proxy tunnels the traffic without change. It must not refuse them, because the Code tab agent sends all of its traffic to one proxy.
  4. Upstream to an agentgateway passthrough route. The proxy does not change the Cookie and Authorization headers. It adds a separate identity header, which agentgateway validates and removes before it sends the request upstream. The header carries the gateway OIDC token or the controller-issued credential. Which one to use, and which daemon runs the listener, is still open: the current loopback proxy runs in user mode only.
  5. First-party mode for the Claude Desktop provider. In this mode, the provider writes no third-party inference keys. It also removes the keys that it wrote before.
  6. Claude Code. In the Desktop Code tab, Claude Code already uses the app's proxy and trusts the System keychain. The standalone CLI can use HTTPS_PROXY, and NODE_EXTRA_CA_CERTS if it is necessary. This is not tested yet.

Alternative: agentgateway is the forward proxy and does the TLS interception. This option needs less code on the device. But agentgateway must support forward-proxy mode and TLS interception, and the identity must go in the Proxy-Authorization header. Both options need a CA that the device trusts. This proposal keeps the interception on the device, so agentgateway stays a normal reverse proxy.

The full design proposal, with the work items: #185

Open questions

A spike on macOS answered some of these questions. Results: spike results comment

  • Does Claude Desktop use the OS proxy and PAC settings and the OS trust store? Does it pin certificates? This is the go/no-go question. Yes, for the app, the Code tab agent, the helpers and the updater. It does not pin certificates. The Cowork VM is not checked.
  • Which hosts and protocols (HTTP/2, SSE, WebSocket) does Claude Desktop use in first-party mode for chat, uploads, connectors, and telemetry? Answered in the spike results for Cowork sessions and the Code tab. Chat mode is not confirmed.
  • With interception on, do these features continue to work: sign-in with SSO, org-managed settings, connectors, and history? Partly answered. Org-managed settings and connectors work. Sign-in and history sync are not tested.
  • Can agentgateway pass claude.ai traffic through and keep streaming intact? Can it apply guardrails to request bodies that do not use the Messages API format?
  • Who owns the CA, and how does the trust install work? The install is for system mode only. On macOS, the System keychain needs admin authentication or an MDM profile. On Windows, the target store is LocalMachine\Root. Partly answered. Full trust in the System keychain works for the Claude processes that were tested. Who owns the CA is still open.
  • If the gateway is not available, does the proxy fail open or fail closed? Partly answered. When the local proxy is down, Claude Desktop fails closed with a PAC file that has no DIRECT fallback. It reconnects when the proxy comes back. A gateway outage is not tested. The proposal is to fail closed by default.
  • How does the proxy work with an enterprise proxy that already inspects TLS? Topics: chaining to an upstream proxy, and double interception.

Out of scope for now

This issue does not include browser-based clients, for example chatgpt.com or Microsoft 365 Copilot on the web. Later, the configurable host allowlist can extend the same mechanism to these clients.

Activity

  1. ilackarms commented on Oct 20, 2026

    @ilackarms
    CollaboratorAuthor

    Spike results: first-party Claude Desktop through a local intercepting proxy

    Setup. Claude Desktop 2.42304.2 ran on macOS 26.6. It used an organization account on claude.ai. The app had no third-party inference keys. mitmproxy 12.2.3 was the local proxy, and a PAC file on loopback pointed the traffic to it. The mitmproxy CA had full trust in the System keychain.

    Verdict: go, with conditions. Claude Desktop follows the OS proxy settings and trusts the OS trust store. It does not pin certificates. The conditions are under "Still open" below.

    Design. The design proposal that uses these results is in #185.

    Answers to the open questions

    OS proxy, PAC and trust store; certificate pinning (go/no-go): Yes.
    The app, its renderer, the Code tab agent, the helper processes and the updater all used the PAC and accepted the local CA. In about 3,100 requests, the proxy recorded 0 client TLS failures. The Code tab agent did not need NODE_EXTRA_CA_CERTS. It gets its proxy from the app when the session starts.
    Control test: I removed the trust for the CA and restarted the app. Then every connection from the app to claude.ai failed. The app log showed ERR_CERT_AUTHORITY_INVALID. This result shows that the app validates certificates.

    Hosts and protocols:

    • claude.ai: HTTP/2 only. It carries org settings, session events, uploads, connector calls and telemetry. Event streams use SSE.
    • api.anthropic.com: traffic from the Code tab agent (HTTP/1.1, Messages API with SSE, one WebSocket) and the update check.
    • bridge.claudeusercontent.com: WebSockets for Remote Control and the browser extension bridge.
    • assets-proxy.anthropic.com, s-cdn.anthropic.com, a.claude.ai: static app files only. assets-proxy had more than half of all requests.
    • downloads.claude.ai: large binaries. The proxy tunneled this host and did not decrypt it.
    • The Claude app processes did not use HTTP/3 or QUIC. In one check while the PAC was on, they had no direct connections to the internet. I did not check the traffic of the Cowork VM.
    • In this app version, each message that I sent from the Desktop window started a Cowork session. The app sends the message as a JSON events array to POST claude.ai/v1/code/sessions/{id}/events. The reply comes back on an SSE events/stream. I did not see a classic chat completion endpoint. I did not confirm if Chat mode uses the same endpoints.

    Sign-in, org-managed settings, connectors, history: Partly tested.
    Org settings loaded. A claude.ai connector tool call worked (POST …/mcp/servers/{id}/tools/call with {tool_name, arguments}). Cowork sessions worked with and without file attachments. Remote Control and the update check worked. I did not test sign-in (including SSO) or history sync between devices.

    agentgateway passthrough and guardrails on bodies that do not use the Messages API: Not tested here.
    Inputs for that test:

    • Request bodies are JSON events arrays (sessions), multipart {file} (uploads), {tool_name, arguments} (connectors), and the Messages API (Code tab).
    • The app needs HTTP/2, unbuffered SSE and long-lived WebSockets.

    CA ownership and trust install.
    Full root trust in the System keychain works for the Claude processes that I tested. These are the app, the Code tab agent, the helpers and the updater. I did not test the Cowork VM or local MCP servers. This needs admin rights or an MDM profile. Each process reads trust when it starts. After an admin installs, rotates or removes the CA, Claude must restart. A web browser also kept the old trust after I removed it, probably because of a cache.

    Fail open or fail closed: Fails closed when the local proxy is down.
    The PAC had no DIRECT fallback. While the proxy was down, messages failed, and no traffic went around the proxy. When the proxy came back, the app reconnected after about 6 seconds, without a restart. Code tab sessions keep the proxy address from their start, so they also fail until the proxy returns. I did not test a gateway outage, because the test had no gateway.

    Enterprise proxies that already inspect TLS: Not tested.

    Still open

    1. Sign-in (including SSO) and history sync while the proxy intercepts the traffic.
    2. Where the model traffic for Cowork sessions goes. I did not see it on any host. Two causes are possible: the model runs on the server, or the VM traffic goes around the host proxy.
    3. Two times, claude.ai sent a TLS bad record mac alert to mitmproxy immediately after a 1.1 MB upload. Five uploads of the same size to a different host had no errors. I will test this again with the real daemon.
    4. The egressProxyPacUrl managed key, which can steer only Claude Desktop. A system-wide PAC also sends browser traffic for claude.ai to the proxy.
    5. Chat mode. Every message in this test started a Cowork session.
    6. A narrow allowlist. Candidate: claude.ai, api.anthropic.com, and optionally bridge.claudeusercontent.com. Static hosts and downloads.claude.ai can stay direct.

    Implications for the implementation

    • The listener must support HTTP/2, HTTP/1.1, unbuffered SSE and WebSocket upgrades.
    • Tunnel CONNECT requests for hosts that are not on the allowlist. Do not refuse them. The Code tab agent sends all of its traffic to the one proxy it gets.
    • Show proxy outages in the daemon status. Users see only "message failed".
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

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions