Skip to content
Back to skills

Codex Runtime

BSecurity

Contract of the codex bridge script — codex-run.sh subcommands and options, background jobs, Codex session resume, permission modes, timeouts, job states, exit codes, failure modes. Internal reference attached to the codex-runner subagent; read it when any codex-* skill needs the exact call.

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 7, 2026
ai-agentsrustgobashgit

Works with

  • terminal
  • cli

Security analysis

B75/100
  • criticalPipes output to a shell interpreter

Pro shows the line behind each finding and how to fix it

Scanned October 7, 2026

npx -y skills add dmitry-fomin/listik --skill codex-runtime --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Codex Runtime?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Codex Runtime
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/dmitry-fomin-codex-runtime/badge)](https://www.skillsdirectory.com/skills/dmitry-fomin-codex-runtime)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: codex-runtime
description: "Contract of the codex bridge script — codex-run.sh subcommands and options, background jobs, Codex session resume, permission modes, timeouts, job states, exit codes, failure modes. Internal reference attached to the codex-runner subagent; read it when any codex-* skill needs the exact call."
user-invocable: false
allowed-tools: Bash(${CLAUDE_PLUGIN_ROOT}/scripts/codex-run.sh *)
---

The only way to call OpenAI Codex CLI is `${CLAUDE_PLUGIN_ROOT}/scripts/codex-run.sh`.
Calling bare `codex exec` loses the default permission mode, job bookkeeping and the exit
code contract.

**Invariant:** stdout of `run`/`resume` (without `--background`) and of `result` is exactly
codex's final answer and nothing else — it comes from `-o/--output-last-message`, not from
the process output. Everything else goes to stderr, so text on stderr is always a problem
signal.

## Commands

```bash
${CLAUDE_PLUGIN_ROOT}/scripts/codex-run.sh run --background --label "<topic>" <<'TASK'
<task text>
TASK
```

| Subcommand | Purpose |
| --- | --- |
| `run` | new session; prompt on stdin. With `--background` stdout is a job-id, without it the answer |
| `resume <job-id>` | continue that job's Codex session (`codex exec resume`); prompt on stdin. Inherits the original permission mode, `--model`, `--effort`, `--cwd`; takes `--background`, `--timeout`, `--label`, and `--permission`/`--write` to override the inherited mode (a read-only run can be continued with write access) |
| `check [--json]` | readiness: binary, `codex doctor`, active provider/model, credentials, jobs in flight |
| `status [--json] [--all] [--running] [job-id]` | no argument — jobs of the current cwd subtree; `--all` — every job on the machine; with an id — one job card |
| `result <job-id> [--wait [s]]` | fetch the answer; `--wait` waits the given seconds (default 300) |
| `logs <job-id> [--tail N]` | codex's own output as it runs: reasoning, tool calls, and how much answer accumulated |
| `cancel <job-id\|--all>` | kill the job and its whole process tree |
| `clean [--older-than <days>] [--all]` | drop finished jobs; never touches running ones |
| `transcript [job-id]` | print the Codex session JSONL — what the conversation actually was |

Every subcommand takes `-h`.

Options for `run`:

| Option | Default | Meaning |
| --- | --- | --- |
| `--background` | off | detach, return a job-id instead of the answer |
| `--label <text>` | none | short tag; the only way jobs differ on sight in `status` |
| `--permission <read\|bash\|write>` | `read` | permission mode in one flag |
| `--write` | off | alias for `--permission write`, kept for Listik routes and pipeline presets |
| `--cwd <dir>` | current | working directory of the run and the sandbox boundary (`-C`); in a git worktree or main checkout `write` also opens a narrow set of git paths (see Permissions) |
| `--timeout <s>` | 540 foreground, 7200 background | `0` removes the limit |
| `--model <id>` | user's `~/.codex/config.toml` | full model id (`-m`) |
| `--effort <level>` | user's setting | `model_reasoning_effort`; not validated — valid values are model-dependent |
| `--provider <route>` | user's setting | `[model_providers.<route>]` from the config; manual use only |

## Permissions

codex has no per-tool allowlist: its boundary is the filesystem sandbox passed as `-s`.

| `--permission` | Sandbox | Meaning |
| --- | --- | --- |
| `read` | `read-only` | default; commands still run, writes are denied |
| `bash` | `read-only` | accepted for parity with the other bridges — the same sandbox as `read`, because running commands is already allowed there |
| `write` | `workspace-write` | file edits inside `--cwd` |

**`write` in a git checkout.** codex keeps the `.git` of the working directory read-only
under `workspace-write`, so the bridge opens a narrow set of git paths with `--add-dir`
(one each, passed before `resume` too) and `git add`/`git commit` work:

- **linked worktree** (`--cwd` anywhere inside it) — the worktree's own git dir
  (`<common>/worktrees/<name>`) and the shared `objects`, `refs` and `logs` of the common
  git dir;
- **main checkout** (`--cwd` anywhere inside it, git dir `<top>/.git`) — the directories
  `.git/objects`, `.git/refs`, `.git/logs` and the files `.git/index`, `.git/index.lock`,
  `.git/HEAD`, `.git/HEAD.lock`, `.git/COMMIT_EDITMSG` (the lock files are passed even
  though they do not exist before the commit). Verified live on macOS with codex-cli 0.154.0.

The whole git dir, `hooks` and `config` stay closed in both cases — a hook written from the
sandbox would later run outside it. A submodule (git dir under `<super>/.git/modules/`) gets
no extra paths. Each commit still prints a harmless
`error: Unable to create '.../packed-refs.lock': Operation not permitted` and exits 0;
`git pack-refs`, `git gc` and deleting a packed branch do not work inside the sandbox.

Add `write` only when the human asked for a change in this message; never infer it from a
task merely looking like implementation. A background write job keeps editing files while
you do other things, so launch one only when the human knows it is running.

## Non-obvious rules

- **Background is the default choice.** `codex exec` blocks until the agent finishes, and a
  subsystem sweep easily outlives the 600 s Bash-call ceiling. Foreground is for questions
  answered inside the current turn. The background is entirely this script's doing (own
  process group, detached worker, meta file) — codex knows nothing about it.
