Make the Codex sandbox runtime reproducible

a28428145a10 · AtlantisPleb · · parent eac3f609d8e9

Make the Codex sandbox runtime reproducible

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified Dockerfile
  • modified docs/operations/scv-staging-qualification.md
  • modified docs/scv-codex-app-server-planning.md
  • modified infra/staging/templates/fleet-startup.sh.tftpl
  • modified ops/staging/publish-candidate.sh
  • modified test/openagents/staging_candidate_contract_test.exs

Diff

6 files changed, +52 -23

Dockerfile modified +6 -6

@@ -142,13 +142,13 @@ RUN set -eu; \

142 142
  curl -fsSL --retry 3 -o "/tmp/${archive}" \
143 143
    "https://github.com/openai/codex/releases/download/rust-v${CODEX_VERSION}/${archive}"; \
144 144
  echo "${checksum}  /tmp/${archive}" | sha256sum --check --strict; \
145
  install -d -m 0755 /opt/codex; \
146
  tar -xzf "/tmp/${archive}" -C /opt/codex; \
147
  ln -s /opt/codex/bin/codex /usr/local/bin/codex; \
148
  ln -s /opt/codex/bin/codex-code-mode-host /usr/local/bin/codex-code-mode-host; \
145
  install -d -m 0755 /usr/local/lib/codex-package; \
146
  tar -xzf "/tmp/${archive}" -C /usr/local/lib/codex-package; \
147
  ln -s /usr/local/lib/codex-package/bin/codex /usr/local/bin/codex; \
148
  ln -s /usr/local/lib/codex-package/bin/codex-code-mode-host /usr/local/bin/codex-code-mode-host; \
149 149
  rm "/tmp/${archive}"; \
150
  test -x /opt/codex/codex-resources/bwrap; \
151
  test -x /opt/codex/codex-path/rg; \
150
  test -x /usr/local/lib/codex-package/codex-resources/bwrap; \
151
  test -x /usr/local/lib/codex-package/codex-path/rg; \
152 152
  codex --version; \
153 153
  codex-code-mode-host --help >/dev/null
154 154
docs/operations/scv-staging-qualification.md modified +13 -8

@@ -85,29 +85,34 @@ service account until this path passes.

85 85
86 86
1. Deploy the exact candidate SHA and immutable image digest to every staging
87 87
   fleet node.
88
2. Confirm that the connected account advertises `gpt-5.6-luna` and at least
88
2. Confirm that the fleet starts the application container with
89
   `seccomp=unconfined` and `apparmor=unconfined`, without `--privileged` or
90
   added Linux capabilities. Confirm that the complete Codex package lives
91
   under `/usr/local/lib/codex-package` so the repository-scoped sandbox can
92
   read it.
93
3. Confirm that the connected account advertises `gpt-5.6-luna` and at least
89 94
   one of the admitted reasoning efforts, `low` or `none`.
90
3. Resolve the public repository, its node-local Forge storage key, and the
95
4. Resolve the public repository, its node-local Forge storage key, and the
91 96
   exact candidate SHA. Do not use a mutable branch name for the run.
92
4. Call `OpenAgents.SCV.CodexRuns.start/5` on one fleet node with the account
97
5. Call `OpenAgents.SCV.CodexRuns.start/5` on one fleet node with the account
93 98
   ID, repository record, exact SHA, bounded read-only objective, and optional
94 99
   issue ID.
95
5. Observe `/status` while the SCV runs. The stream must advance through the
100
6. Observe `/status` while the SCV runs. The stream must advance through the
96 101
   Codex runtime, session, turn, tool or report, and terminal persistence
97 102
   phases without showing the objective, repository path, command, output,
98 103
   account identity, or credential data.
99
6. Call `OpenAgents.SCV.CodexRuns.await/2` with a bounded timeout. Require a
104
7. Call `OpenAgents.SCV.CodexRuns.await/2` with a bounded timeout. Require a
100 105
   terminal `succeeded` row, a nonempty `openagents.scv.report.v1` report, its
101 106
   SHA-256 digest, exact repository SHA, Codex thread and turn IDs, event count,
102 107
   usage, and resources.
103
7. Query `scv_run_events` for that run. Require `driver_started`,
108
8. Query `scv_run_events` for that run. Require `driver_started`,
104 109
   `driver_session_started`, `turn_started`, at least one successful
105 110
   `tool_started` and `tool_completed` pair, `turn_finished`, and
106 111
   `run_finished`. A message-only report does not prove repository access and
107 112
   does not qualify the SCV runtime.
108
8. Confirm that no disposable workspace or temporary `CODEX_HOME` remains and
113
9. Confirm that no disposable workspace or temporary `CODEX_HOME` remains and
109 114
   that the connected account remains **Ready**.
110
9. Scan the bounded event payloads, application logs, and public status
115
10. Scan the bounded event payloads, application logs, and public status
111 116
   response for credential patterns and raw protocol content. Any match fails
112 117
   qualification.
113 118
docs/scv-codex-app-server-planning.md modified +11 -1

@@ -483,7 +483,9 @@ For one admitted account runtime:

