Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
42 changes: 42 additions & 0 deletions .github/workflows/claude-auto-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -535,12 +535,54 @@ jobs:
.pytest_cache/ .mypy_cache/ .ruff_cache/ .coverage node_modules/ \
>>.git/info/exclude

# codex-action's default safety-strategy (drop-sudo) chmods root-owned
# service sockets under /run, breaking D-Bus -> systemd-resolved -> DNS;
# the hosted runner then dies with "lost communication" 52-65 minutes
# into the job (openai/codex-action#160; observed twice at ~62 min on
# inspect_ai#389, 2026-08-31). Until fixed upstream, run codex as a
# dedicated unprivileged user — the README's other blessed strategy for
# permission profiles: containment is the user boundary plus the
# profile sandbox, and the API key stays out of reach (codex has no
# sudo). Setup mirrors the action's examples/unprivileged-user.yml —
# the chown/g+rwX is what lets codex (primary group codex) write the
# runner-owned workspace — plus two grants the example never needs (it
# passes no explicit output-file and its demo prompt never runs git):
# RUNNER_TEMP group-write and git safe.directory, commented inline.
# NB the runner->codex group grant is inert for this job:
# supplementary groups are fixed at process start, and every step is
# spawned from a Runner.Worker that predates the usermod. Landing
# codex's edits works anyway — runner still owns the workspace
# directories, and codex's files are world-readable under its umask
# (kept as a faithful copy of the example; if codex ever drops a 600
# file, the land step needs a chmod -R a+rX sweep, not this group).
- name: Create codex user
if: steps.gate.outputs.act == 'fix' && steps.gate.outputs.engine == 'codex'
run: |
set -euo pipefail
sudo adduser --system --home /home/codex --shell /bin/bash --group codex
sudo usermod -a -G codex runner
sudo usermod -a -G runner codex
sudo chown -R runner:codex "$GITHUB_WORKSPACE"
sudo chmod -R g+rwX "$GITHUB_WORKSPACE"
sudo find "$GITHUB_WORKSPACE" -type d -exec chmod g+s {} +
# codex itself (via sudo -u) writes the explicit output-file and
# the action re-reads it as codex, but RUNNER_TEMP is 755
# runner:runner on the hosted image — grant its group (runner,
# which codex joined above) write.
sudo chmod g+w "$RUNNER_TEMP"
# The checkout stays runner-owned, so git commands running as
# codex hit the dubious-ownership refusal, and no profile sandbox
# lets the agent add the exemption itself.
sudo -u codex -H git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Run Codex fix
id: codexfix
if: steps.gate.outputs.act == 'fix' && steps.gate.outputs.engine == 'codex'
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
safety-strategy: unprivileged-user
codex-user: codex
model: ${{ inputs.codex_model }}
permission-profile: ":workspace"
allow-bot-users: "claude"
Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/claude-auto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -357,12 +357,54 @@ jobs:
.pytest_cache/ .mypy_cache/ .ruff_cache/ .coverage node_modules/ \
>>.git/info/exclude

