Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs-site/src/content/docs/guides/remote-hub.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ data-key rotation, revocation, and disconnect.

### What a connected client shows

`ocx connect` and `ocx connect status` check catalog readiness against the first valid local
Codex runtime in selection order. Failed preferred candidates can fall back, but lower-priority
alternatives are not probed after a valid runtime is selected. This check leaves the saved runtime
selection unchanged. General `ocx status` still discovers alternatives for runtime diagnostics.

A client stores no provider credentials and no catalog of its own, so its local config and
credential store are empty by design — and reading them as the truth produces a confident, wrong
answer about what the hub can serve. On a connected client `ocx status` therefore leads with
Expand Down
24 changes: 19 additions & 5 deletions src/cli/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ export interface ClientCommandDeps extends RuntimeApiDeps {
export interface ClientCatalogProbeDeps extends CatalogCompatibilityDeps {
/** Injected in tests; defaults to reading the materialized client catalog off disk. */
readCatalogBody?: () => string | null;
/**
* A Codex command the caller already resolved, handed over so readiness skips resolving it
* again. General `ocx status` resolves the full runtime for its diagnostics block; the resolver
* memo is keyed by discovery scope and holds one entry, so a priority-only readiness resolve
* and the full one miss each other and re-probe the same command with `--version` — up to eight
* seconds apiece. Passing the command across adds no cache state, and it cannot disagree with
* what status prints because it is the selection status is printing.
*/
selectedCodexCommand?: string;
}

export const CONNECT_USAGE = `Usage:
Expand Down Expand Up @@ -103,11 +112,15 @@ function readInstalledCatalogBody(): string | null {
* `codexSupportedReasoningEfforts()` with no deps reaches `resolveAndPersistCodexRuntime`, which
* writes codex-runtime.json. `ocx status` deliberately resolves without persisting, and a
* read-only diagnostics command should not start writing runtime selection state because a
* readiness check was added to it. Handing the already-resolved command in as the only candidate
* skips that path and reuses the resolve cache `ocx status` has usually already filled.
* readiness check was added to it. Stop at the first valid runtime, then hand only that command
* to the catalog probe: readiness does not consume alternative-runtime diagnostics. The resolver
* keeps this priority-only cache separate from the full discovery used by `ocx status`.
*
* A caller that has already resolved passes its selection in through `selectedCodexCommand` rather
* than paying for a second `--version` probe of the command it just resolved.
*/
function observeLocalCodexEffortLadder(): ReadonlySet<string> | null {
const command = resolveCodexRuntime().runtime.command;
function observeLocalCodexEffortLadder(selected?: string): ReadonlySet<string> | null {
const command = selected ?? resolveCodexRuntime({ discoverAlternatives: false }).runtime.command;
return codexSupportedReasoningEfforts({ commandCandidates: () => [command] });
}

Expand All @@ -119,7 +132,8 @@ function observeLocalCodexEffortLadder(): ReadonlySet<string> | null {
* a single `ocx connect`.
*/
function catalogObserver(deps: ClientCatalogProbeDeps | undefined): CatalogCompatibilityDeps {
return { supportedEfforts: deps?.supportedEfforts ?? observeLocalCodexEffortLadder };
const selected = deps?.selectedCodexCommand;
return { supportedEfforts: deps?.supportedEfforts ?? (() => observeLocalCodexEffortLadder(selected)) };
}

/** The stat half of the catalog verdict, shared by the status collector and `ocx connect`. */
Expand Down
40 changes: 21 additions & 19 deletions src/cli/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,27 @@ export async function collectStatus(): Promise<CliStatusView> {
desiredEnabled: claudeDesktopIntegrationEnabled(config),
policy: claudeDesktopPolicyHealth(probeClaudeDesktopPolicy()),
};
const clientConnection = collectClientConnectionStatus();
const resolvedRuntime = (() => {
try {
return resolveCodexRuntime();
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
const redacted = redactUserPath(redactSecretString(message)).slice(0, 160);
return {
runtime: { command: "codex", version: null, source: "fallback" as const },
failures: [{
command: "codex",
source: "fallback" as const,
reason: `resolve threw: ${redacted}`,
}],
replacedConfigured: undefined,
newerAvailable: undefined,
};
}
})();
const clientConnection = collectClientConnectionStatus(Date.now(), undefined, {
selectedCodexCommand: resolvedRuntime.runtime.command,
});
// Asked before the local probes below so a connected client's report is hub-sourced from its
// first line. Bounded and failure-tolerant: an offline hub degrades the remoteHub block, it
// does not fail `ocx status`.
Expand Down Expand Up @@ -567,24 +587,6 @@ export async function collectStatus(): Promise<CliStatusView> {
routingKind: getCodexRoutingKind(),
});
const codexPlugins = diagnoseCodexBundledPlugins();
const resolvedRuntime = (() => {
try {
return resolveCodexRuntime();
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
const redacted = redactUserPath(redactSecretString(message)).slice(0, 160);
return {
runtime: { command: "codex", version: null, source: "fallback" as const },
failures: [{
command: "codex",
source: "fallback" as const,
reason: `resolve threw: ${redacted}`,
}],
replacedConfigured: undefined,
newerAvailable: undefined,
};
}
})();
const lastClamp = loadLastEffortClamp();
const clampActive = effortClampAppliesToRuntime(lastClamp, resolvedRuntime.runtime);
const codexHome = collectOrcaCodexHomeDiagnostic();
Expand Down
3 changes: 3 additions & 0 deletions structure/clients/claude-desktop.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Claude-only connections keep their existing non-failing readiness policy; displa

## Connected Claude Desktop profiles

The connection's local Codex readiness check follows the [selected-runtime probe contract](../runtime.md#remote-hub-hardening-ownership); general status hands its resolved command to this check instead of probing the version twice.
It does not discover lower-priority alternatives after a valid selection or alter Desktop ownership.

Connected `ocx claude desktop apply` reads the hub's Desktop snapshot and writes the hub origin
and exact hub-issued IDs to the local Desktop configuration. Static/hybrid embed the entries;
discovery-only keeps discovery on the hub. The hub owns family assignments and defaults; local
Expand Down
3 changes: 3 additions & 0 deletions structure/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ the residual directory for manual review; there is no recursive-delete fallback.
The connection's `tokenFingerprint` participates in
[`ocx status` credential binding](runtime.md#remote-hub-status-credential-binding).

Client catalog readiness observes the selected Codex runtime without creating or rewriting
`codex-runtime.json`; general status reuses its already-resolved command under the [runtime contract](runtime.md#remote-hub-hardening-ownership).

Client connection metadata stores a stable `apiKeyId` and a non-secret rotation `pendingOperation`. The current data secret remains only in `service-api-token`; a bounded rotation temporarily keeps the old secret in owner-only `service-api-token.prev`. Commit or recovery clears the marker before orphan cleanup. `ocx disconnect` is local-only and leaves remote revocation to the hub's **Integrations → API Keys** page. Hub and local usage stores are not mirrored.

Codex display-cache expiry, retained main-policy evidence, and reset history follow the
Expand Down
3 changes: 3 additions & 0 deletions structure/ops/docs-and-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Native retirement keeps active model/quota instructions aligned across locales w
[catalog contract](../catalog.md#shared-catalog). Historical records and other providers
sharing a model-name fragment remain distinct from current Codex-native support.

The Remote Hub guide distinguishes selected-runtime readiness from general runtime diagnostics;
`tests/cli/cli-connect-readiness.test.ts` exercises that boundary and general status's single discovery pass with isolated executable fixtures.

## GitHub Pages

`.github/workflows/deploy-docs.yml` publishes the docs to:
Expand Down
5 changes: 5 additions & 0 deletions structure/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,11 @@ provider name for that assessment, so `planPassthroughWebSearchBridge` takes it

## Remote Hub hardening ownership

`src/cli/connect.ts` resolves only through the first valid local Codex runtime for catalog
readiness, then reads that runtime's effort ladder without persisting its selection. Rejected
preferred candidates still fall back in priority order. General `ocx status` retains full runtime
discovery and passes its resolved command into readiness, avoiding a second version probe without adding cache state.

`src/remote/protocol.ts` owns pure interval/feature negotiation. `src/remote/hub-state.ts` owns the `GET|HEAD /v1/hub-state` contract, its caps, and the parser both sides share. `src/client/hub-client.ts` owns bounded, schema-validated remote catalog consumption, hub-state reads, and key-id probes; `src/client/hub-state.ts` owns the resolution and the owner-stamped 0600 cache, and a failed read reports "unavailable" rather than degrading to the client's own local provider and login state. `src/client/hub-relay.ts` is a fixed-authority management relay with URL, header, body, redirect, and stream bounds. The public data listener remains the direct client→hub path; the loopback management ingress never serves data-plane routes.

### Remote Hub status credential binding
Expand Down
Loading
Loading