Repository navigation
Feature: provenance-aware Codex CLI update manager #2811
Description
Area
Multiple areas
What are you trying to accomplish?
OpenCodex already detects the Codex runtime it uses, restores an established shim after a supported external update, and can identify long-lived app-server processes. It does not yet provide one safe workflow for checking and updating the independently installed Codex CLI that OpenCodex is actually integrating with.
The desired workflow is an explicit, provenance-aware Codex CLI update manager. It should tell the operator which selected CLI installation is manageable, which installation channel owns it, whether an update can proceed without disrupting an active Codex session, and what exact version would be installed before any mutation occurs.
What prevents this today?
Codex can coexist in several independent forms on one host: an npm global installation, a native app bundle, a standalone installer, or a version-manager-owned tree. A path and codex --version result are not enough to decide which installer owns that runtime or whether OpenCodex may safely replace it.
Operators currently have to combine separate manual steps:
- determine which
codexruntime OpenCodex selected; - infer how that runtime was installed;
- check whether an interactive CLI, app-server, or code-mode host still uses it;
- run the matching installer;
- restore and verify the OpenCodex shim when applicable; and
- read the actual runtime back instead of trusting an installer exit code.
This is error-prone when an app-bundled CLI and an independently installed CLI coexist. It is also unsafe to generalize the existing OpenCodex self-update worker to Codex: the two packages have different ownership, process, and rollback boundaries.
What should OpenCodex do?
Provide a separate, explicitly invoked Codex CLI update workflow with these phases:
-
Inspect and dry-run
- Resolve the exact selected Codex runtime.
- Classify its installation provenance from direct package or installer evidence.
- Report current version, update channel, redacted canonical location, shim state, and active or unknown process blockers.
- Produce a deterministic plan without writing files, changing configuration, or starting an installer.
-
Explicit apply
- Require an operator-confirmed plan ID.
- Revalidate runtime identity, installation provenance, current version and hash, target version and integrity, and process blockers immediately before applying.
- Install one exact resolved version through the verified owner channel.
- Read back the selected runtime and restore the existing shim only when that installation had an eligible shim before the update.
- Classify the result from readback as applied, not applied, ambiguous, or applied with shim repair still required.
-
Dashboard integration
- Present Codex CLI updates separately from OpenCodex updates.
- Keep Apply disabled until the operator has reviewed the dry-run plan and confirmed that only the selected independent CLI will be changed.
Safety requirements:
- Native app, Store/MSIX, and app-bundled Codex binaries are always non-managed.
- Version-manager-owned and otherwise unverified installations are non-managed; OpenCodex must not adopt or rewrite them.
- An active matching Codex session defers the update. Unknown process identity or failed enumeration also defers it.
- OpenCodex must not terminate or restart Codex, the native app, app-server, code-mode host, proxy, or tray as part of this workflow.
latestis resolved before apply and the exact version plus registry integrity or installer digest is bound into the plan.- A stale plan is rejected rather than silently regenerated.
- Ambiguous post-install state is not retried or rolled back automatically.
- No app-bundle copying or manual restoration of a package tree is used as rollback.
The implementation should remain reviewable as three focused changes: provenance and dry-run; apply engine and CLI/management API; dashboard and documentation.
Example usage or interface
ocx system codex-cli-update check --json
ocx system codex-cli-update dry-run --channel latest --json
ocx system codex-cli-update apply --plan-id <id> --yes --json
ocx system codex-cli-update status <job-id> --jsonExample non-managed result:
{
"selectedVersion": "0.149.0",
"provenance": "app-bundle",
"managed": false,
"reason": "app_bundle",
"installerCommand": null
}Example deferred result:
{
"managed": true,
"status": "deferred",
"reason": "active_codex_session",
"installerStarted": false
}Alternatives or workarounds
- Running the installation command manually and then
ocx ensureworks when the operator already knows the owning channel and no Codex process is using that installation. It does not provide a bound plan or post-readback classification. - A scheduler or cross-host updater would add a second orchestration problem before the single-host operation is safe. This proposal deliberately leaves scheduling and multi-host coordination out of scope.
- Updating the native desktop app remains the app platform's responsibility.
Additional context
This proposal builds on, rather than duplicates, existing contracts:
- fix(codex): auto-restore the codex shim after external npm updates (#320) #356 restores an established OpenCodex shim after a supported external npm update.
- fix(codex): report a destroyed shim instead of bailing silently #2519 refuses to adopt version-manager-owned Codex installations and provides actionable diagnostics.
- feat(cli): add an opt-in Windows desktop-app restart for a stale model picker #2382 keeps desktop-app restart an explicit opt-in operation.
- fix(server): refuse requests after the package tree is replaced under a live proxy (#2459) #2632 fails closed when the OpenCodex package tree changes under a live process.
Acceptance tests should cover npm shim-to-backing provenance, simultaneous PATH candidates, app bundles, version-manager layouts, unverified standalone installs, active and unknown process identity, PID reuse, a dry run with zero writes and zero installer spawns, deterministic plan identity, stale-plan rejection, and post-install readback taking precedence over exit status.
Checks
- I searched existing issues and documentation.
- This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
- I removed secrets and personal data.
Activity
lidge-jun commentedon Aug 28, 2026
리뷰 · 우선순위 51 / 80
설명
이 이슈는 OpenCodex가 골라 쓰는 독립 Codex CLI를 설치 출처를 확인한 뒤에만 안전하게 올리는 새 워크플로를 원한다.
지금은 런타임을 고르고, 심을 복구하고, 오래 사는 app-server를 찾는 조각은 있다.
지금 HEAD에는 런타임 선택과 심 복구와 app-server 감시만 있다.
설치 채널을 보고 안전하게 올리는 한 흐름은 아직 없다.
운영자는 경로와 버전만 보고 설치기를 손으로 고른 뒤 ocx ensure로 심을 되살린다.
앱 번들 CLI와 따로 깐 CLI가 한 머신에 있으면 잘못된 쪽을 올릴 수 있다.
지금 dev HEAD 8d9e28692에서 이미 있는 조각은 이렇다.
src/codex/runtime.ts 의 resolveCodexRuntime 은 environment, configured, shim, path, fallback 순으로 고른 바이너리와 버전을 돌려 준다.
이 값은 어떻게 골랐는가지, 누가 설치했는가가 아니다.
심 자동 복구는 이미 있다.
버전 매니저 트리 거부는 mise, asdf, volta 만 해당하고 nvm 과 fnm 은 일부러 빼 둔다.
오래 사는 app-server 와 code-mode-host 감시, PID 재사용 가드, 프로세스 확인 실패 시 닫는 처리도 이미 있다.
OpenCodex 자기 업데이트는 이미 다른 물건이다. system update 동사와 update 워커는 OpenCodex 패키지만 다룬다.
이 워커를 Codex 에 재사용하면 안 된다. 소유자, 프로세스, 롤백 경계가 다르다.
제안한 system codex-cli-update 동사를 따로 쓰는 게 맞다.
dry-run 은 파일을 쓰지 않고 설치기를 켜지 않아야 한다.
apply 는 확인한 plan id 만 받고, 적용 직전에 런타임과 출처와 버전과 해시와 프로세스 차단을 다시 검사해야 한다.
설치 후 읽은 값이 종료 코드보다 우선한다.
앱 번들, Store, MSIX, 버전 매니저, 출처를 못 밝힌 단독 설치는 관리하지 않는다.
세션이 살아 있거나 프로세스 목록을 못 읽으면 미룬다. Codex 나 데스크톱이나 프록시를 이 흐름에서 끄면 안 된다.
latest 는 apply 전에 풀어서 정확한 버전과 무결성을 계획에 묶고, 낡은 계획은 거절한다.
작성자가 적은 세 단계, 출처와 dry-run, apply 엔진과 CLI, 대시보드와 문서는 리뷰하기 좋다.
지금 dev 열차는 Cursor umbrella catalog, destination-policy IPv6 래핑, OAuth identity 재바인딩이다. 이 기능은 그 열차에 끼울 이유가 없다.
types 와 config 스플릿과도 직접 충돌하지 않는다. 다만 새 설정 키를 큰 설정 타입에 넣으면 그 캠페인에 쓸려 갈 수 있으니 계획과 잡 상태는 설정 파일 밖에 두는 편이 낫다.
Preview 배포도 이 이슈의 범위가 아니다.
경로 system-command ocx system update - 이미 OpenCodex 자기 업데이트 동사다. Codex CLI 업데이트는 다른 동사여야 한다.
경로 update detectInstall - OpenCodex 설치 출처만 본다. Codex 설치기를 이 함수로 판정하면 잘못된 패키지를 올리게 된다.
경로 runtime 선택 출처는 설치 채널이 아니다.
설치 채널 분류는 새 검사기가 필요하다.
경로 shim 버전 매니저 검사는 지금 세 매니저만 거부한다.
업데이트 쪽이 더 엄격해야 하는지는 정해야 한다.
경로 app-server 감시가 실패하면 반드시 미룬다.
restart 를 이 흐름에 넣으면 안전 요구를 깨뜨린다.
경로 대시보드 - Codex CLI 카드를 따로 두고 Apply 는 dry-run 뒤에만 켠다.
GUI 는 세 번째 변경으로 미루라.
메인테이너의 판단이 필요한 지점
너의 추천
이슈는 열고, 구현은 지금 열차 뒤에 두어라. 첫 변경은 출처 분류와 dry-run만, 쓰기는 없어야 한다.
이미 있는 런타임 선택과 버전 매니저 거부와 app-server 감시를 재사용하고, OpenCodex 자기 업데이트 워커는 재사용하지 마라.
앱 번들과 스토어와 버전 매니저는 비관리로 두고, 프로세스 목록을 못 읽으면 미뤄라. 대시보드는 세 번째다. 스플릿에 걸릴 새 설정 키는 넣지 마라.
이 댓글은 grok-bot이 작성했습니다
hhhappb commentedon Sep 3, 2026
Real-world Windows reproduction and verified workaround
I hit this exact issue on Windows with an npm-backed Codex CLI selected through the OpenCodex shim while Codex Desktop had already updated its bundled runtime.
Environment
2.39.00.147.00.152.1C:\Users\[USER]\AppData\Roaming\npmObserved behavior
Updating Codex Desktop did not update the independently installed npm CLI used by OpenCodex. OpenCodex correctly reported the version difference:
{ "codexRuntime": { "version": "0.147.0", "source": "configured", "newerAvailable": { "version": "0.152.1" }, "warning": "OpenCodex is using an older Codex binary." } }However, the inspection command could not identify a manageable candidate:
{ "candidateAvailable": false, "selectionAttested": false, "provenance": "unknown", "managed": false, "reason": "candidate_unavailable" }This leaves users in a confusing state where Codex Desktop appears current, but OpenCodex continues using an older CLI from PATH. This is likely common on Windows systems where both Codex Desktop and a previous global npm installation coexist.
Verified manual workaround
After closing active Codex sessions, I used the following process:
Resolve the latest stable npm version:
Update the independently installed npm CLI to the exact resolved version:
The npm install replaces the OpenCodex wrapper launchers. Restore the shim explicitly:
ocx codex-shim installOpenCodex detected the replacement, backed up the new npm launchers as
codex.opencodex-real.*, and recreated the wrapper shim.Verify both the backing CLI and shim state:
Result
The OpenCodex proxy continued running throughout, and routing remained functional after shim restoration.
Suggested short-term documentation/UI improvement
Until the full provenance-aware updater is implemented, the dashboard/doctor warning could include an installation-specific recovery hint when all of the following are directly evidenced:
@openai/codexunder the npm global prefix;codex.opencodex-real.*backing launcher exist;Suggested guidance:
The important part is documenting that updating Codex Desktop does not update the separate npm runtime selected by OpenCodex, and that npm replacement of the launchers requires explicit shim restoration and readback verification.
luvs01 commentedon Sep 10, 2026
Author response: reproduction accepted, scope clarified
Thank you for the Windows reproduction. It is direct evidence for the gap this issue describes, and it sharpens two points I had only stated abstractly.
The
candidate_unavailableresult is the reportable defect, not just missing tooling. Your capture showscodexRuntime.newerAvailablecorrectly detecting0.152.1while inspection simultaneously returnscandidateAvailable: false,provenance: "unknown",managed: false. Version detection and provenance classification therefore disagree on the same runtime. That combination — an npm-global@openai/codexbehind a healthy OpenCodex shim, which is the ordinary Windows layout once Codex Desktop is also installed — should classify as npm-owned and manageable rather than unknown. I am folding this exact state into the acceptance criteria as a required classifier case, because a provenance engine that cannot recognize the most common manageable installation would not deliver the workflow.Your workaround also confirms the shim-restoration ordering the design depends on.
npm install -greplaces the wrapper launchers, OpenCodex backs them up ascodex.opencodex-real.*, andocx codex-shim installrecreates the wrapper. That is why the apply phase must restore the shim only when an eligible shim existed beforehand, and must classify the outcome from an actual runtime readback rather than the installer exit code. Your final verification throughcodex --version,ocx codex-shim statusandocx status --jsonis the readback the apply phase is meant to automate. The proxy staying up throughout also supports the constraint that this flow must never stop Codex, the desktop app, or the proxy.On the short-term guidance you propose: I agree with the substance and with your evidence gating. The message should appear only when the selected runtime is npm-global
@openai/codex, a valid shim pluscodex.opencodex-real.*backing launcher exist, npm reports a newer stable version, and no matching Codex process is active. The core fact worth documenting is the one users miss: updating Codex Desktop does not update the separate npm runtime that OpenCodex selected, and npm replacing the launchers requires explicit shim restoration followed by readback.I am not opening an implementation PR for that hint yet. The maintainer review on this issue placed implementation behind the current release train and asked that the first change be provenance classification plus a write-free dry-run, with dashboard work third. A standalone warning-string change now would land ahead of the classifier it depends on, since the hint's trigger conditions are exactly the provenance evidence that does not yet exist. When the classifier lands, this hint becomes a natural part of it rather than a separate surface.
The maintainer decision points from that review are still open and unchanged: whether managed scope stays limited to verified standalone and global installations, whether version-manager rejection is widened for updates while the shim-recovery helper keeps its current behavior, whether plan and job state live outside the configuration file, and whether the three phases are accepted now or after the current train. Your report does not change those questions, but it does remove any doubt that the npm-global case must be in scope.
luvs01 commentedon Sep 10, 2026
Correction: the classifier already shipped, and this is its declared Windows boundary
My previous comment got a material fact wrong, so I want to correct it before it misleads anyone reading this thread.
I wrote that the hint's trigger conditions "are exactly the provenance evidence that does not yet exist." That is not true. #2818 landed on 2026-08-31 as
d5c1abe99, addingsrc/codex/cli-install-provenance.tsandsrc/cli/codex-cli-update.ts— the candidate-only provenance check that the review asked for as the first change. It merged three days before @hhhappb's report, which means thecandidateAvailable: false,provenance: "unknown",managed: falseoutput in that report is the shipped classifier's own result, not the absence of one.That result is the declared PR1 boundary rather than a classification defect. In
cli-install-provenance.ts,readPersistedCandidate()returnsnullimmediately when the platform iswin32, andresolveCandidateCommandPath()does the same. The comments state the reason plainly: a pathname-only Windows read cannot prove a writable ancestor stayed local and non-reparse between validation and open, so the first slice accepts only the proof-capturedCODEX_CLI_PATHenvironment candidate and leaves the rest to a later handle-bound Windows provenance layer. Refusing to open a candidate-controlled Windows pathname is the correct conservative choice, and I would not want it relaxed.What the report does expose is a reporting gap, and it is narrower than I described.
resolveCodexRuntime()derivessource: "configured"from the persisted runtime state file, and @hhhappb'scodexRuntimeblock shows exactly that source with a known version. The provenance inspector declines to read that same persisted state on Windows, so it finds no candidate at all and falls through toreason: "candidate_unavailable". The operator is therefore told that no manageable candidate exists, when the truth is that OpenCodex has a perfectly well-identified candidate and is deliberately declining to inspect it on this platform. The vocabulary for the accurate answer already exists:windows_inspection_deferredis a definedCodexCliInstallReason, but it is only reachable whenCODEX_CLI_PATHis set.So the acceptance criterion I proposed should be restated. Rather than "npm-global on Windows must classify as managed", which would require the handle-bound layer to exist first, the immediate criterion is: when a persisted candidate is present but Windows inspection is deferred, report the deferral instead of claiming the candidate is unavailable. Reading OpenCodex's own bounded state file inside its own config directory is a different operation from opening a candidate-controlled path, so the deferral can be reported honestly without weakening the boundary and without resolving or opening the candidate binary.
That leaves two separable follow-ups, in this order:
managedand make the recovery hint's trigger conditions evaluable.The rest of my earlier comment stands: the shim-restoration ordering @hhhappb verified is the behaviour the apply phase must automate,
managedis stillfalsein every branch today so no apply engine exists yet, and the maintainer decisions listed in the review — managed scope, version-manager rejection breadth, plan/job state location, and phase timing — are all still open.@lidge-jun if you would like item 1 as a self-contained PR I will open it against current
dev; it touches neither the GUI nor any credential path, so it does not depend on the screenshot gate that is currently holding other work.luvs01 commentedon Sep 10, 2026
The implementation is now tracked in #4178. Its final scope preserves zero Windows candidate/configuration filesystem I/O: it changes the no-observed-candidate reason to
windows_inspection_deferred, without reading persisted state.candidateAvailable: falsestill means the inspection obtained no candidate evidence; it does not establish installation absence. The earlier suggestion to inspect OpenCodex's persisted state was not adopted, because that path is part of the same deferred Windows filesystem boundary.#2818 already provides the candidate-only check. #4178 addresses this diagnostic only; Windows handle-bound inspection, installation attestation and a safe apply workflow remain separate work, so this issue stays open.
luvs01 commentedon Sep 16, 2026
Maintainer decision requested: scope of the next #2811 implementation PR
@lidge-jun, would you accept installation attestation as a separate prerequisite PR, followed by the plan/apply PR, or would you prefer one combined PR that completes attestation and apply together?
I recommend the separate prerequisite PR, with this concrete scope:
The subsequent apply PR would consume that same identity, bind the approved artifact integrity through installation and readback, and cover all processes using the selected CLI—including TUI and
codex exec—with an explicit check-to-replacement race contract. It would need an isolated end-to-end installation test and the required security review.The reason for splitting is concrete: the current inspector deliberately returns
managed:falseandselectionAttested:false, so the prepared apply prototype's positive tests currently require injected evidence. #4178 only improves the deferral diagnostic; its sponsorship does not cover this broader capability.Is this two-PR split and Windows-first prerequisite scope acceptable, or should I prepare the combined implementation instead? I am requesting the implementation/review boundary here, not approval to enable the unverified prototype.
luvs01 commentedon Sep 17, 2026
Draft PR #4910 implements the attestation prerequisite scope proposed above: bare ocx system codex-cli-update attest now identifies the selected npm-global Codex CLI from the proof-bound launcher snapshot (configured CODEX_CLI_PATH or the first codex on the captured PATH), resolving an OpenCodex wrapper to its renamed codex.opencodex-real.cmd npm backing. The explicit four-path form remains as an all-or-none override; discovery only proposes paths and the held-handle observation remains the authority. Read-only throughout — no installer execution, process control, or dashboard work. The apply phase remains separate follow-up, so the issue stays open.
feat(codex): attest selected CLI installation safely (#4978)
luvs01 commentedon Sep 18, 2026
Draft PR #5016 implements the phase-2 plan/apply scope on top of the landed attestation slice (#4978):
plandry-runs an update and prints an evidence-bound plan id,apply --plan <id>installs exactly the bound version after re-verifying live evidence, andcheck/attestremain read-only. The parser is the authorization boundary - apply is unreachable without a plan id from a dry-run, and no plan state persists on disk.luvs01 commentedon Sep 19, 2026
Update: PR #5016 is now marked ready for review. Since the earlier draft note, the unreachable shim-repair lane was removed (a matched shim can never classify as npm-global under the phase-1 contract, so apply stays fail-closed on shimmed installs), and all quality gates are green on the current head.
lidge-jun commentedon Sep 27, 2026
Release train 4 triage: please keep this issue open as design work. #5016 was closed because its plan required managed:true, which the production inspector never reported, so its apply path was reachable only with synthetic evidence. Please establish a reachable, proven installation-provenance predicate and a read-only plan against that predicate before proposing any apply step.
feat(codex): attest the selected npm-global CLI and add a read-only u…
luvs01 commentedon Sep 28, 2026
The predicate and read-only plan the 9/27 triage asks for are implemented in #6152.
Review feedback has been iterating on the evidence binding; the current head addresses the reported P2s.
Ingwannu commentedon Sep 28, 2026
Maintainer status correction for the issue record: #6152 at exact head d05a5d9 remains CHANGES_REQUESTED. The read-only direction is sound, but Node output-path channels (NODE_V8_COVERAGE and NODE_REDIRECT_WARNINGS) can still write outside the temporary root, and the absent-PATH/real-npm regressions do not exercise the production boundary they claim. The provenance/read-only-plan phase is therefore not accepted yet; fix those exact findings and rerun current-head CI before treating this issue phase as complete. No security scan was run.
luvs01 commentedon Sep 28, 2026
Noted - that review covered the older head d05a5d9. The current head aba8580 already addresses those exact findings:
Current-head CI is green and a re-review has been requested on the PR.