# codex-action's default safety-strategy (drop-sudo) chmods root-owned
# service sockets under /run, breaking D-Bus -> systemd-resolved -> DNS;
# the hosted runner then dies with "lost communication" 52-65 minutes
# into the job (openai/codex-action#160; observed twice at ~62 min on
# inspect_ai#389, 2026-08-31). Until fixed upstream, run codex as a
# dedicated unprivileged user — the README's other blessed strategy for
# permission profiles: containment is the user boundary plus the
# profile sandbox, and the API key stays out of reach (codex has no
# sudo). Setup mirrors the action's examples/unprivileged-user.yml —
# the chown/g+rwX is what lets codex (primary group codex) write the
# runner-owned workspace — plus two grants the example never needs (it
# passes no explicit output-file and its demo prompt never runs git):
# RUNNER_TEMP group-write and git safe.directory, commented inline.
# NB the runner->codex group grant is inert for this job:
# supplementary groups are fixed at process start, and every step is
# spawned from a Runner.Worker that predates the usermod. Landing
# codex's edits works anyway — runner still owns the workspace
# directories, and codex's files are world-readable under its umask
# (kept as a faithful copy of the example; if codex ever drops a 600
# file, the land step needs a chmod -R a+rX sweep, not this group).
- name: Create codex user
if: steps.gate.outputs.act == 'fix' && steps.gate.outputs.engine == 'codex'
run: |
set -euo pipefail
sudo adduser --system --home /home/codex --shell /bin/bash --group codex
sudo usermod -a -G codex runner
sudo usermod -a -G runner codex
sudo chown -R runner:codex "$GITHUB_WORKSPACE"
sudo chmod -R g+rwX "$GITHUB_WORKSPACE"
sudo find "$GITHUB_WORKSPACE" -type d -exec chmod g+s {} +
# codex itself (via sudo -u) writes the explicit output-file and
# the action re-reads it as codex, but RUNNER_TEMP is 755
# runner:runner on the hosted image — grant its group (runner,
# which codex joined above) write.
sudo chmod g+w "$RUNNER_TEMP"
# The checkout stays runner-owned, so git commands running as
# codex hit the dubious-ownership refusal, and no profile sandbox
# lets the agent add the exemption itself.
sudo -u codex -H git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Run Codex CI fix
id: codexfix
if: steps.gate.outputs.act == 'fix' && steps.gate.outputs.engine == 'codex'
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
safety-strategy: unprivileged-user
codex-user: codex
model: ${{ inputs.codex_model }}
permission-profile: ":workspace"
# workflow_run's actor is the CI run's actor: normally the machine
Expand Down
43 changes: 43 additions & 0 deletions .github/workflows/claude-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,47 @@ jobs:
# SAME markers the @auto loop consumes. The verdict is thereby
# deterministic rather than prompt-enforced. Inline comments are a v1
# non-goal; findings carry file:line references in the body instead.
#
# codex-action's default safety-strategy (drop-sudo) chmods root-owned
Comment thread
ransomr marked this conversation as resolved.
# service sockets under /run, breaking D-Bus -> systemd-resolved -> DNS;
# the hosted runner then dies with "lost communication" 52-65 minutes
# into the job (openai/codex-action#160; observed twice at ~62 min on
# inspect_ai#389, 2026-08-31). Until fixed upstream, run codex as a
# dedicated unprivileged user — the README's other blessed strategy for
# permission profiles: containment is the user boundary plus the
# profile sandbox, and the API key stays out of reach (codex has no
# sudo). Setup mirrors the action's examples/unprivileged-user.yml —
# the chown/g+rwX is what lets codex (primary group codex) write the
# runner-owned workspace — plus two grants the example never needs (it
# passes no explicit output-file and its demo prompt never runs git):
# RUNNER_TEMP group-write and git safe.directory, commented inline.
# NB the runner->codex group grant is inert for this job:
# supplementary groups are fixed at process start, and every step is
# spawned from a Runner.Worker that predates the usermod. Landing
# codex's edits works anyway — runner still owns the workspace
# directories, and codex's files are world-readable under its umask
# (kept as a faithful copy of the example; if codex ever drops a 600
# file, the land step needs a chmod -R a+rX sweep, not this group).
- name: Create codex user
Comment thread
ransomr marked this conversation as resolved.
if: steps.trig.outputs.ok == 'true' && steps.engine.outputs.engine == 'codex'
run: |
set -euo pipefail
sudo adduser --system --home /home/codex --shell /bin/bash --group codex
sudo usermod -a -G codex runner
sudo usermod -a -G runner codex
sudo chown -R runner:codex "$GITHUB_WORKSPACE"
Comment thread
ransomr marked this conversation as resolved.
Outdated
Comment thread
ransomr marked this conversation as resolved.
Outdated
sudo chmod -R g+rwX "$GITHUB_WORKSPACE"
sudo find "$GITHUB_WORKSPACE" -type d -exec chmod g+s {} +
# codex itself (via sudo -u) writes the explicit output-file and
# the action re-reads it as codex, but RUNNER_TEMP is 755
# runner:runner on the hosted image — grant its group (runner,
# which codex joined above) write.
sudo chmod g+w "$RUNNER_TEMP"
# The checkout stays runner-owned, so git commands running as
# codex hit the dubious-ownership refusal, and no profile sandbox
# lets the agent add the exemption itself.
sudo -u codex -H git config --global --add safe.directory "$GITHUB_WORKSPACE"

# Our trig gate did the real authorization; allow-bot-users lifts the
# action's own actor guard for the reviewer-bot trigger the Claude
# path already accepts (the dev agent's @review hand-back).
Expand All @@ -628,6 +669,8 @@ jobs:
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
safety-strategy: unprivileged-user
Comment thread
ransomr marked this conversation as resolved.
codex-user: codex
model: ${{ inputs.codex_model }}
effort: ${{ inputs.codex_effort }}
permission-profile: ":read-only"
Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -588,12 +588,54 @@ jobs:
.pytest_cache/ .mypy_cache/ .ruff_cache/ .coverage node_modules/ \
>>.git/info/exclude

