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
1 change: 1 addition & 0 deletions scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -1656,6 +1656,7 @@
"server-request-body-size.test.ts": "server",
"server-search.test.ts": "server",
"server-startup-reconcile-resilience.test.ts": "server",
"server-startup-codex-off.test.ts": "server",
"server-stop-config-hardening.test.ts": "server",
"server-xai-chat-reasoning-streaming.test.ts": "server",
"server-xai-header-parity.test.ts": "server",
Expand Down
14 changes: 7 additions & 7 deletions src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,17 +267,17 @@ function startServerWithSpendLedgerOwner(port: number | undefined, deps: StartSe
startupOwnershipHomes = homes;
startupOwnershipStatePaths = statePaths;
} catch { /* inspection below stays unknown */ }
const startupCacheOwnership = inspectStartupOwnership(
const startupCacheOwnership = shouldSyncCodexOnStart(config) ? inspectStartupOwnership(
deps,
startupOwnershipHomes,
startupOwnershipStatePaths,
startupWindowsTaskListingCache,
);
) : null;
Comment on lines +270 to +275

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the mapped ownership-probe contract

For Codex-OFF, sibling, or hub-without-local-integration starts, this branch now skips the first ownership probe and the analogous branch below skips the second, but structure/ops/service-and-sidecars.md:109-113 still states that every proxy startup asks twice and that the second query is mandatory. Update that mapped architecture document to scope the two-probe contract to enabled startup so it does not contradict the runtime.

AGENTS.md reference: src/AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

