mirror of
https://github.com/openai/codex.git
synced 2026-09-11 20:36:49 +00:00
## Why Managed app-server shutdowns use a fixed 60-second grace period. Allow users to adjust how long shutdown waits before forcing the process to exit. ## What changed - Add `shutdownGraceSeconds` to daemon settings, accepting integers from 0 through 300 and defaulting to 60. Zero requests graceful shutdown and then forces termination immediately. - Apply the setting to managed app-server stops and restarts, including updater-triggered restarts, and extend the lifecycle lock timeout to accommodate the maximum grace period. - Keep `stop` usable with unreadable or invalid settings by falling back to the default grace period. ## Testing Add tests for setting defaults and bounds, invalid-value fallback, preservation when saving remote-control settings, graceful process exit, and forced termination with zero or finite grace periods. GitOrigin-RevId: 8627b391323cf27b40df96935a82e66aa715f572
180 lines
9.1 KiB
Markdown
180 lines
9.1 KiB
Markdown
# codex-app-server-daemon
|
|
|
|
> `codex-app-server-daemon` is experimental and its lifecycle contract may
|
|
> change while the remote-management flow is still being developed.
|
|
|
|
`codex-app-server-daemon` backs the machine-readable `codex app-server`
|
|
lifecycle commands used by remote clients such as the desktop and mobile apps.
|
|
It is intended for Codex instances launched over SSH, including fresh developer
|
|
machines that should expose app-server with `remote_control` enabled.
|
|
|
|
## Platform support
|
|
|
|
The daemon supports Linux, macOS, and Windows using platform-specific process
|
|
and file-locking primitives. Windows startup requires a non-elevated terminal
|
|
whose host permits detached child processes.
|
|
|
|
Windows automatic attachment requires the canonical socket address to fit the
|
|
108-byte AF_UNIX limit (including its terminator). A short junction alias whose
|
|
resolved address exceeds that limit falls back to the embedded server. Use a
|
|
shorter `CODEX_HOME` to share the daemon; discovery does not trust a mutable alias.
|
|
|
|
Shared clients use the environment inherited when the daemon started. Opening a
|
|
new terminal or clearing variables there does not clear the running daemon's
|
|
environment; per-client environment isolation is not provided.
|
|
An invocation that sets `CODEX_EXEC_SERVER_URL` skips implicit daemon attachment
|
|
so its executor selection is preserved. If an implicitly discovered daemon cannot
|
|
initialize the connection, the TUI starts an embedded server instead. Explicit
|
|
`--remote` endpoints remain authoritative and report connection failures.
|
|
|
|
## Commands
|
|
|
|
```sh
|
|
codex app-server daemon start
|
|
codex app-server daemon restart
|
|
codex app-server daemon update
|
|
codex app-server daemon enable-remote-control
|
|
codex app-server daemon disable-remote-control
|
|
codex app-server daemon stop
|
|
codex app-server daemon version
|
|
codex app-server daemon bootstrap --remote-control
|
|
```
|
|
|
|
On success, every command writes exactly one JSON object to stdout. Consumers
|
|
should parse that JSON rather than relying on human-readable text. Lifecycle
|
|
responses report the resolved backend, socket path, local CLI version, and
|
|
running app-server version when applicable.
|
|
|
|
Standalone-managed daemons check for updates after five minutes, then hourly by
|
|
default. Edit `CODEX_HOME/app-server-daemon/settings.json` to change this:
|
|
|
|
```json
|
|
{"remoteControlEnabled": false,
|
|
"shutdownGraceSeconds": 60,
|
|
"updater": {"autoUpdateEnabled": false, "updateIntervalMinutes": 120}}
|
|
```
|
|
|
|
Positive minute intervals have no configured cap. `daemon restart` applies the
|
|
enabled state; the next updater wait reads a new interval. The preference does
|
|
not affect an explicit `codex update` command or `daemon update`.
|
|
|
|
`daemon update` checks the latest stable release once, even with automatic
|
|
updates disabled. It requires a Codex installer-owned latest-channel standalone
|
|
install. JSON reports `updated`, `noUpdate`, or `unsupported`, with installed
|
|
and running versions. The updater owns scheduled and manual installs. A manual
|
|
update restarts a running managed daemon, so active or queued work may be
|
|
interrupted.
|
|
Accepted updates continue if the CLI exits. Installer errors return nonzero.
|
|
The updater uses saved network settings; CLI `-c` overrides do not reach it.
|
|
|
|
For all managed app-server shutdowns, including explicit stop and restart and
|
|
updater-triggered restarts, `shutdownGraceSeconds` defaults to 60 and accepts
|
|
an integer from 0 through 300. Zero forces shutdown immediately after requesting
|
|
a graceful exit; the five-minute maximum bounds the wait even if a turn is still
|
|
running.
|
|
|
|
## Bootstrap flow
|
|
|
|
For a new Linux or macOS machine:
|
|
|
|
```sh
|
|
curl -fsSL https://chatgpt.com/codex/install.sh | sh
|
|
$HOME/.codex/packages/standalone/current/codex app-server daemon bootstrap --remote-control
|
|
```
|
|
|
|
On Windows, use a non-elevated PowerShell terminal whose host allows breakaway:
|
|
|
|
```powershell
|
|
irm https://chatgpt.com/codex/install.ps1 | iex
|
|
$codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $HOME '.codex' }
|
|
& "$codexHome\packages\standalone\current\bin\codex.exe" app-server daemon bootstrap --remote-control
|
|
```
|
|
|
|
`bootstrap` requires the standalone managed install. It records the daemon
|
|
settings under `CODEX_HOME/app-server-daemon/`, starts app-server as a
|
|
pidfile-backed detached process. It launches a detached updater loop when
|
|
automatic updates are enabled, the installer selected the stable `latest`
|
|
channel, and the managed binary supports the updater command.
|
|
|
|
## Installation and update cases
|
|
|
|
The daemon uses the standalone installer (`install.sh` on Unix, `install.ps1`
|
|
on Windows) and its managed binary under `CODEX_HOME/packages/standalone/current`:
|
|
`bin/codex` or `bin/codex.exe`, falling back to the legacy flat layout when present.
|
|
|
|
| Situation | What starts | Does this daemon fetch new binaries? | Does a running app-server eventually move to a newer binary on its own? |
|
|
| --- | --- | --- | --- |
|
|
| Latest-channel installer has run; `start` or `bootstrap` is used with automatic updates enabled | Managed binary and detached updater when supported | When supported, the platform's installer runs on the configured cadence. | When supported, the running server restarts with the new binary before the updater replaces itself. |
|
|
| Installer selected an explicit release; `bootstrap` is used | Managed binary only | No; the selected release stays pinned. | No; an explicit restart uses the selected binary. |
|
|
| Another tool updates the managed binary | A fresh start or explicit restart uses it; a running server is reused. | Yes, when a latest-channel updater is running, on the configured cadence. | An updater that was running through the change compares binary contents on its next successful installer pass and refreshes the server first. |
|
|
|
|
### Standalone installs
|
|
|
|
For installs created by either platform's standalone installer:
|
|
|
|
- lifecycle commands always use the standalone managed binary path
|
|
- `bootstrap` is supported
|
|
- managed `start`, `restart`, and `bootstrap` ensure a single detached pid-backed
|
|
updater loop only when automatic updates are enabled for a stable latest-channel
|
|
release whose managed binary supports the updater command
|
|
- the installer records the latest-channel selection alongside `current`;
|
|
selecting an explicit release clears it, even if that version is currently
|
|
latest. The updater checks the selection again while holding the install lock
|
|
so an in-flight update cannot override a new pin
|
|
- installs made before the installer recorded channel selections need one new
|
|
`latest` installation to opt into automatic updates; until then the daemon
|
|
continues to serve app-server without updating the selected release
|
|
- after a successful refresh, if app-server is running and the managed binary
|
|
contents changed, the updater restarts app-server with that binary first and
|
|
only then replaces its own process image
|
|
- the updater loop is not reboot-persistent; a managed start after reboot
|
|
starts it again
|
|
|
|
### Out-of-band updates
|
|
|
|
This daemon does not watch arbitrary executable files for replacement. If some
|
|
other tool updates the managed binary path:
|
|
|
|
- an updater that was already running notices a changed managed
|
|
binary on its next successful scheduled installer pass; if
|
|
app-server is running, it refreshes app-server first and then refreshes itself
|
|
once that replacement starts successfully
|
|
- if the updater was absent during a same-version binary replacement, a later
|
|
managed start recovers it but cannot infer the running server's previous
|
|
executable identity; use `codex app-server daemon restart` to refresh the server
|
|
|
|
## Lifecycle semantics
|
|
|
|
`start` is idempotent and returns after app-server is ready to answer the normal
|
|
JSON-RPC initialize handshake on the Unix control socket.
|
|
|
|
`restart` stops any managed daemon and starts it again.
|
|
|
|
`enable-remote-control` and `disable-remote-control` persist the launch setting
|
|
for future starts. If a managed app-server is already running, they restart it
|
|
so the new setting takes effect immediately.
|
|
|
|
Top-level `codex remote-control start` enables and persists remote control for
|
|
the managed daemon, overriding a saved disabled value. It starts or bootstraps
|
|
the daemon as needed. Plain `codex remote-control` runs a separate foreground
|
|
server and does not change daemon settings; `codex remote-control stop` stops
|
|
the managed daemon without clearing its saved remote-control preference.
|
|
`daemon start` and `daemon restart` use that saved preference. `daemon bootstrap`
|
|
sets it according to `--remote-control` (disabled when omitted).
|
|
|
|
`stop` sends a graceful termination request first, then force-terminates the
|
|
process after the configured grace window if it is still alive.
|
|
|
|
All mutating lifecycle commands are serialized per `CODEX_HOME`, so a concurrent
|
|
`start`, `restart`, `enable-remote-control`, `disable-remote-control`, `stop`,
|
|
or `bootstrap` does not race another in-flight lifecycle operation.
|
|
|
|
## State
|
|
|
|
The daemon stores its local state under `CODEX_HOME/app-server-daemon/`:
|
|
|
|
- `settings.json` for remote-control launch settings and updater preferences
|
|
- `app-server.pid` for the app-server process record
|
|
- `app-server-updater.pid` for the pid-backed standalone updater loop
|
|
- `daemon.lock` for daemon-wide lifecycle serialization
|