# codex-action's default safety-strategy (drop-sudo) chmods root-owned
# service sockets under /run, breaking D-Bus -> systemd-resolved -> DNS;
# the hosted runner then dies with "lost communication" 52-65 minutes
# into the job (openai/codex-action#160; observed twice at ~62 min on
# inspect_ai#389, 2026-08-31). Until fixed upstream, run codex as a
# dedicated unprivileged user — the README's other blessed strategy for
# permission profiles: containment is the user boundary plus the
# profile sandbox, and the API key stays out of reach (codex has no
# sudo). Setup mirrors the action's examples/unprivileged-user.yml —
# the chown/g+rwX is what lets codex (primary group codex) write the
# runner-owned workspace — plus two grants the example never needs (it
# passes no explicit output-file and its demo prompt never runs git):
# RUNNER_TEMP group-write and git safe.directory, commented inline.
# NB the runner->codex group grant is inert for this job:
# supplementary groups are fixed at process start, and every step is
# spawned from a Runner.Worker that predates the usermod. Landing
# codex's edits works anyway — runner still owns the workspace
Comment thread
ransomr marked this conversation as resolved.
Outdated
# directories, and codex's files are world-readable under its umask
# (kept as a faithful copy of the example; if codex ever drops a 600
# file, the land step needs a chmod -R a+rX sweep, not this group).
- name: Create codex user
if: steps.engine.outputs.engine == 'codex'
run: |
set -euo pipefail
sudo adduser --system --home /home/codex --shell /bin/bash --group codex
sudo usermod -a -G codex runner
sudo usermod -a -G runner codex
sudo chown -R runner:codex "$GITHUB_WORKSPACE"
sudo chmod -R g+rwX "$GITHUB_WORKSPACE"
sudo find "$GITHUB_WORKSPACE" -type d -exec chmod g+s {} +
# codex itself (via sudo -u) writes the explicit output-file and
# the action re-reads it as codex, but RUNNER_TEMP is 755
# runner:runner on the hosted image — grant its group (runner,
# which codex joined above) write.
sudo chmod g+w "$RUNNER_TEMP"
Comment thread
ransomr marked this conversation as resolved.
Outdated
# The checkout stays runner-owned, so git commands running as
# codex hit the dubious-ownership refusal, and no profile sandbox
# lets the agent add the exemption itself.
sudo -u codex -H git config --global --add safe.directory "$GITHUB_WORKSPACE"

- name: Run Codex
id: codexrun
if: steps.engine.outputs.engine == 'codex'
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
safety-strategy: unprivileged-user
codex-user: codex
model: ${{ inputs.codex_model }}
permission-profile: ":workspace"
# Authorization: codex-action's own built-in write-access check on
Expand Down
19 changes: 19 additions & 0 deletions design/codex-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,25 @@ the secret: codex-labeled runs fail at the codex step with a clear
error rather than silently falling back (a silent Claude fallback would
misattribute output).

## Safety strategy: unprivileged-user, not drop-sudo

codex-action's default `drop-sudo` chmods root-owned service sockets under
`/run`, which breaks D-Bus, crashes systemd-resolved, and kills DNS — the
hosted runner then dies with "lost communication" 52–65 minutes into the
job (openai/codex-action#160; hit twice at ~62 min on the first codex
runs, inspect_ai#389). Every codex step therefore creates a dedicated
`codex` system user and runs with `safety-strategy: unprivileged-user`:
containment is the user boundary plus the permission-profile sandbox, the
API key stays unreadable (codex has no sudo), and the host is never
mutated. The setup mirrors the action's `examples/unprivileged-user.yml`
plus two grants its demo never needs: group-write on `$RUNNER_TEMP` (755
`runner:runner` on the hosted image, and codex — in group `runner` — must
create the explicit `output-file` there, which the action then re-reads as
codex), and the checkout added to the codex user's git `safe.directory`
(the repo stays runner-owned, so git run as codex otherwise refuses with
"dubious ownership", and no profile sandbox lets the agent add the
exemption itself). Revisit when #160's fixes land upstream.

## v1 limitations (deliberate)

- **External proxy reviews stay on Claude** — their contributor-code
Expand Down