From 203b3e208acc1158d3d67334f9632aaa19aa9711 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 26 Jun 2026 04:04:42 +0000 Subject: [PATCH] fix: scope codex-pool sentinel image to enabled targets --- .../platform-infra-sub2api-codex/actions.ts | 43 +++++++++++++------ .../platform-infra-sub2api-codex/options.ts | 1 + .../remote-python-sync.ts | 11 ++++- .../runtime-target.ts | 6 +++ .../src/platform-infra-sub2api-codex/types.ts | 1 + 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/scripts/src/platform-infra-sub2api-codex/actions.ts b/scripts/src/platform-infra-sub2api-codex/actions.ts index f1321b1b..cfbf1244 100644 --- a/scripts/src/platform-infra-sub2api-codex/actions.ts +++ b/scripts/src/platform-infra-sub2api-codex/actions.ts @@ -95,9 +95,9 @@ export async function codexPoolSync(config: UniDeskConfig, options: SyncOptions) }; } - const sentinelImage = pool.sentinel.monitor.enabled + const sentinelImage = pool.sentinel.monitor.enabled && runtimeTarget.sentinelEnabled ? await runCodexPoolSentinelImage(config, pool, { action: "build", confirm: true, dryRun: false, full: options.full, raw: false, targetId: options.targetId }) - : { ok: true, mode: "skipped-monitor-disabled" }; + : { ok: true, mode: runtimeTarget.sentinelEnabled ? "skipped-monitor-disabled" : "skipped-target-disabled" }; if (sentinelImage.ok !== true) { return { ok: false, @@ -113,17 +113,20 @@ export async function codexPoolSync(config: UniDeskConfig, options: SyncOptions) const payload = { pruneRemoved: options.pruneRemoved, sentinel: { - manifest: renderCodexPoolSentinelManifest(pool.sentinel, sentinelProfileSecrets(profiles), { - namespace: runtimeTarget.namespace, - serviceName: runtimeTarget.serviceName, - serviceDns: runtimeTarget.serviceDns, - appSecretName: runtimeTarget.appSecretName, - adminEmailDefault: pool.adminEmailDefault, - proxy: runtimeTarget.egressProxy?.applyToSentinel ? { - httpProxy: runtimeTarget.egressProxy.httpProxy, - noProxy: runtimeTarget.egressProxy.noProxy, - } : null, - }), + enabledForTarget: runtimeTarget.sentinelEnabled, + manifest: runtimeTarget.sentinelEnabled + ? renderCodexPoolSentinelManifest(pool.sentinel, sentinelProfileSecrets(profiles), { + namespace: runtimeTarget.namespace, + serviceName: runtimeTarget.serviceName, + serviceDns: runtimeTarget.serviceDns, + appSecretName: runtimeTarget.appSecretName, + adminEmailDefault: pool.adminEmailDefault, + proxy: runtimeTarget.egressProxy?.applyToSentinel ? { + httpProxy: runtimeTarget.egressProxy.httpProxy, + noProxy: runtimeTarget.egressProxy.noProxy, + } : null, + }) + : null, summary: codexPoolSentinelSummary(pool.sentinel), }, pool: { @@ -215,6 +218,20 @@ export async function codexPoolSentinelImage(config: UniDeskConfig, options: Sen export async function runCodexPoolSentinelImage(config: UniDeskConfig, pool: CodexPoolConfig, options: SentinelImageOptions): Promise> { const runtimeTarget = codexPoolRuntimeTarget(options.targetId); const target = codexPoolSentinelRuntimeImage(pool.sentinel); + if (!runtimeTarget.sentinelEnabled) { + return { + ok: true, + action: "platform-infra-sub2api-codex-pool-sentinel-image", + mode: "skipped-target-disabled", + target: { + id: runtimeTarget.id, + namespace: runtimeTarget.namespace, + }, + image: target, + mutation: false, + valuesPrinted: false, + }; + } if (options.action === "build" && options.dryRun) { return { ok: true, diff --git a/scripts/src/platform-infra-sub2api-codex/options.ts b/scripts/src/platform-infra-sub2api-codex/options.ts index 1ddb4515..c13c87c4 100644 --- a/scripts/src/platform-infra-sub2api-codex/options.ts +++ b/scripts/src/platform-infra-sub2api-codex/options.ts @@ -324,5 +324,6 @@ export interface Sub2ApiRuntimeConfig { defaultTargetId: string; appSecretName: string; secretsRoot: string; + sentinelEnabledOnTargets: string[]; targets: Record[]; } diff --git a/scripts/src/platform-infra-sub2api-codex/remote-python-sync.ts b/scripts/src/platform-infra-sub2api-codex/remote-python-sync.ts index 016e7b5a..7d892900 100644 --- a/scripts/src/platform-infra-sub2api-codex/remote-python-sync.ts +++ b/scripts/src/platform-infra-sub2api-codex/remote-python-sync.ts @@ -67,6 +67,7 @@ EXPECTED_ACCOUNT_TEMP_UNSCHEDULABLE = json.loads(${JSON.stringify(JSON.stringify MANUAL_ACCOUNT_PROTECTIONS = json.loads(${JSON.stringify(JSON.stringify(resolvedManualAccountProtections(pool, target)))}) SENTINEL_CONFIG = json.loads(${JSON.stringify(JSON.stringify(pool.sentinel))}) TARGET_EGRESS_PROXY = json.loads(${JSON.stringify(JSON.stringify(target.egressProxy))}) +TARGET_SENTINEL_ENABLED = ${JSON.stringify(target.sentinelEnabled)} MODE = "${mode}" PAYLOAD_B64 = "${encodedPayload}" @@ -166,7 +167,7 @@ def empty_to_none(value): return value if isinstance(value, str) and value else None def sentinel_quality_gate_enabled(): - return (SENTINEL_CONFIG.get("monitor") or {}).get("enabled") is True and (SENTINEL_CONFIG.get("actions") or {}).get("enabled") is True + return TARGET_SENTINEL_ENABLED and (SENTINEL_CONFIG.get("monitor") or {}).get("enabled") is True and (SENTINEL_CONFIG.get("actions") or {}).get("enabled") is True def account_notes_fingerprint(account): notes = account.get("notes") if isinstance(account, dict) else None @@ -981,6 +982,12 @@ def ensure_api_key_secret(group_id): return api_key, secret_action, text(proc.stdout, 1000) def apply_sentinel_manifest(manifest): + if not TARGET_SENTINEL_ENABLED: + return { + "ok": True, + "action": "skipped-target-disabled", + "valuesPrinted": False, + } if not isinstance(manifest, str) or not manifest.strip(): return { "ok": False, @@ -1433,6 +1440,8 @@ def sentinel_state_summary(): } def reassert_sentinel_freezes_after_sync(token): + if not TARGET_SENTINEL_ENABLED: + return {"ok": True, "skipped": True, "reason": "target-disabled", "items": [], "valuesPrinted": False} if (SENTINEL_CONFIG.get("actions") or {}).get("enabled") is not True: return {"ok": True, "skipped": True, "reason": "actions-disabled", "items": [], "valuesPrinted": False} _, state = sentinel_state_object() diff --git a/scripts/src/platform-infra-sub2api-codex/runtime-target.ts b/scripts/src/platform-infra-sub2api-codex/runtime-target.ts index 52394639..9e610b32 100644 --- a/scripts/src/platform-infra-sub2api-codex/runtime-target.ts +++ b/scripts/src/platform-infra-sub2api-codex/runtime-target.ts @@ -41,11 +41,16 @@ export function readSub2ApiRuntimeConfig(): Sub2ApiRuntimeConfig { const secrets = runtime !== null && isRecord(runtime.secrets) ? runtime.secrets : null; const secretsRoot = secrets === null ? null : stringValue(secrets.root); if (secretsRoot === null || !secretsRoot.startsWith("/")) throw new Error(`${sub2apiConfigPath}.runtime.secrets.root must be an absolute path`); + const sentinel = runtime !== null && isRecord(runtime.sentinel) ? runtime.sentinel : null; + const enabledOnTargets = Array.isArray(sentinel?.enabledOnTargets) + ? sentinel.enabledOnTargets.map((entry) => stringValue(entry)).filter((entry): entry is string => entry !== null && entry.length > 0) + : []; if (!Array.isArray(parsed.targets) || !parsed.targets.every(isRecord)) throw new Error(`${sub2apiConfigPath}.targets must be a list`); return { defaultTargetId, appSecretName, secretsRoot, + sentinelEnabledOnTargets: enabledOnTargets, targets: parsed.targets, }; } @@ -100,6 +105,7 @@ export function codexPoolRuntimeTarget(targetId?: string): CodexPoolRuntimeTarge publicExposure, appSecretName: runtimeConfig.appSecretName, secretsRoot: runtimeConfig.secretsRoot, + sentinelEnabled: runtimeConfig.sentinelEnabledOnTargets.some((entry) => entry.toLowerCase() === id.toLowerCase()), sentinelImageBuild, egressProxy, }; diff --git a/scripts/src/platform-infra-sub2api-codex/types.ts b/scripts/src/platform-infra-sub2api-codex/types.ts index d516774f..76e14d45 100644 --- a/scripts/src/platform-infra-sub2api-codex/types.ts +++ b/scripts/src/platform-infra-sub2api-codex/types.ts @@ -87,6 +87,7 @@ export interface CodexPoolRuntimeTarget { publicExposure: PublicServiceExposure | null; appSecretName: string; secretsRoot: string; + sentinelEnabled: boolean; sentinelImageBuild: { baseImageCachePolicy: "pull" | "local-if-present"; noProxy: string;