483 483
1. Resolve a digest-pinned worker image and complete Codex package. The package
484 484
   must include the app-server entry point, `codex-code-mode-host`, and its
485 485
   packaged `bwrap`, `rg`, and `zsh` resources. Installing only the `codex`
486
   binary disables code-mode tools and does not qualify an SCV runtime.
486
   binary disables code-mode tools and does not qualify an SCV runtime. Install
487
   the package under `/usr/local/lib/codex-package` so the `:minimal`
488
   filesystem policy can read the executable and its packaged resources.
487 489
2. Materialize the account secret or credential home into its isolated
488 490
   compartment.
489 491
3. Generate host-owned Codex configuration. Ignore repository-controlled user

@@ -511,6 +513,14 @@ behavior for it. Refuse the run instead of falling back to the legacy

511 513
full-filesystem read-only sandbox when the pinned runtime cannot activate the
512 514
profile.
513 515
516
On Container-Optimized OS, Docker's default seccomp and AppArmor profiles block
517
the nested Bubblewrap user namespace. The staging fleet removes those two
518
Docker profiles from the application container without using `--privileged` or
519
adding Linux capabilities. Bubblewrap must still drop every capability, disable
520
network access, and mount only the minimal runtime paths plus the repository.
521
Treat these host settings as staging-specific. Do not carry them into a
522
production SCV runtime without a separate containment review.
523
514 524
### SCV run sequence
515 525
516 526
1. Claim one account-runtime capacity lease and bind it to the SCV run
infra/staging/templates/fleet-startup.sh.tftpl modified +5

@@ -190,10 +190,15 @@ fi

190 190
191 191
docker pull "$image"
192 192
docker rm --force openagents 2>/dev/null || true
193
# Codex creates a nested Bubblewrap user namespace for each repository command.
194
# COS blocks that setup through Docker's default profiles. Remove only those
195
# profiles; keep the container unprivileged and add no Linux capabilities.
193 196
docker run --detach \
194 197
  --name openagents \
195 198
  --network host \
196 199
  --restart always \
200
  --security-opt seccomp=unconfined \
201
  --security-opt apparmor=unconfined \
197 202
  --env-file /run/openagents/runtime.env \
198 203
  --volume "$state_root:$state_root" \
199 204
  "$image"
ops/staging/publish-candidate.sh modified +5 -5

@@ -252,11 +252,11 @@ docker run --rm \

252 252
  --entrypoint /bin/sh \
253 253
  "$application_image" \
254 254
  -c 'set -eu
255
      test -x /opt/codex/bin/codex
256
      test -x /opt/codex/bin/codex-code-mode-host
257
      test -x /opt/codex/codex-resources/bwrap
258
      test -x /opt/codex/codex-path/rg
259
      /opt/codex/bin/codex-code-mode-host --help >/dev/null'
255
      test -x /usr/local/lib/codex-package/bin/codex
256
      test -x /usr/local/lib/codex-package/bin/codex-code-mode-host
257
      test -x /usr/local/lib/codex-package/codex-resources/bwrap
258
      test -x /usr/local/lib/codex-package/codex-path/rg
259
      /usr/local/lib/codex-package/bin/codex-code-mode-host --help >/dev/null'
260 260
261 261
application_config_digest=$(docker image inspect "$application_image" --format '{{.Id}}')
262 262
builder_config_digest=$(docker image inspect "$builder_image" --format '{{.Id}}')
test/openagents/staging_candidate_contract_test.exs modified +12 -3

@@ -40,12 +40,21 @@ defmodule OpenAgents.StagingCandidateContractTest do

40 40
    assert dockerfile =~ ~s(archive="codex-package-${codex_arch}-unknown-linux-musl.tar.gz")
41 41
    assert dockerfile =~ ~r/amd64\).*checksum=[0-9a-f]{64}/
42 42
    assert dockerfile =~ ~r/arm64\).*checksum=[0-9a-f]{64}/
43
    assert dockerfile =~ "/opt/codex/bin/codex-code-mode-host"
44
    assert dockerfile =~ "test -x /opt/codex/codex-resources/bwrap"
45
    assert dockerfile =~ "test -x /opt/codex/codex-path/rg"
43
    assert dockerfile =~ "/usr/local/lib/codex-package/bin/codex-code-mode-host"
44
    assert dockerfile =~ "test -x /usr/local/lib/codex-package/codex-resources/bwrap"
45
    assert dockerfile =~ "test -x /usr/local/lib/codex-package/codex-path/rg"
46 46
    assert dockerfile =~ "codex-code-mode-host --help"
47 47
  end
48 48
49
  test "the staging fleet admits nested Codex sandbox namespaces without privilege" do
50
    startup = File.read!("infra/staging/templates/fleet-startup.sh.tftpl")
51
52
    assert startup =~ "--security-opt seccomp=unconfined"
53
    assert startup =~ "--security-opt apparmor=unconfined"
54
    refute startup =~ "--privileged"
55
    refute startup =~ "--cap-add"
56
  end
57
49 58
  test "the isolated builder loads runtime configuration without the web role" do
50 59
    dockerfile = File.read!("Dockerfile")
51 60
    runtime_config = File.read!("config/runtime.exs")

This page updates live while a promote is in flight · changelog