02 / Blog · 19.08.2026

A forced-command SSH boundary for a Codex worker

Why the Homepage worker reaches Codex only through aidev on the AI CLI server without turning SSH into general remote access.

A structured job passes through a narrow locked SSH channel while shell access and unrelated files remain blocked.

The account is part of the architecture

A remote AI worker needs a runtime and a valid Codex session. The convenient solution would be to copy authentication into the web container or use a normal SSH login. Both make the boundary much larger than necessary. A web container should not own a Codex home, and it should not be able to execute arbitrary commands on another server.

The Homepage therefore uses one fixed endpoint: aidev@5.45.99.175 on the AI CLI server. This is not merely a configurable default; it is a checked contract. The client rejects other targets, pins the host key, and disables forwarding. Staging and production use separate keys, but both arrive at the same narrow protocol.

This separation also avoids a subtle operational mistake. A valid session for a web or GUI account is not automatically the correct runtime identity. Codex runs where aidev is deliberately authenticated. The authentication file, session history, and full Codex home are never copied into the application, image, or web server.

SSH without a selectable command

An SSH key normally grants more than this job requires. Its authorized_keys entries therefore force a single wrapper. The original command sent by the client is never evaluated by a shell. The wrapper parses only a tiny protocol: mode, permitted web-search state, and timeout. A wrong field count, unknown mode, or excessive limit fails immediately.

The run stays constrained after that check. Structured work uses a read-only sandbox. Image work receives its own temporary directory. Shell, browser, plugins, apps, multi-agent features, and other unrelated tools are disabled. A global lock prevents concurrent runs, request and response sizes are bounded, and a timeout terminates stalled work.

SSH consequently stops being general remote access. The application key can neither open an interactive shell nor choose a different command. It can hand one bounded job to the intended runner.

The result crosses the same narrow boundary

The restriction works in both directions. Structured results must satisfy the strict JSON Schema sent with the request. For image work, the wrapper accepts a file only from the temporary workspace or intended output directory, only in a supported bitmap format, and only below the size limit. Resolved paths prevent a declared file from reading elsewhere. The Homepage validates the returned payload again before TYPO3 persists anything.

Errors are intentionally coarse at the boundary. Invalid authentication, rate limiting, and a timeout are operationally useful states. Internal error-log content and authentication details are not useful to the calling container.

Provisioning is not deployment

A dedicated Ansible playbook provisions the AI boundary. It verifies the aidev account and Codex login, the private runtime directory, the wrapper, and the allowed public keys. Normal staging and production deployments do not modify the AI CLI server. They deliver only the matching private key as a Docker secret and run a real strict-schema probe.

This keeps responsibilities small. Provisioning manages the trust endpoint. Deployment proves that its environment identity can reach that endpoint. The worker executes only the defined protocol. None of those layers needs to be as powerful as all three combined.