Repository navigation
feat(claude): local intercept proxy — CA, CONNECT proxy, TLS listener, server wiring - #5301
devin-ai-integration[bot] wants to merge 4 commits into
Conversation
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Repository: lidge-jun/opencodex/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Deterministic PR hygiene checks passed. |
d3112e9 to
f6d64ac
Compare
리뷰 · 우선순위 64 / 80이 PR은 Claude Desktop을 first-party로 두는 스택의 1/3입니다. Claude Code가 라인 - PR 본문 vs 메인테이너의 판단이 필요한 지점 허브에서 intercept를 기본 ON으로 둘지, PR 2가 settings를 쓰기 전까지는 기본 OFF로 둘지. PR 본문·릴리즈 노트와 코드를 어느쪽에 맞출지. IPv4-mapped(및 유사) 루프백 CONNECT를 지금 막을지, 루프백만 바인드이니 후속으로 둘지. #5302/#5303과 같이 스택 순서로만 머지할지. 너의 추천 방향과 보안 기본값(루프백 전용, CA 0600, OS trust 미설치, 실패 시 degrade, settings 소유권 검사)은 좋습니다. 머지 전에 (1) 기본 ON/OFF를 본문·타입 주석·실제 기본값 중 하나로 맞추고, (2) IPv4-mapped 루프백 CONNECT 거부를 좁게 넣는 쪽을 권합니다. 경로 허용 목록은 한곳으로 모으거나 공유 상수를 쓰면 드리프트를 줄입니다. CI tip 통과 후 #5302 → #5303 순으로 이어 주세요. 로컬 풀 스위트는 이 환경에서 돌리지 마세요. 이 댓글은 grok-bot이 작성했습니다 |
⏳ DRAFT
What to do
Review readiness checklist
0/4 boxes ticked. This PR stays in draft until every box above is ticked. |
|
Addressed in 7d154b0:
|
…ver wiring Claude Code honours HTTPS_PROXY/NODE_EXTRA_CA_CERTS from its settings env, so a loopback CONNECT proxy plus a locally-signed TLS listener for api.anthropic.com lets the router see Claude Code's Messages traffic without any ANTHROPIC_BASE_URL rewrite and without touching the Desktop app's own (first-party) configuration. - src/claude/intercept/local-ca.ts: ECDSA P-256 CA + leaf issuance with a hand-rolled DER encoder (node:crypto only); CA persisted under <OPENCODEX_HOME>/claude-intercept with a 0600 key, never installed in an OS store. - src/claude/intercept/connect-proxy.ts: CONNECT-only loopback proxy; splices api.anthropic.com:443 onto the TLS listener, relays other targets blind, refuses plain HTTP, loopback targets and oversized heads. - src/claude/intercept/listener.ts: TLS listener; POST /v1/messages and /v1/messages/count_tokens are rewritten onto a loopback origin and dispatched to the route table under the new claude-intercept ingress (loopback policy); every other path is relayed verbatim to the configured Anthropic upstream. - src/claude/intercept/settings.ts: ownership-aware apply/inspect/remove of the two env keys in Claude Code settings.json (anchored on the CA path). - src/claude/intercept/runtime.ts + server wiring: enabled by default on a hub, proxy port = public port + 100 unless claudeCode.intercept.port is set; bind failure degrades to a warning; stop joins both sockets. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…t wiring out of index.ts startServer(0) has no stable port to derive the CONNECT proxy from, so the intercept pair now stays off unless claudeCode.intercept.port is explicit; this also keeps in-process test fixtures at their expected listener count. The wiring moves into src/server/index/claude-intercept-lifecycle.ts and the inbound-body-limit warning into startup-warnings.ts so src/server/index.ts stays under its file-size cap. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…gets, share the intercepted-path predicate isLoopbackTarget now checks 127/8, ::1, 0.0.0.0 and :: through a BlockList (which also matches IPv4-mapped IPv6), *.localhost, and numeric resolver shorthands like 127.1. serve-options.ts reuses isClaudeInterceptedPath from listener.ts so the two route lists cannot drift apart. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
7d154b0 to
117d257
Compare
추가 리뷰 · 우선순위 42 / 80이전 리뷰(우선순위 64) 이후 tip이 라인 - 메인테이너의 판단이 필요한 지점 숫자 단축 거절을 “루프백만”으로 더 좁힐지, 지금처럼 넓게 fail-closed로 둘지. 허브 기본 ON + 빈 리스너를 이 PR에서 그대로 둘지, 아니면 PR 2와 같이 올릴지. draft 체크리스트·CI tip 통과를 머지 게이트로 둘지. 너의 추천 이전 세 지적은 반영된 것으로 보고, 남은 우선순위는 낮춥니다. tip CI가 초록이면 #5301 → #5302 → #5303 순으로 이어 가도 됩니다. 숫자 단축 과거절은 후속 미세 조정으로 충분합니다. 로컬 풀 스위트는 이 환경에서 돌리지 마세요. 이 댓글은 grok-bot이 작성했습니다 |
|
Agreed on all three. The numeric-shorthand rule is intentionally fail-closed: any non- |
|
Superseded by #5319, merged to The chain was carried as a GitHub native stack, which stops accepting ordinary merges once its bottom lands by squash. Its tip already contained both ancestors and branched cleanly from Closing as superseded rather than stale. |
Summary
Adds the runtime half of Claude Desktop first-party mode (1/3 in the stack). Nothing writes to
~/.claude/settings.jsonin this PR, so no client is pointed at the proxy yet — but the proxy pair itself is on by default on a hub (claudeCode.intercept.enabled !== false, off forruntimeRole: "client"and whenclaudeCode.enabledis false). PR 2 relies on that default so a fresh install resolves to first-party without extra config; setclaudeCode.intercept.enabled: falseto opt out. On its own this PR therefore creates the local CA and binds a loopback CONNECT listener onport + 100that nothing connects to.src/claude/intercept/local-ca.ts— ECDSA P-256 local CA, persisted under<configDir>/claude-intercept/{ca.pem,ca.key}(atomic write,0600, regenerated if corrupt). Issues per-host leaf certs. Never installed into the OS trust store; Claude Code trusts it process-locally viaNODE_EXTRA_CA_CERTS.src/claude/intercept/connect-proxy.ts— raw HTTPCONNECTproxy.CONNECT api.anthropic.com:443is handed to the TLS listener; any other host is blind-tunneled; non-CONNECT, malformed, oversized-header and local-target requests are rejected. Local targets cover 127/8,::1,0.0.0.0,::, their IPv4-mapped IPv6 forms (::ffff:127.0.0.1,::ffff:7f00:1),*.localhost, and numeric resolver shorthands (127.1,0x7f000001,2130706433).src/claude/intercept/listener.ts— TLS termination with the issued leaf.POST /v1/messagesandPOST /v1/messages/count_tokensare rewritten to the loopback OpenCodex origin and served by the existing Claude Messages routing (path/query/headers/body/streaming preserved); every other path is relayed byte-for-byte tohttps://api.anthropic.com, so subscription OAuth traffic keeps working.isClaudeInterceptedPathis the single source of that list;serve-options.tsreuses it for the ingress allow-check.src/claude/intercept/settings.ts— ownership-awaresettings.json.envwriter forHTTPS_PROXY/NODE_EXTRA_CA_CERTS: preserves unrelated keys, refuses to overwrite foreign (corporate) proxy/CA values, replaces stale OpenCodex-owned loopback values, never rewrites an unreadable file. (Consumed by PR 2.)src/claude/intercept/runtime.ts+src/server/index/claude-intercept-lifecycle.ts— starts/stops the proxy with the server lifecycle, fire-and-forget sostartServerstays synchronous; bind failure is logged as a warning, not fatal.startServer(0)(ephemeral) starts no proxy unlessclaudeCode.intercept.portis explicit, since the derived port would be unstable.claudeCode.intercept.{enabled,port}typed and schema-validated.Verification
bun run typecheckbun test tests/claude-integration/claude-intercept-*.test.ts tests/server/claude-intercept-integration.test.ts tests/lab/core-lab-boundary.test.ts tests/configbun run privacy:scan,bun run structure:check,bun scripts/file-size-ratchet.tsChecklist
0600, proxy binds loopback only, local CONNECT targets (incl. IPv4-mapped) are refused, no OS trust-store changes.Link to Devin session: https://app.devin.ai/sessions/4ec9c3528d424ced9e4a4da906168ad1
Open in Devin Desktop: https://app.devin.ai/desktop/session/4ec9c3528d424ced9e4a4da906168ad1?variant=devin
Requested by: @lidge-jun
Review readiness checklist
This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:
All CI tests are green on my local testing.
I pushed my PR to the latest dev commit.
I resolved all correct Codex and CodeRabbit findings.
My PR is ready for review.