- **There is no approval channel in a headless run.** An attempt to escalate rights from
  inside the task fails instead of prompting; rerun with `--permission write`.
- **Secrets are a prompt-side concern.** codex opens files on its own, so scope the task to
  the files it needs and explicitly forbid `.env`, `*.key`, `*.pem`, `credentials.json`.
- **Model, provider and effort are the human's choice.** A run goes on the settings in
  `~/.codex/config.toml`; `check` reports them as diagnostics, not as an invitation to
  switch. The one exception is a call from a feature-pipeline preset, which must pass the
  `--model` and `--effort` it requires, because model-per-role is part of the preset
  (rationale in `plugins/feature-pipeline/references/ROLES.md`). Never add `--provider`
  for that exception.
- **Parallel runs are supported**, including in one working directory — separate sessions,
  separate job directories, no shared lock. Exception: two `--permission write` runs in the
  same directory overwrite each other's edits, so keep writes to one job per directory.
- **Don't poll in a foreground Bash call.** Wait with one backgrounded call instead:

  ```bash
  until ! ${CLAUDE_PLUGIN_ROOT}/scripts/codex-run.sh status <job-id> | grep -q '^actual_status=running'; do sleep 20; done
  ```

  Its completion notification is the "codex finished" signal. When the answer is due within
  a minute or two, `result <job-id> --wait 120` is the short alternative.

## Job states

| Status | Meaning |
| --- | --- |
| `running` | process alive, codex working |
| `completed` | answer ready, fetch with `result` |
| `timeout` | hit its limit; `result` still returns the partial answer |
| `canceled` | killed via `cancel` |
| `failed` | codex exited with an error; cause in `logs` |
| `orphaned` | process gone, outcome never written: reboot or `kill -9`. No answer is coming |

`status <id>` prints the status twice: `status=` is what the worker recorded,
`actual_status=` corrects it for process liveness. **Trust `actual_status`.**

## Resuming a Codex session

The Codex session id (a UUID) is written to the job's meta as `codex_session` and shown by
`status`/`status --json` once codex has flushed the rollout file — that is, after the job
finished.

```bash
${CLAUDE_PLUGIN_ROOT}/scripts/codex-run.sh resume <job-id> --background --label "<follow-up>" <<'TASK'
<follow-up text: the author's answers, or the red acceptance items verbatim>
TASK
```

The new job gets its own job-id; `codex_session` stays the same and `resumed_from` points
at the old id. The permission mode is inherited unless `--permission`/`--write` overrides it, so a
read-only run can be continued with write access. Exit 2 — no session id, the job is still `running`, or codex could not find
the session — means **fall back to a fresh `run`** with the current task text. Don't invent
another syntax.

## How a run is wired

- codex reads `AGENTS.md`/`CLAUDE.md` in the working directory itself — never restate
  project rules in the task.
- The prompt goes to stdin of `codex exec`, so this bridge sets no size limit on it.
- The answer is the file behind `-o/--output-last-message`: exactly the agent's final
  message. Reasoning and tool calls stay in codex's own output, visible through `logs`;
  the full session is `transcript`.
- Job state (prompt, codex's output, answer) is stored in plaintext in the state directory
  and never expires; `clean` removes it.
- `transcript` matches the session file by working directory and, given a job-id, by the
  job's time window — without a job-id it may return your own interactive codex session and
  says so on stderr.

## Exit codes

| Code | Meaning | Action |
| --- | --- | --- |
| 0 | success: answer, job-id or report on stdout | pass it through verbatim |
| 1 | `check`: codex not ready; `status`: no jobs | an answer, not a failure |
| 2 | bad call: missing binary, empty prompt, bad option, unknown job-id, no session to resume | fix the command, or fall back to `run` |
| 5 | background job still running | wait and retry `result` |
| 6 | timeout, cancel, non-zero codex exit or empty answer | check `logs`, then `check` |

## Failure modes

| Symptom | Cause / action |
| --- | --- |
| Bash call cut at 600 s | run was started in foreground; restart with `--background` |
| `codex not found in PATH` | not installed, or installed after the session started — new terminal or `CODEX_BIN` |
| `empty answer - check readiness` | no auth, or the answer was blocked: run `check`, then rephrase |
| job asks for approval and fails | a write attempt in read-only mode; rerun with `--permission write` if edits were actually requested |
| status `orphaned` | the worker died with the machine or a `kill -9`; no answer is coming, relaunch |
| answer looks invented | check `transcript` for whether files were read at all |
| `transcript` finds no session | the run used a different `CODEX_HOME`, or has not finished yet |

## Red lines (apply inside every codex run)

- Never commit, push or delete recursively on the strength of another harness's output.
- Never read, print or forward `.env`, `*.key`, `*.pem`, `credentials.json`. Naming an env
  var is fine, printing its value is not.
- Never install or authenticate on the human's behalf.
- Never switch the model, provider or effort on your own.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…