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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions docs/launcher-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,16 @@ runs with stdout/stderr detached (`>/dev/null 2>&1`), which cut the
caller pipe — an orphaned `sleep` holding an inherited fd blocks command
substitution until it exits.

The default preflight remains asynchronous; `CODEX_SYNC_CLI_PREFLIGHT=1` still
opts into the existing synchronous check while preserving fail-soft behavior for
a CLI that is not known broken. A detected npm-managed CLI missing
The default update/version preflight remains asynchronous, but every generated
launcher first runs its bundled synchronous, execution-free trust check. This
applies to native packages with or without the updater, AppImage, Nix, and
user-local installs, without depending on the version of any system updater. A
managed standalone CLI that fails this check blocks startup before any CLI
version probe; a successful check pins launch to the canonical verified release
binary so replacement of its visible symlink cannot redirect execution.
`CODEX_SYNC_CLI_PREFLIGHT=1` still opts into the full synchronous check while
preserving fail-soft behavior for a CLI that is not known broken. A detected
npm-managed CLI missing
`@openai/codex-linux-x64` or `@openai/codex-linux-arm64` is the blocking
exception: Electron cannot use that CLI, so the launcher waits for one repair
attempt. The updater's initial and post-repair CLI version probes are each
Expand Down
67 changes: 67 additions & 0 deletions docs/updater.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,73 @@ installs continue to update through npm, while official standalone installs
under `~/.codex/packages/standalone` are updated with the official standalone
installer instead of being replaced through npm.

The updater scopes permission hardening to the official standalone installer
process. New managed releases use the caller's existing umask plus the
group/world write restrictions from `0022`; stricter policies such as `0027`
and `0077` remain intact, and the launcher, Electron, app-server, hooks, and
unrelated child processes keep the caller's original mask.

Before executing a managed standalone CLI, the updater verifies that its tree
and canonical parent chain are owned by the current user or root and are not
group/world-writable (apart from root-owned sticky directories such as
`/tmp`). Every generated launcher performs the same trust-only check through a
bundled helper before any CLI version probe, including AppImage and native
packages built without the updater. The helper executes the returned canonical
release binary rather than a replaceable visible symlink and does not depend on
an installed updater version. An unsafe tree is rejected without
executing it, changing its modes, or deleting it; updater state records a
failed preflight with clean-reinstall guidance.

To recover, stop any active updater or Codex installer, remove the rejected
`~/.codex/packages/standalone` tree, and run:

```bash
codex-update-manager recover-standalone-cli --print-path
```

If the standalone installer link belongs in a non-default directory, add
`--install-dir /absolute/path/to/bin`. If the recorded standalone home is not
the default `~/.codex`, also add `--codex-home /absolute/path/to/codex-home`.
Recovery refuses to overwrite any
existing standalone tree. It downloads the official installer and runs only
that child with the caller's umask plus the `0022` write restrictions, so the
flow remains safe even when the desktop session uses `umask 0002`. Before the
download, recovery removes group/world write access from existing
current-user-owned directories below `$HOME` along both installer paths (for
example `.codex`, `packages`, `.local`, and `bin`) and rejects symlinks,
untrusted ownership, or unsafe ancestors it cannot safely narrow. Automatic
standalone updates reject an unsafe visible-command directory before
downloading or spawning the installer. Both update and recovery resolve the
installer shell, downloader, and child commands only from root-controlled
system tool directories; they never reuse programs already present in a
formerly writable user directory. Do not run the official installer directly
for this recovery: it would inherit the ambient mask. Removing write bits from
the rejected standalone tree or its command directory is not sufficient because
their contents may already have been modified.

AppImage, Nix, and native packages built without the updater do not provide the
recovery command. On those formats, either temporarily install an updater-enabled
native package and use the command above, or remove the rejected standalone tree
and reinstall the CLI from a verified package channel into a fresh path whose
files and canonical ancestors are owned by the current user or root and are not
group/world-writable. Do not reuse the rejected tree, and do not rerun the
standalone installer under an ambient `0002` mask.

An npm CLI whose canonical executable or ancestors are already group/world-
writable is also rejected because its pathname cannot be pinned safely against
replacement. Do not reinstall into that existing prefix. Remove the rejected
Codex package tree, then use a fresh dedicated prefix such as
`(umask 0022; npm i -g --include=optional --prefix ~/.codex-cli-npm
@openai/codex)`; the launcher and updater both discover that prefix.

After a managed standalone tree is first detected, launcher and updater trust
checks record its home in `~/.codex-standalone-provenance`, outside the managed
tree. That durable record keeps standalone provenance active even if the tree or
visible command has already been replaced. To switch installation channels
intentionally, remove both the standalone tree and this provenance file;
otherwise an external replacement is rejected rather than silently reclassified
as npm- or system-managed.

System-package-managed CLI installs are reused but not mutated through npm or
the standalone installer flow. On Arch-like hosts, when the resolved CLI lives
under a system bin directory and `pacman -Qo` confirms package ownership, the
Expand Down
1 change: 1 addition & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ SCRIPT

chmod +x "$INSTALL_DIR/start.sh"
mkdir -p "$INSTALL_DIR/.codex-linux"
cp "$SCRIPT_DIR/launcher/cli-launch-path.py" "$INSTALL_DIR/.codex-linux/cli-launch-path.py"
cp "$SCRIPT_DIR/launcher/webview-server.py" "$INSTALL_DIR/.codex-linux/webview-server.py"
local linux_icon_source="$LINUX_ICON_SOURCE"
[ -f "$linux_icon_source" ] || linux_icon_source="$ICON_SOURCE"
Expand Down
Loading
Loading