-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Merge train round 3 B8: Remote Link enrollment and relay, sidecar probe, Windows Desktop proxy report (#6064 #6068 #6067 #6065) #6071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
907e99a
159085e
e1c8943
a5e2f72
390b69f
eab0ac1
37d0608
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # B8 — Remote Link relay and enrollment, sidecar probe, Windows Desktop proxy report | ||
|
|
||
| Base: `dev` `29cef45a86` (after B7 #6070; open issues reached 40). Branch `codex/train3-b8`. | ||
|
|
||
| | PR | Author | Change | Review | | ||
| |---|---|---|---| | ||
| | #6064 | luvs01 | Enrollment cancellation and commit share one terminal outcome: a tunnel exit aborts the connection transaction, an exit after commit keeps the key, and an exit before commit drains local rollback before revoking | APPROVE; security BLOCKER no; negative control fails the three advertised cases | | ||
| | #6068 | luvs01 | The Child relay authenticates and streams on one socket with a one-use, direction-tagged proof; pending rotation keys are accepted; no plain-fetch fallback (supersedes closed #6044) | LAND; security BLOCKER no; Windows segfault hold passed on the exact head (fork run) | | ||
| | #6067 | luvs01 | The web-search probe is released when the error body settles, not on status alone (follow-up to #6047) | APPROVE; new tests fail on dev | | ||
| | #6065 | kaladinhonor | `ocx doctor` and Desktop status report a Windows system proxy that bypasses Desktop first-party | APPROVE; prior hold points fixed | | ||
|
|
||
| Order: #6064 before #6068, resolving their shared tail of `structure/remote-link.md`; new layout entries go on | ||
| existing lines. Security reviews for #6064 and #6068 are recorded in scratch. | ||
|
|
||
| ## Build and evidence | ||
|
|
||
| Carried: `159085ed6c` (#6064), `e1c8943670` (#6068; the `remote-link.md` tail keeps both sections), | ||
| `a5e2f7272a` (#6067), `390b69f2c8` (#6065); `eab0ac1422` pairs the two new layout entries (layout.json 1994 lines). | ||
|
|
||
| Local: typecheck, structure and privacy exit 0; the six carried test files plus `doctor.test.ts`: 185 pass here, and | ||
| the 19 `doctor` failures are this worktree's protected-home guard. In a `/tmp` worktree at `eab0ac1422`, | ||
| `doctor`, `link-join-route` and `claude-desktop-system-proxy` pass 136/136. | ||
|
|
||
| Aside: all four PR pages captured; no open CHANGES_REQUESTED review on any of them. Security reviews for #6064 and | ||
| #6068 (BLOCKER no) are kept in scratch. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -184,6 +184,18 @@ working. OpenCodex only writes two variables into the `env` block of `~/.claude/ | |
|
|
||
| Claude Desktop first-party routes its Code tab and subagents through OpenCodex. The standalone Claude Code CLI has a separate first-party switch. Both clients read the same `~/.claude/settings.json` proxy and CA settings: if only one switch is on, the other client still transits the local proxy, where TLS terminates, but its Messages requests relay to Anthropic unchanged. Other Anthropic paths relay unchanged and unrelated hosts remain blind tunnels. | ||
|
|
||
| :::note[Windows system proxy (Clash, v2rayN, corporate proxies)] | ||
| When a Windows system proxy is on, Claude Desktop hands it to the Code tab as `HTTPS_PROXY`, and | ||
| that value takes precedence over the OpenCodex proxy in `~/.claude/settings.json`. The Code tab | ||
| then goes around OpenCodex and routed models fail there, while the standalone CLI keeps working. | ||
| Add `api.anthropic.com` to your proxy client's system-proxy bypass list (in Clash Verge, | ||
| `system_proxy_bypass`), then fully quit and reopen Claude Desktop. `ocx doctor` reports this | ||
| under "Claude Desktop first-party vs Windows system proxy". It cannot evaluate a PAC script or | ||
| automatic proxy detection (WPAD, "Automatically detect settings"), so it reports those as unknown; | ||
| with either, make the script return `DIRECT` for `api.anthropic.com` or turn detection off. If the | ||
| first-party settings are stale, it asks you to run `ocx ensure` instead of reporting `ok`. | ||
| ::: | ||
|
|
||
|
Comment on lines
+187
to
+198
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
fd -p 'guides/claude-code.md' docs-site/src/content/docs
fd -p 'guides/claude-code.md' docs-site/src/content/docs --exec rg -n -i 'api.anthropic.com|system_proxy_bypass|WPAD' {}Repository: lidge-jun/opencodex Length of output: 1674 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- changed diff ---'
git diff --unified=25 29cef45a869136f93477694c4198a33ab2ad84b0 -- docs-site/src/content/docs/guides/claude-code.md
printf '%s\n' '--- translated page headings and proxy/doctor terms ---'
for f in \
docs-site/src/content/docs/ja/guides/claude-code.md \
docs-site/src/content/docs/ko/guides/claude-code.md \
docs-site/src/content/docs/ru/guides/claude-code.md \
docs-site/src/content/docs/zh-cn/guides/claude-code.md
do
printf '\n### %s\n' "$f"
rg -n -i 'claude desktop|first-party|doctor|proxy|system.proxy|system_proxy|windows|配置|代理|プロキシ|прокси' "$f" | head -80 || true
doneRepository: lidge-jun/opencodex Length of output: 29395 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- changed diff ---'
git diff --unified=25 29cef45a869136f93477694c4198a33ab2ad84b0 -- docs-site/src/content/docs/guides/claude-code.md
printf '%s\n' '--- translated page headings and proxy/doctor terms ---'
for f in \
docs-site/src/content/docs/ja/guides/claude-code.md \
docs-site/src/content/docs/ko/guides/claude-code.md \
docs-site/src/content/docs/ru/guides/claude-code.md \
docs-site/src/content/docs/zh-cn/guides/claude-code.md
do
printf '\n### %s\n' "$f"
rg -n -i 'claude desktop|first-party|doctor|proxy|system_proxy|windows|配置|代理|プロキシ|прокси' "$f" | head -80 || true
doneRepository: lidge-jun/opencodex Length of output: 29395 Update the translated Claude Code guides. The new Windows system proxy guidance appears only in the English guide. Add an equivalent note to:
If translation is pending, mark the section accordingly. Otherwise, users of these locales will not see the documented 🤖 Prompt for AI AgentsSource: Path instructions |
||
| Subagents on routed (non-Claude) models do not use Claude Code's server-side message threads, because only Anthropic stores that state. OpenCodex declines a threaded request for such a model, and Claude Code resends that turn, and the turns after it, with the full conversation. | ||
|
|
||
| Mode is persisted as `claudeCode.desktopMode`. Installs that already applied either mode retain it, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -88,3 +88,9 @@ ocx link revoke --link-id <id> [--json] | |
|
|
||
| - [Remote Hub Deployment](/guides/remote-hub/) | ||
| - [Remote Workspace](/guides/remote-workspace/) | ||
|
|
||
| ### Relay authentication compatibility | ||
|
|
||
| Update both the Home and Child when upgrading to connection-bound relay authentication. Before sending a relayed request's link credential or body, the Child verifies the Home on the same connection it will use for that request. A closed connection is not silently replaced. A Home without this protocol causes a retryable authentication error; upgrade the Home and Child, and re-link when the stored link is no longer recognized. There is no insecure fallback switch. An unexpired pending API-key rotation remains valid until it expires or the rotation is committed or aborted. | ||
|
|
||
| Removing the final Home link drains pending authenticated relay requests before releasing its listener. Stopping the process still cancels active connections. This does not change which caller credentials are stripped or which routes can be relayed, and it does not replace SSH's host-key verification. | ||
|
Comment on lines
+91
to
+96
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Move "Relay authentication compatibility" out of the "Related guides" section. The new Fix: make it a 📝 Proposed fix+## Relay authentication compatibility
+
+Update both the Home and Child when upgrading ... (moved text)
+
## Related guides
- [Remote Hub Deployment](/guides/remote-hub/)
- [Remote Workspace](/guides/remote-workspace/)
-
-### Relay authentication compatibility
-...🤖 Prompt for AI AgentsSources: Coding guidelines, Path instructions |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| import type { OcxConfig } from "../types"; | ||
| import { | ||
| readWindowsProxyBypassRegistry, | ||
| readWindowsSystemProxy, | ||
| windowsProxyOverrideBypasses, | ||
| type WindowsProxyBypassValues, | ||
| type WindowsSystemProxyResult, | ||
| } from "../lib/windows-system-proxy"; | ||
| import { inspectDesktopFirstParty, observeClaudeDesktopMode } from "./desktop-first-party"; | ||
| import { desktopFirstPartyDesired } from "./first-party-settings"; | ||
|
|
||
| /** | ||
| * Whether the Windows system proxy silently bypasses Desktop first-party mode. | ||
| * | ||
| * When Claude Desktop starts the Code tab's Claude Code process, it resolves the operating | ||
| * system proxy for the API host and, when that yields an HTTP proxy, passes it to the process | ||
| * as `HTTPS_PROXY`/`HTTP_PROXY`. That inherited value takes precedence over the `env` block | ||
| * OpenCodex writes into the user's `~/.claude/settings.json` (only Claude Code managed settings | ||
| * override it), so Code-tab traffic goes to the system proxy and never reaches the intercept. | ||
| * Routed subagent models then fail, and nothing else reports why. A system proxy such as Clash or | ||
| * v2rayN, with no bypass for the API host, is the common case. | ||
| * | ||
| * Observe-only: this reads the registry and never changes it. A PAC script and WPAD automatic | ||
| * detection decide per request, so they are reported as undecidable rather than guessed, and a | ||
| * failed read is reported as unreadable rather than as an absent value. Like the other doctor | ||
| * proxy surfaces it never prints the proxy value, which can carry credentials. | ||
| */ | ||
|
|
||
| export const FIRST_PARTY_API_HOST = "api.anthropic.com"; | ||
|
|
||
| export type DesktopSystemProxyVerdict = "no-proxy" | "bypassed" | "conflict" | "pac" | "auto-detect" | "unreadable"; | ||
|
|
||
| export type DesktopSystemProxyAssessment = | ||
| | { kind: "not-applicable" } | ||
| | { | ||
| kind: DesktopSystemProxyVerdict; | ||
| /** settings.json carries an owned first-party env that no longer matches the port or token. */ | ||
| settingsStale: boolean; | ||
| /** WPAD detection is on (or unknown) next to a static proxy; a WPAD script would win. */ | ||
| autoDetectAlsoOn: boolean; | ||
| }; | ||
|
|
||
| export type DesktopFirstPartySettingsState = "applied" | "stale" | "off"; | ||
|
|
||
| export interface DesktopSystemProxyInput { | ||
| platform: NodeJS.Platform; | ||
| firstParty: DesktopFirstPartySettingsState; | ||
| systemProxy: WindowsSystemProxyResult; | ||
| /** `null` when the Internet Settings key could not be read. */ | ||
| bypass: WindowsProxyBypassValues | null; | ||
| } | ||
|
|
||
| function classify(systemProxy: WindowsSystemProxyResult, bypass: WindowsProxyBypassValues | null): DesktopSystemProxyVerdict { | ||
| if (bypass === null || systemProxy.kind === "unreadable") return "unreadable"; | ||
| if (bypass.autoConfigUrl) return "pac"; | ||
| // Desktop skips SOCKS entries, and an http=-only value does not cover an https:// API host. | ||
| const covering = systemProxy.kind === "proxy" && Boolean(systemProxy.httpsUrl); | ||
| if (!covering) return bypass.autoDetect === false ? "no-proxy" : "auto-detect"; | ||
| // A failed WPAD lookup falls back to the static proxy, so a static conflict stands either way. | ||
| return windowsProxyOverrideBypasses(bypass.proxyOverride, FIRST_PARTY_API_HOST) ? "bypassed" : "conflict"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When automatic proxy detection is enabled or unreadable alongside a static proxy whose bypass list contains AGENTS.md reference: docs-site/AGENTS.md:L7-L10 Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| export function assessDesktopSystemProxy(input: DesktopSystemProxyInput): DesktopSystemProxyAssessment { | ||
| if (input.platform !== "win32" || input.firstParty === "off") return { kind: "not-applicable" }; | ||
| const kind = classify(input.systemProxy, input.bypass); | ||
| return { | ||
| kind, | ||
| settingsStale: input.firstParty === "stale", | ||
| autoDetectAlsoOn: kind === "bypassed" && input.bypass?.autoDetect !== false, | ||
| }; | ||
| } | ||
|
|
||
| const STALE_LINES = [ | ||
| " The OpenCodex first-party env in ~/.claude/settings.json is stale (proxy port or token changed);", | ||
| " run `ocx ensure`, then re-run `ocx doctor`.", | ||
| ]; | ||
|
|
||
| function verdictLines(kind: DesktopSystemProxyVerdict): string[] { | ||
| switch (kind) { | ||
| case "no-proxy": | ||
| return [" ok No Windows system proxy covers the Claude API; the Code tab uses the OpenCodex proxy from settings.json."]; | ||
| case "bypassed": | ||
| return [` ok ${FIRST_PARTY_API_HOST} is on the Windows proxy bypass list; the Code tab uses the OpenCodex proxy from settings.json.`]; | ||
| case "pac": | ||
| return [ | ||
| " -- Windows uses a proxy auto-config (PAC) script, so OpenCodex cannot tell whether Claude Desktop", | ||
| ` sends ${FIRST_PARTY_API_HOST} through a proxy. If routed models fail in the Code tab, make the script return DIRECT for it.`, | ||
| ]; | ||
| case "auto-detect": | ||
| return [ | ||
| " -- Windows automatic proxy detection (WPAD) is on or could not be read, so OpenCodex cannot tell whether", | ||
| ` Claude Desktop sends ${FIRST_PARTY_API_HOST} through a proxy. If routed models fail in the Code tab, turn off`, | ||
| " \"Automatically detect settings\" or make the network's WPAD script return DIRECT for it.", | ||
| ]; | ||
| case "unreadable": | ||
| return [" -- Could not read the Windows proxy settings."]; | ||
| case "conflict": | ||
| return [ | ||
| ` !! The Windows system proxy applies to ${FIRST_PARTY_API_HOST}. Claude Desktop passes it to the Code tab`, | ||
| " as HTTPS_PROXY, which takes precedence over the OpenCodex proxy in ~/.claude/settings.json,", | ||
| " so first-party routing is bypassed and routed models fail there.", | ||
| ` Fix: add ${FIRST_PARTY_API_HOST} to your proxy client's system-proxy bypass list (Clash Verge: system_proxy_bypass),`, | ||
| " then fully quit and reopen Claude Desktop.", | ||
| ]; | ||
| } | ||
| } | ||
|
|
||
| export function formatDesktopSystemProxyLines(assessment: DesktopSystemProxyAssessment): string[] { | ||
| if (assessment.kind === "not-applicable") return []; | ||
| const clear = assessment.kind === "no-proxy" || assessment.kind === "bypassed"; | ||
| // A stale env never earns an `ok`: the Code tab would reach a proxy URL that no longer matches. | ||
| if (clear && assessment.settingsStale) return [` -- ${STALE_LINES[0]!.trimStart()}`, STALE_LINES[1]!]; | ||
| const lines = verdictLines(assessment.kind); | ||
| if (assessment.autoDetectAlsoOn) { | ||
| lines.push(" Automatic proxy detection (WPAD) is also on; a WPAD script on this network would decide instead."); | ||
| } | ||
| if (assessment.settingsStale) lines.push(...STALE_LINES); | ||
| return lines; | ||
| } | ||
|
|
||
| export interface DesktopSystemProxyDeps { | ||
| platform?: NodeJS.Platform; | ||
| firstPartyState?: () => DesktopFirstPartySettingsState; | ||
| readSystemProxy?: () => WindowsSystemProxyResult; | ||
| readBypass?: () => WindowsProxyBypassValues | null; | ||
| } | ||
|
|
||
| function liveFirstPartyState( | ||
| config: Pick<OcxConfig, "claudeCode" | "clientIntegrations" | "port" | "runtimeRole">, | ||
| ): DesktopFirstPartySettingsState { | ||
| try { | ||
| const settings = inspectDesktopFirstParty(config).settings.kind; | ||
| if (settings !== "applied" && settings !== "stale") return "off"; | ||
| return desktopFirstPartyDesired(config, observeClaudeDesktopMode(config)) ? settings : "off"; | ||
| } catch { // no-excuse-ok: catch -- unreadable Claude settings are no first-party evidence. | ||
| return "off"; | ||
| } | ||
| } | ||
|
|
||
| /** Reads the live state. Registry reads run only on Windows with a Desktop first-party env. */ | ||
| export function collectDesktopSystemProxy( | ||
| config: Pick<OcxConfig, "claudeCode" | "clientIntegrations" | "port" | "runtimeRole">, | ||
| deps: DesktopSystemProxyDeps = {}, | ||
| ): DesktopSystemProxyAssessment { | ||
| const platform = deps.platform ?? process.platform; | ||
| if (platform !== "win32") return { kind: "not-applicable" }; | ||
| const firstParty = (deps.firstPartyState ?? (() => liveFirstPartyState(config)))(); | ||
| if (firstParty === "off") return { kind: "not-applicable" }; | ||
| return assessDesktopSystemProxy({ | ||
| platform, | ||
| firstParty, | ||
| systemProxy: (deps.readSystemProxy ?? readWindowsSystemProxy)(), | ||
| bypass: (deps.readBypass ?? readWindowsProxyBypassRegistry)(), | ||
| }); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Keep the PR reference out of heading position.
Line 25 triggers the reported MD018 warning because it starts with
#6068. Prefix the reference withPRor join it to the preceding sentence. Do not add a space after#, which would turn it into a heading.🧰 Tools
🪛 markdownlint-cli2 (0.23.2)
[warning] 25-25: No space after hash on atx style heading
(MD018, no-missing-space-atx)
🤖 Prompt for AI Agents
Source: Linters/SAST tools