// Startup cache invalidation is best-effort and applies only when startup sync is enabled.
// Check OFF before taking K: on Windows K resolves SID + LocalAppData through PowerShell,
// and run 35093667426 exceeded healthy controls by 33.8 s against that 30 s child budget.
// The permit callback still re-reads intent under K to close a concurrent disable race.
if (shouldSyncCodexOnStart(config) && startupCacheOwnership.ownership === "owned" && startupOwnershipHomes !== null) {
if (startupCacheOwnership?.ownership === "owned" && startupOwnershipHomes !== null) {
try {
const startupCodexHome = startupOwnershipHomes.codexHome;
// #1046: record whether this actually rewrote the cache. `handleStart` ORs this
Expand Down Expand Up @@ -549,12 +549,12 @@ function startServerWithSpendLedgerOwner(port: number | undefined, deps: StartSe
// clients; no Codex request can use this lifecycle in that state.
// Re-probe here instead of trusting the earlier cache decision: startup work
// between the two sites must not widen the service-install race.
const nativeOwnership = inspectStartupOwnership(
const nativeOwnership = shouldSyncCodexOnStart(config) ? inspectStartupOwnership(
deps,
startupOwnershipHomes,
startupOwnershipStatePaths,
);
const preparedNativeMainLifecycle = nativeOwnership.ownership !== "foreign"
) : null;
const preparedNativeMainLifecycle = nativeOwnership !== null && nativeOwnership.ownership !== "foreign"
&& startupOwnershipHomes !== null
? prepareNativeMainStartupLifecycle(
deps.nativeMainStartup,
Expand Down Expand Up @@ -600,7 +600,7 @@ function startServerWithSpendLedgerOwner(port: number | undefined, deps: StartSe
return retryPreparedNativeMainLifecycle.start();
},
};
const nativeMainLifecycle: NativeMainStartupLifecycle = shouldSyncCodexOnStart(config)
const nativeMainLifecycle: NativeMainStartupLifecycle = nativeOwnership !== null
? nativeOwnership.ownership === "owned"
? preparedNativeMainLifecycle
? preparedNativeMainLifecycle.start()
Expand Down
4 changes: 2 additions & 2 deletions structure/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ independent. Ordinary stop drains the spend reporters after listener teardown be
reference. `src/server/index/spend-ledger-lifecycle.ts` also holds the lease through failed-start listener rollback. Enforced seeds remain live until reporters close and settlement plus send-ID forgetting are durable; scope totals survive forgetting. SQLite and the OS release a crashed owner; no PID, timestamp,
TTL or lock-file deletion participates in recovery. Startup passes the provider roster and top-level pool aliases with the spend policy; live state reconciliation refreshes both without opening an unused ledger. Under the [request-start policy](transports/responses-spend.md#durable-spend-reservations), existing requests retain their initial enforcement mode, numeric ceilings and L; changed limits apply to later requests. HTTP admission checks [historical continuity](transports/responses-spend.md#historical-pool-continuity-and-rollback) even without a root; failed accounting reads release the active-turn lease. Rooted continuity denials record exactly one workflow refusal event before response formatting.

An explicit Codex integration OFF skips startup cache invalidation before the user-scoped catalog
serialization lock is resolved. Explicit `sync` and `sync-cache` retain their catalog-only override.
An explicit Codex integration OFF skips startup ownership probes, native-main preparation and cache invalidation before resolving the catalog lock.
Enabled startup retains separate fresh ownership decisions. Explicit `sync` and `sync-cache` retain their catalog-only override.

`startServer` composes up to four sockets in one synchronous startup transaction: the public data listener, optional unauthenticated data-loopback and hub-management listeners, and the optional `hub-link` listener, which opens only for a recorded link and persists its concrete `127.0.0.1:<listenerPort>`. HTTP Responses/Messages apply the [caller header contract](data-planes/inbound-compat.md#http-caller-conversation-identity) before turn admission and dispatch. Linked-machine data uses the [connection-bound relay contract](remote-link.md#connection-bound-relay-authentication); client-local credentials and routing policy remain unchanged.
The data-loopback socket serves a fixed data-plane allowlist: Responses and its compact sibling,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import { afterEach, beforeEach, describe, expect, setDefaultTimeout, test } from "bun:test";
import { mkdtempSync, writeFileSync } from "node:fs";
import http2 from "node:http2";
import { tmpdir } from "node:os";
Expand All @@ -17,10 +17,13 @@ import { handleNativeProfileAPI } from "../../src/codex/native-profile-api";
import type { NativeProfileManager } from "../../src/codex/native-profile-manager";
import type { OcxConfig } from "../../src/types";
import { ownedServiceHomeInspection } from "../helpers/owned-service-home-inspection";
import { removeTreeWithRetry } from "../helpers/remove-tree";
import { removeTreeAfterBackgroundWork } from "../helpers/remove-tree";
import { fakeChatGptJwt } from "../helpers/fake-chatgpt-jwt";
import { resetVisionDescriptionCache } from "../../src/vision";
import { SERVER_BUDGET_MS } from "../helpers/test-budget";

// Every case starts a real server; on Windows its config-dir ACL work alone can exceed 5 s.
setDefaultTimeout(SERVER_BUDGET_MS);
import { saveCodexAccountCredential } from "../../src/codex/account-store";

/**
Expand Down Expand Up @@ -195,7 +198,7 @@ beforeEach(() => {
}) as typeof fetch;
});

afterEach(() => {
afterEach(async () => {
resetVisionDescriptionCache();
closeRequestHistoryIndex();
clearComboTargetCooldowns();
Expand All @@ -209,8 +212,9 @@ afterEach(() => {
else process.env.CODEX_HOME = previousCodexHome;
if (previousDataToken === undefined) delete process.env.OPENCODEX_API_AUTH_TOKEN;
else process.env.OPENCODEX_API_AUTH_TOKEN = previousDataToken;
if (ocxHome) removeTreeWithRetry(ocxHome);
if (codexHome) removeTreeWithRetry(codexHome);
// Startup's config-dir ACL hardening may still hold the homes on Windows; settle it first.
if (ocxHome) await removeTreeAfterBackgroundWork(ocxHome);
if (codexHome) await removeTreeAfterBackgroundWork(codexHome);
ocxHome = "";
codexHome = "";
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ describe("Windows ownership probe hardening regressions", () => {
...getDefaultConfig(),
port: 0,
hostname: "127.0.0.1",
clientIntegrations: { codex: false },
clientIntegrations: { codex: true },
subagentModels: [],
}, null, 2));

Expand Down Expand Up @@ -278,7 +278,10 @@ describe("Windows ownership probe hardening regressions", () => {
winswStatus: () => "nonexistent",
});
ownerships.push(answer.ownership);
return answer;
// Exercise enabled startup's two real inspections without taking the unrelated
// user-scoped catalog lock (which resolves Windows identity through PowerShell).
// Preserve each raw verdict above so a stale second listing still fails this test.
return ownerships.length === 1 ? { ownership: "unknown", reason: "cache writes outside this fixture" } : answer;
} finally {
setTrustedWindowsSystemDirectoryResolverForTests(null);
}
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/test-layout-expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,7 @@
"server-opencode-go-goal-streaming.test.ts": "server", "server-rate-limit-retry-e2e.test.ts": "server",
"server-request-body-size.test.ts": "server", "server-search.test.ts": "server",
"server-startup-reconcile-resilience.test.ts": "server", "server-stop-config-hardening.test.ts": "server",
"server-startup-codex-off.test.ts": "server",
"server-xai-chat-reasoning-streaming.test.ts": "server", "server-xai-header-parity.test.ts": "server",
"server-xai-oauth-401-replay.test.ts": "server", "server-xai-responses-streaming.test.ts": "server",
"service-auth-qualified-localhost.test.ts": "service", "service-child-ownership.test.ts": "service",
Expand Down
47 changes: 47 additions & 0 deletions tests/server/server-startup-codex-off.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { expect, test } from "bun:test";
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { saveConfig } from "../../src/config";
import { startServer } from "../../src/server";
import { installIsolatedCodexHome } from "../helpers/isolated-codex-home";
import { removeTreeWithRetry } from "../helpers/remove-tree";

test("Codex OFF starts without service-manager probes or native-main preparation", async () => {
const priorHome = process.env.OPENCODEX_HOME;
const home = mkdtempSync(join(tmpdir(), "ocx-start-codex-off-"));
const codex = installIsolatedCodexHome("ocx-start-codex-off-client-");
process.env.OPENCODEX_HOME = home;
let inspections = 0;
let preparations = 0;
let server: ReturnType<typeof startServer> | undefined;
try {
saveConfig({
providers: { fixture: { adapter: "openai-chat", baseUrl: "http://127.0.0.1:1/v1", allowPrivateNetwork: true } },
defaultProvider: "fixture", port: 0, hostname: "127.0.0.1",
clientIntegrations: { codex: false }, checkForUpdates: false,
syncResumeHistory: false, claudeCode: { intercept: { enabled: false } },
});
server = startServer(0, {
inspectNativeCodexOwnership: () => {
inspections++;
return { ownership: "owned", reason: "unused Codex OFF probe" };
},
nativeMainStartup: {
get manager() {
preparations++;
throw new Error("Codex OFF must not prepare a native manager");
},
},
});
expect(inspections).toBe(0);
expect(preparations).toBe(0);
expect((await fetch(`http://127.0.0.1:${server.port}/healthz`)).status).toBe(200);
} finally {
await server?.stop(true);
codex.restore();
if (priorHome === undefined) delete process.env.OPENCODEX_HOME;
else process.env.OPENCODEX_HOME = priorHome;
removeTreeWithRetry(home);
}
});
Loading