Skip to content

RFC-0002 egress track: transparently capture + credential Claude Code's untokened control-plane traffic to api.anthropic.com #124

Description

@cspinetta

Context

RFC-0002 M0 (PR #105) routes the credentialed host to the proxy cooperatively — the base-URL redirect plus an /etc/hosts alias (api.anthropic.com → gateway). That captures only the SDK's inference calls (/v1/messages, which carry the per-sandbox token). Claude Code also makes control-plane calls to its own api.anthropic.com endpoints that ignore ANTHROPIC_BASE_URL and carry no token, so M0's proxy neither sees nor credentials them; on KVM they reach a gateway port with no listener and are refused.

Captured on an arm64/KVM Lima host by temporarily binding the proxy on :443 and logging every request at the top of the pipeline (host / method / path / token-presence):

Path Method token?
/v1/messages POST yes (SDK)
/api/event_logging/v2/batch POST no
/api/claude_code/settings GET no
/api/claude_code/policy_limits GET no
/api/eval/sdk-… POST no
/api/claude_code_penguin_mode GET no
/ HEAD no

This is benign in M0 — the calls are best-effort (telemetry, settings, policy limits, feature flags, reachability); the agent completes correctly with all of them failing. It is recorded as a third M0 deviation in docs/rfc/0002-… ("Auxiliary control-plane traffic is not captured").

What the egress track needs to do

The egress-profiles milestone resolves this uniformly (not endpoint-by-endpoint):

  1. Transparent capture. Network-layer DNAT of guest ports 80/443 to the per-sandbox proxy listener; the proxy recovers the destination from the TLS SNI. This catches any non-cooperative traffic to a credentialed host, not just a named endpoint. Build the redirect as a general rule primitive in Rules/translate_outbound (not a hardcoded dst==gateway && port==443 special case) so the same seam serves monitored/allowlist.
  2. Attribution for untokened flows. These control-plane calls carry no token, so they cannot pass M0's token gate. The per-sandbox network rule (§A: deny every gateway hop except this sandbox's own listener) makes the arrival socket the sandbox identity, so untokened DNAT'd flows are safely attributable — the prerequisite for crediting them without a cross-sandbox key-theft hole. Injecting on untokened traffic is unsafe until this rule exists.
  3. Injection policy decision. Once these calls reach the proxy, decide whether to credential them unconditionally (they are anthropic-native and likely need the key), only when they carry the guest placeholder, or by path — a policy this milestone settles against the observed traffic. The EgressEvent.path field (added in PR RFC-0002 M0: host-side credential-injection proxy (Claude API-key) #105) audits them for that decision.

References

RFC-0002 Rollout ("Egress profiles" milestone) and the M0 deviation note; src/proxy/server.rs (token gate before inject), src/network/nat.rs (translate_outbound), PR #105.

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

    area:networkSLIRP, NAT, 9p transport, proxy/egressenhancementNew feature or requestsecurityPublic hardening work; undisclosed vulns via private reporting

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions