From bb8551efa93af77d63e1b4a3a6d95261d0dceecf Mon Sep 17 00:00:00 2001 From: Kyle Brennan Date: Fri, 28 Aug 2026 02:22:31 +0000 Subject: [PATCH 1/4] chore: manage development services with Compose Co-authored-by: Codex --- .gitignore | 1 + .ona/automations.yml | 10 ++ compose.yaml | 17 ++++ spec.md | 215 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+) create mode 100644 spec.md diff --git a/.gitignore b/.gitignore index fe166502f..7f7861ef8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules/ +.pnpm-store/ dist/ __pycache__/ .pytest_cache/ diff --git a/.ona/automations.yml b/.ona/automations.yml index a9355768d..1f0faa63f 100644 --- a/.ona/automations.yml +++ b/.ona/automations.yml @@ -12,10 +12,20 @@ tasks: pnpm --dir sdk/typescript install --frozen-lockfile npm ci --prefix plugins/codex-security/mcp-app --no-audit --no-fund python -m pip install --disable-pip-version-check --no-input -e 'plugins/codex-security[test]' + docker compose build codex-security findings triggeredBy: - manual - prebuild + start-findings: + name: Start findings service + command: docker compose up --detach --no-build findings + dependsOn: + - install + triggeredBy: + - manual + - postDevcontainerStart + build: name: Build command: pnpm --dir sdk/typescript run build diff --git a/compose.yaml b/compose.yaml index d69aa3535..8b01136ad 100644 --- a/compose.yaml +++ b/compose.yaml @@ -4,6 +4,7 @@ services: build: context: . init: true + network_mode: host user: ${CODEX_SECURITY_USER:-10001:10001} cap_drop: - ALL @@ -38,3 +39,19 @@ services: - /input/repositories.csv - --output-dir - /output + + findings: + image: ${CODEX_SECURITY_FINDINGS_IMAGE:-codex-security-findings:local} + build: + context: . + target: findings-service + init: true + network_mode: host + environment: + OPENAI_API_KEY: + CODEX_API_KEY: + volumes: + - findings-state:/state + +volumes: + findings-state: diff --git a/spec.md b/spec.md new file mode 100644 index 000000000..951ed17a6 --- /dev/null +++ b/spec.md @@ -0,0 +1,215 @@ +# Task-driven Docker Compose inside the Dev Container + +## Summary + +Use one Universal Dev Container and run the repository's two application +containers on the Docker engine inside it. This makes `docker compose up`, +`docker compose start`, `docker compose stop`, `docker compose ps`, and the Ona +container tasks all operate on the same scanner and findings containers. + +The upstream baseline already uses one Universal Dev Container and does not add +a Docker-in-Docker Feature. It does not yet build or start the application +containers, and the root Compose project defines only the scanner. This change +keeps that single-container model and adds task-driven lifecycle management for +both application services on the existing in-container Docker engine. + +## Requirements + +1. `.devcontainer/devcontainer.json` must define one development container + using `mcr.microsoft.com/devcontainers/universal:4.0.1-noble`. +2. The Dev Container must not use `dockerComposeFile` or a Docker-in-Docker + Feature. +3. The pinned Universal image's existing Docker Engine, Docker Compose v2, and + Buildx installation must provide the in-container Docker runtime. The live + image currently provides Docker Engine 28.3.3, Compose 2.39.2, and Buildx + 0.27.0. +4. `compose.yaml` must define exactly two services: + - `codex-security`, the existing one-shot scan runner and orchestrator; and + - `findings`, the persistent findings API and SQLite database. +5. Both Compose services must use `network_mode: host`. The findings API must + therefore be reachable from the Dev Container and scanner at + `http://localhost:3000` without a Compose `ports` mapping. +6. The scanner must preserve its current image target, nonroot user, dropped + capabilities, seccomp profile, credential inputs, bind mounts, and default + bulk-scan command. +7. The findings service must build the `findings-service` target from the root + `Dockerfile`, preserve `/state` in a named volume, and use its existing + foreground server entrypoint. +8. The Ona install task must retain dependency installation and additionally + build both local Compose images with `docker compose build codex-security + findings`. +9. The install task must retain its `manual` and `prebuild` triggers. It must + not also use `postDevcontainerStart`, because the dependent findings startup + task invokes it during post-start and duplicate triggers run installation + concurrently. +10. Add a findings startup task that: + - depends on the install task; + - runs `docker compose up --detach --no-build findings`; and + - is triggered by `manual` and `postDevcontainerStart`. +11. Keep real scanner execution manual. No post-start task may choose a scan + target, repository CSV, credentials, or output directory. +12. Ignore the repository-local `.pnpm-store/` generated by dependency + installation. + +## Constraints + +- Do not add a Dev Container Feature. Feature resolution is the image-build + path that invokes blocked package-manager executables. In particular, do not + add `ghcr.io/devcontainers/features/docker-in-docker`; it would duplicate the + Docker runtime already present in the pinned Universal image and recreate the + known build failure. +- Do not add a custom Dev Container Dockerfile unless validation proves the + pinned Universal image no longer includes a working Engine, Compose v2, or + Buildx. Current runtime evidence shows all three are present, so no Dockerfile + is planned. +- Do not mount the outer host Docker socket into the Dev Container. +- Do not use Ona's outer multi-container `runServices` model for the scanner or + findings service. +- Do not run two copies of the findings service on port 3000. +- Do not add an idle process to the scanner. It is a one-shot runner and may be + stopped after completing or failing a scan. +- Do not weaken scanner credentials, unsafe-path, scan-integrity, seccomp, + capability, user, or filesystem protections. +- Do not invent scanner inputs so that a container can be created during + startup. +- Do not add or change public scanner commands, flags, accepted values, + environment variables, or defaults. + +## Architecture + +```text +Ona environment +└── Universal Dev Container + ├── Ona install/build/test/start tasks + ├── Docker Engine + Compose v2 + Buildx + └── compose.yaml + ├── codex-security (manual, one-shot scanner) + └── findings (persistent API, auto-started) +``` + +The development shell and all Ona tasks use the same in-container Docker socket. +Consequently, a container created by an Ona task is visible to a later terminal +command, and vice versa. + +The two child containers use the Docker daemon host network, which is the Dev +Container's network namespace in this Docker-in-Docker topology. The findings +API is therefore available to the shell and scanner on `localhost:3000`. + +## Compose command semantics + +`docker compose up` creates missing containers and starts them. `docker compose +start` only starts containers that were created previously. The expected +workflows are therefore: + +- Findings startup: the post-start Ona task runs `docker compose up -d + --no-build findings`. Later `docker compose stop findings` and `docker compose + start findings` operate on that same container. +- Scanner execution: after the user supplies the required CSV and durable + directories, use the existing `docker compose up codex-security` workflow or + `docker compose run --rm codex-security ...` for a one-off command. +- Scanner restart: `docker compose start codex-security` is valid only after a + non-removed scanner container was created by `up` or `create`. It reruns the + configured one-shot command; it does not turn the scanner into a daemon. + +On a clean environment, `docker compose ps -a` will initially show the findings +container after the startup task. It will show both application containers once +the user has prepared scanner inputs and created or run the scanner container. + +## Ona task design + +### Install dependencies + +Keep the current language dependency installation commands. After dependencies +are installed, run: + +```sh +docker compose build codex-security findings +``` + +Triggers: `manual` and `prebuild`. The dependent findings task invokes install +during post-start, so install must not also have a post-start trigger. + +### Start findings service + +Run: + +```sh +docker compose up --detach --no-build findings +``` + +This task depends on `install` and uses `manual` and +`postDevcontainerStart` triggers. Detaching is intentional: Compose owns the +long-running child container while the one-shot Ona task exits successfully. + +### Scanner + +Do not add an automatic scanner task. Scanner commands require user-selected +inputs and credentials. The existing Compose service remains the manual +interface. + +## Implementation steps + +1. Keep `.devcontainer/devcontainer.json` on the existing single pinned + Universal image without features or outer Compose service selection. +2. Keep the root Compose project application-only; do not add a development + container service to it. +3. Keep the existing `codex-security` service and add the local `findings` + service built from the `findings-service` target. Set host networking on + both and define the findings state volume. +4. Update the Ona install task to build both images while retaining its manual + and prebuild triggers. +5. Add the manual/post-start findings task, depending on install, which starts + only the findings service through Compose. +6. Keep `.pnpm-store/` in `.gitignore`. +7. Validate, rebuild, apply the Ona configuration, and verify all tasks and the + in-container Compose lifecycle. + +## Validation plan + +1. Validate `.devcontainer/devcontainer.json` and the checked-in Ona task + configuration. +2. Run `docker compose config --quiet` and verify: + - exactly `codex-security` and `findings` are defined; + - both use `network_mode: host`; + - findings has no port mapping; and + - the scanner's hardening and mounts are unchanged. +3. Rebuild the Ona environment and verify `docker version`, `docker compose + version`, `docker buildx version`, and `docker info` succeed inside the + single Dev Container. +4. Apply the updated Ona configuration. +5. Start the findings task through Ona and verify: + - the install dependency successfully builds both images; + - `docker compose ps` inside the Dev Container shows findings running; and + - `curl -fsS http://localhost:3000/v1/findings` returns JSON. +6. Run `docker compose stop findings`, `docker compose start findings`, and the + API check again to prove terminal commands manage the task-created container. +7. Validate the scanner with synthetic, non-sensitive paths and input: + - create private temporary result and state directories; + - provide a synthetic CSV through `CODEX_SECURITY_CSV`; + - create or run the scanner service without starting a real external scan; + - verify `docker compose ps -a` contains both service containers; and + - remove only the temporary validation artifacts created by the test. +8. Run the configured build and test tasks through Ona and require a successful + execution for every task. + +## Success criteria + +- Ona starts one Universal Dev Container rather than an outer multi-container + Compose project. +- Docker Engine, Compose v2, and Buildx work inside that Dev Container without + installing a Feature. +- `compose.yaml` defines exactly the scanner and findings services. +- Both application services use host networking and findings is reachable on + `localhost:3000`. +- The install task builds both local images in the same Docker engine used by + terminal Compose commands. +- The findings task creates and starts the findings container automatically and + manually. +- `docker compose stop findings` followed by `docker compose start findings` + works from the development shell. +- The scanner remains manual and retains all existing hardening and input + requirements. +- Once created, scanner and findings appear together in the in-container + Compose project. +- Dev Container, Compose, and Ona configuration validation pass; the rebuilt + environment resumes; and all configured tasks succeed. From 87c861dc0bd21e7f603bf9b789ad5ae284d3dd3d Mon Sep 17 00:00:00 2001 From: Kyle Brennan Date: Fri, 28 Aug 2026 02:33:32 +0000 Subject: [PATCH 2/4] docs: remove implementation spec Co-authored-by: Codex --- spec.md | 215 -------------------------------------------------------- 1 file changed, 215 deletions(-) delete mode 100644 spec.md diff --git a/spec.md b/spec.md deleted file mode 100644 index 951ed17a6..000000000 --- a/spec.md +++ /dev/null @@ -1,215 +0,0 @@ -# Task-driven Docker Compose inside the Dev Container - -## Summary - -Use one Universal Dev Container and run the repository's two application -containers on the Docker engine inside it. This makes `docker compose up`, -`docker compose start`, `docker compose stop`, `docker compose ps`, and the Ona -container tasks all operate on the same scanner and findings containers. - -The upstream baseline already uses one Universal Dev Container and does not add -a Docker-in-Docker Feature. It does not yet build or start the application -containers, and the root Compose project defines only the scanner. This change -keeps that single-container model and adds task-driven lifecycle management for -both application services on the existing in-container Docker engine. - -## Requirements - -1. `.devcontainer/devcontainer.json` must define one development container - using `mcr.microsoft.com/devcontainers/universal:4.0.1-noble`. -2. The Dev Container must not use `dockerComposeFile` or a Docker-in-Docker - Feature. -3. The pinned Universal image's existing Docker Engine, Docker Compose v2, and - Buildx installation must provide the in-container Docker runtime. The live - image currently provides Docker Engine 28.3.3, Compose 2.39.2, and Buildx - 0.27.0. -4. `compose.yaml` must define exactly two services: - - `codex-security`, the existing one-shot scan runner and orchestrator; and - - `findings`, the persistent findings API and SQLite database. -5. Both Compose services must use `network_mode: host`. The findings API must - therefore be reachable from the Dev Container and scanner at - `http://localhost:3000` without a Compose `ports` mapping. -6. The scanner must preserve its current image target, nonroot user, dropped - capabilities, seccomp profile, credential inputs, bind mounts, and default - bulk-scan command. -7. The findings service must build the `findings-service` target from the root - `Dockerfile`, preserve `/state` in a named volume, and use its existing - foreground server entrypoint. -8. The Ona install task must retain dependency installation and additionally - build both local Compose images with `docker compose build codex-security - findings`. -9. The install task must retain its `manual` and `prebuild` triggers. It must - not also use `postDevcontainerStart`, because the dependent findings startup - task invokes it during post-start and duplicate triggers run installation - concurrently. -10. Add a findings startup task that: - - depends on the install task; - - runs `docker compose up --detach --no-build findings`; and - - is triggered by `manual` and `postDevcontainerStart`. -11. Keep real scanner execution manual. No post-start task may choose a scan - target, repository CSV, credentials, or output directory. -12. Ignore the repository-local `.pnpm-store/` generated by dependency - installation. - -## Constraints - -- Do not add a Dev Container Feature. Feature resolution is the image-build - path that invokes blocked package-manager executables. In particular, do not - add `ghcr.io/devcontainers/features/docker-in-docker`; it would duplicate the - Docker runtime already present in the pinned Universal image and recreate the - known build failure. -- Do not add a custom Dev Container Dockerfile unless validation proves the - pinned Universal image no longer includes a working Engine, Compose v2, or - Buildx. Current runtime evidence shows all three are present, so no Dockerfile - is planned. -- Do not mount the outer host Docker socket into the Dev Container. -- Do not use Ona's outer multi-container `runServices` model for the scanner or - findings service. -- Do not run two copies of the findings service on port 3000. -- Do not add an idle process to the scanner. It is a one-shot runner and may be - stopped after completing or failing a scan. -- Do not weaken scanner credentials, unsafe-path, scan-integrity, seccomp, - capability, user, or filesystem protections. -- Do not invent scanner inputs so that a container can be created during - startup. -- Do not add or change public scanner commands, flags, accepted values, - environment variables, or defaults. - -## Architecture - -```text -Ona environment -└── Universal Dev Container - ├── Ona install/build/test/start tasks - ├── Docker Engine + Compose v2 + Buildx - └── compose.yaml - ├── codex-security (manual, one-shot scanner) - └── findings (persistent API, auto-started) -``` - -The development shell and all Ona tasks use the same in-container Docker socket. -Consequently, a container created by an Ona task is visible to a later terminal -command, and vice versa. - -The two child containers use the Docker daemon host network, which is the Dev -Container's network namespace in this Docker-in-Docker topology. The findings -API is therefore available to the shell and scanner on `localhost:3000`. - -## Compose command semantics - -`docker compose up` creates missing containers and starts them. `docker compose -start` only starts containers that were created previously. The expected -workflows are therefore: - -- Findings startup: the post-start Ona task runs `docker compose up -d - --no-build findings`. Later `docker compose stop findings` and `docker compose - start findings` operate on that same container. -- Scanner execution: after the user supplies the required CSV and durable - directories, use the existing `docker compose up codex-security` workflow or - `docker compose run --rm codex-security ...` for a one-off command. -- Scanner restart: `docker compose start codex-security` is valid only after a - non-removed scanner container was created by `up` or `create`. It reruns the - configured one-shot command; it does not turn the scanner into a daemon. - -On a clean environment, `docker compose ps -a` will initially show the findings -container after the startup task. It will show both application containers once -the user has prepared scanner inputs and created or run the scanner container. - -## Ona task design - -### Install dependencies - -Keep the current language dependency installation commands. After dependencies -are installed, run: - -```sh -docker compose build codex-security findings -``` - -Triggers: `manual` and `prebuild`. The dependent findings task invokes install -during post-start, so install must not also have a post-start trigger. - -### Start findings service - -Run: - -```sh -docker compose up --detach --no-build findings -``` - -This task depends on `install` and uses `manual` and -`postDevcontainerStart` triggers. Detaching is intentional: Compose owns the -long-running child container while the one-shot Ona task exits successfully. - -### Scanner - -Do not add an automatic scanner task. Scanner commands require user-selected -inputs and credentials. The existing Compose service remains the manual -interface. - -## Implementation steps - -1. Keep `.devcontainer/devcontainer.json` on the existing single pinned - Universal image without features or outer Compose service selection. -2. Keep the root Compose project application-only; do not add a development - container service to it. -3. Keep the existing `codex-security` service and add the local `findings` - service built from the `findings-service` target. Set host networking on - both and define the findings state volume. -4. Update the Ona install task to build both images while retaining its manual - and prebuild triggers. -5. Add the manual/post-start findings task, depending on install, which starts - only the findings service through Compose. -6. Keep `.pnpm-store/` in `.gitignore`. -7. Validate, rebuild, apply the Ona configuration, and verify all tasks and the - in-container Compose lifecycle. - -## Validation plan - -1. Validate `.devcontainer/devcontainer.json` and the checked-in Ona task - configuration. -2. Run `docker compose config --quiet` and verify: - - exactly `codex-security` and `findings` are defined; - - both use `network_mode: host`; - - findings has no port mapping; and - - the scanner's hardening and mounts are unchanged. -3. Rebuild the Ona environment and verify `docker version`, `docker compose - version`, `docker buildx version`, and `docker info` succeed inside the - single Dev Container. -4. Apply the updated Ona configuration. -5. Start the findings task through Ona and verify: - - the install dependency successfully builds both images; - - `docker compose ps` inside the Dev Container shows findings running; and - - `curl -fsS http://localhost:3000/v1/findings` returns JSON. -6. Run `docker compose stop findings`, `docker compose start findings`, and the - API check again to prove terminal commands manage the task-created container. -7. Validate the scanner with synthetic, non-sensitive paths and input: - - create private temporary result and state directories; - - provide a synthetic CSV through `CODEX_SECURITY_CSV`; - - create or run the scanner service without starting a real external scan; - - verify `docker compose ps -a` contains both service containers; and - - remove only the temporary validation artifacts created by the test. -8. Run the configured build and test tasks through Ona and require a successful - execution for every task. - -## Success criteria - -- Ona starts one Universal Dev Container rather than an outer multi-container - Compose project. -- Docker Engine, Compose v2, and Buildx work inside that Dev Container without - installing a Feature. -- `compose.yaml` defines exactly the scanner and findings services. -- Both application services use host networking and findings is reachable on - `localhost:3000`. -- The install task builds both local images in the same Docker engine used by - terminal Compose commands. -- The findings task creates and starts the findings container automatically and - manually. -- `docker compose stop findings` followed by `docker compose start findings` - works from the development shell. -- The scanner remains manual and retains all existing hardening and input - requirements. -- Once created, scanner and findings appear together in the in-container - Compose project. -- Dev Container, Compose, and Ona configuration validation pass; the rebuilt - environment resumes; and all configured tasks succeed. From fcb265f5c2526ada57aa9444a9d3acd4a952f3e5 Mon Sep 17 00:00:00 2001 From: Kyle Brennan Date: Fri, 28 Aug 2026 02:46:33 +0000 Subject: [PATCH 3/4] fix: keep bulk scan opt-in in Compose Co-authored-by: Codex --- compose.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compose.yaml b/compose.yaml index 8b01136ad..d10e23122 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,6 +1,8 @@ services: codex-security: image: ${CODEX_SECURITY_IMAGE:-codex-security:local} + profiles: + - scan build: context: . init: true From f96a391544786bfce3ad6c39794101c5218888e6 Mon Sep 17 00:00:00 2001 From: Kyle Brennan Date: Fri, 28 Aug 2026 03:03:15 +0000 Subject: [PATCH 4/4] chore: manage Compose lifecycle in Ona Co-authored-by: Codex --- .ona/automations.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.ona/automations.yml b/.ona/automations.yml index 1f0faa63f..45ded8b40 100644 --- a/.ona/automations.yml +++ b/.ona/automations.yml @@ -17,15 +17,21 @@ tasks: - manual - prebuild - start-findings: - name: Start findings service - command: docker compose up --detach --no-build findings + compose-up: + name: Compose up + command: docker compose up --detach --no-build && ona environment port open 3000 --name findings --admission creator_only dependsOn: - install triggeredBy: - manual - postDevcontainerStart + compose-down: + name: Compose down + command: docker compose down + triggeredBy: + - manual + build: name: Build command: pnpm --dir sdk/typescript run build