Codex App Server is a protocol layer on top of which you build your own clients. If the SDK gives a ready interface for a language, App Server is a level below: the protocol itself, which can be connected to from anything. It is available via JSONL over stdio, WebSocket and a Unix socket. An important caveat from the start: the documentation marks the command as experimental, so a generated client must be version-aware - account for the protocol version rather than relying on its immutability.
App Server's transports cover different connection scenarios. It helps to see them side by side once. Below are launching with JSONL over stdio by default, a local WebSocket on 127.0.0.1 and a Unix socket. stdio is handy for a child process, a Unix socket for local inter-process communication, WebSocket for a network client. The choice of transport is a choice of how the client connects, and each has its own area of use and its own security boundary.
The WebSocket transport opens the network, and here runs a serious risk boundary. A non-local WebSocket listener without authentication is an open door: anyone who reaches the port gets access to execution. A local listener on 127.0.0.1 is limited to the machine, but a non-local one requires protection without exception. This is not a configuration detail but the question of who can connect to your App Server at all and, through it, perform actions.
It helps to see a secure launch for remote clients once. Below is a WebSocket with --ws-auth capability-token and --ws-token-file pointing to a token file in a secure path. The bundle is simple: the listener requires a token, the token lies in a file with restricted access, not in the command line or a log. For any non-local client this is the minimum: a non-local listener must not be raised without authentication, and the token is kept where it cannot be carried off.
The client lifecycle is what separates a toy prototype from a production client. It helps to understand it in full. A production client must correctly handle initialization, thread creation and resume, turn start and cancel, streaming items, approvals and tool calls. Each of these stages is not an optional detail but part of the contract: a client that cannot cancel a turn or handle an approval will behave unpredictably exactly when it matters most.
Streaming and approvals in the lifecycle require special attention. Streaming items mean the result comes gradually, and the client must be able to accumulate and display them rather than wait for one final message. Approvals mean execution may stop and ask for a decision - and the client must be able to request and pass that decision. A client that ignores approvals will either hang on the request or, worse, bypass a point of control the protocol provided deliberately.
The point of App Server is to give full control over the client where the SDK is not enough. It is the choice for those building their own interface to Codex: their own UI, their own integration, their own protocol bridge. For full control you pay with responsibility: version-awareness because of the experimental status, transport authentication, correct handling of the whole lifecycle. App Server is a foundation for a client, not a ready client, and it is worth building on understanding that the protocol contract will have to be observed in full.
The typical failures around App Server are predictable. Generating a client that does not account for the version and breaking on a change of the experimental protocol. Raising a non-local WebSocket listener without authentication and opening execution to the network. Keeping the token in the command line or a log instead of a secure file. And handling not the whole lifecycle, ignoring turn cancel or approvals. Make the client version-aware, protect non-local transports with a token from a file and implement the whole lifecycle contract, not its convenient part.
# Default JSONL over stdio
codex app-server --listen stdio://
# Local WebSocket
codex app-server --listen ws://127.0.0.1:4500
# Default Unix socket
codex app-server --listen unix://
# the command is marked experimental - the client must be version-aware# A secure WebSocket for remote clients
codex app-server \
--listen ws://127.0.0.1:4500 \
--ws-auth capability-token \
--ws-token-file /secure/path/app-server.token
# a non-local listener without auth is a serious risk boundary
# lifecycle: init, thread create/resume, turn start/cancel, streaming, approvals, tool calls