You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2229e88
Browse filesBrowse the repository at this point in the historyBrowse files
Address second Copilot review: container lock-down, symlinks, cmd wording
- Design doc: lock down agent containers (read-only root, tmpfs scratch,
non-root, no capabilities, no host mounts, resource limits) with a
start-up self-test; /work is the only lasting writable place. T1 verify
steps extended. Fix the last "three checks" to four.
- plan-format.md: reject symlink components and .git targets in path and
renamed_from; after each run, reject undeclared symlinks, symlinks that
leave the repo, and any .git change; run codeboost's git with hooks off.
- Both schemas, the guide, and the prompt describe cmd as one program with
literal arguments run without a shell. Example P2 command no longer
contains a quoted pipe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/designs/codeboost-plan-indexed-review.md
+16-3Lines changed: 16 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -212,11 +212,24 @@ It ignores this task's own PR, any draft PRs it opened earlier, and its own comm
212
212
- Every invocation records which comments it was given.
213
213
214
214
**The container (the real safety boundary).** Changed by the engineering review, R1 (answer D2: B). Every invocation, of every phase, runs inside a Docker or Podman container. The agent tool itself runs inside it. The container holds only:
215
-
- the task's folder, including its own `.git` (O6), mounted as `/work`. This is the only folder the agent can write to;
215
+
- the task's folder, including its own `.git` (O6), mounted as `/work`. This is the only project folder the agent can write to;
216
216
- the agent's own sign-in. For Codex, that is its `auth.json` from `CODEX_HOME`, mounted read-only. For Claude, it is a long-lived token made with `claude setup-token`, passed as an environment variable. (On macOS, Claude keeps its normal sign-in in the keychain, which a container cannot read.)
217
217
218
218
Nothing else from your computer is inside. So `~/.ssh`, `~/.config/gh`, `~/.npmrc`, `~/.aws`, `~/.docker`, and your git credential helper simply are not there. The container's `HOME` is its own empty folder.
219
219
220
+
**How the container is locked down.** Docker and Podman leave a container's own files writable by default. So codeboost starts every agent container with these settings, and a start-up self-test fails the run if any is missing:
221
+
222
+
| Setting | Why |
223
+
|---|---|
224
+
| Read-only root filesystem (`--read-only`) | The agent cannot change the tools in the image, such as `git`, `go`, or the agent CLI itself. |
225
+
| Writable scratch only as size-limited in-memory folders (`--tmpfs`): `/tmp`and the empty `HOME`. Both are emptied when the container ends. | Tools that need scratch space still work, and nothing written there outlives the invocation or reaches the task's code. |
226
+
| Build caches point inside the scratch folders (for example `GOCACHE`, `npm_config_cache`). | Caches do not need a writable root. |
227
+
| Runs as a non-root user; all Linux capabilities dropped (`--cap-drop=ALL`); `--security-opt=no-new-privileges` | The agent cannot become root or use privileged system calls. |
228
+
| No `--privileged`, no `--device`, no host network, no Docker socket, and no host mounts other than `/work` and the read-only sign-in file | Nothing on the host is reachable through the container. |
229
+
| Limits on processes, memory, and CPU (`--pids-limit`, `--memory`, `--cpus`) | A runaway agent cannot slow down your computer. |
230
+
231
+
So the only place an agent can change something that lasts is `/work`, and codeboost checks every change there against the declared files before it commits (see "Checks after import" and "After each run" in `docs/plan-format.md`).
232
+
220
233
**Network (engineering review, R2, answer D3: A).** The container sits on an internal network. A small proxy lets it reach only the agent vendor's API hosts, and the host list is pinned with each image version. As a second layer, codeboost turns off the agent's own ways to reach the web: for Claude, `--disallowedTools WebFetch,WebSearch` and `--strict-mcp-config` with no servers; for Codex, web search is turned off. So a hostile issue cannot make the agent send your code to an outside server.
221
234
222
235
If Docker or Podman is missing or not running, codeboost runs no agents and tells you what to install. When the container image is built, codeboost pins the `claude` and `codex` versions it tested.
@@ -1552,7 +1565,7 @@ Leave this finding open. ✅ No work now. ✅ Listed as an open decision. ❌ Bu
1552
1565
1553
1566
State: approved
1554
1567
Actual answer: A) Apply this change (answer to D18, 2026-09-22)
1555
-
Accepted scope: build step 2 is a read-only review screen (rows, segments, three checks, approvals, per-item conversation; no merging). The merge gate, pre-merge sequence, and `gh` merge become build step 4, right after the go/no-go check. Later steps renumber: running agents 5, planning 6, queue 7, issue list 8. Design sections amended: Build order and the go/no-go check.
1568
+
Accepted scope: build step 2 is a read-only review screen (rows, segments, four checks, approvals, per-item conversation; no merging). The merge gate, pre-merge sequence, and `gh` merge become build step 4, right after the go/no-go check. Later steps renumber: running agents 5, planning 6, queue 7, issue list 8. Design sections amended: Build order and the go/no-go check.
1556
1569
History: an earlier draft of this record bundled the experiment design; it was split into O8 and O9 before being asked.
1557
1570
1558
1571
### O9: Writing the go/no-go experiment down before running it
@@ -1862,7 +1875,7 @@ Built from this review's findings. Each task comes from a specific decision abov
1862
1875
- [ ] **T1 (P1, human: ~3 days / CC: ~1 hour)** — agents — Build the pinned agent container that mounts only `/work` (with its own `.git`) and the agent's sign-in
1863
1876
- Surfaced by: R1 (D2: B), O6 (D16: A)
1864
1877
- Files: agents/container/, git/clone
1865
-
- Verify: real-Docker test shows only `/work` and sign-in; `git status` works inside; the main repo path is absent; changing a task-clone object in disposable repos leaves the source object unchanged
1878
+
- Verify: real-Docker test shows only `/work` and sign-in; writing to `/`, `/usr/bin`, or the image's tools fails; `/tmp` and `HOME` are empty in each new container; the process is not root and has no capabilities; the start-up self-test refuses a container missing any lock-down setting; `git status` works inside; the main repo path is absent; changing a task-clone object in disposable repos leaves the source object unchanged
1866
1879
- [ ] **T2 (P1, human: ~1 day / CC: ~30 min)** — agents — Add the vendor-only egress proxy and turn off web and MCP tools
Copy file name to clipboardExpand all lines: docs/plan-format.md
+4-1Lines changed: 4 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -61,7 +61,7 @@
61
61
62
62
| Field | Type | Rule |
63
63
|---|---|---|
64
-
|`type`|`cmd` or `check`|`cmd` runs in the agent's container and passes when it exits with 0. `check` is a statement the review agent judges. |
64
+
|`type`|`cmd` or `check`|`cmd`is one program and its literal arguments, such as `go test ./... -run TestRetry`. codeboost runs it in the agent's container without a shell, so pipes, redirects, `;`, `&&`, `$( )`, and variables are not allowed. It passes when it exits with 0. `check` is a statement the review agent judges. |
65
65
|`text`| text | The command, or the statement. |
66
66
67
67
**Every field is always present.** A field with nothing to say is `null` or `[]`, never left out. This is what lets the same schema work with both agents' strict answer modes.
@@ -100,6 +100,7 @@ The schema checks the shape. codeboost then checks the meaning. A **failure** bl
100
100
| Item IDs are unique. | Failure |
101
101
| Every `depends_on` ID exists, comes earlier in the list, and there is no loop. | Failure |
102
102
| A path has no `..` part and stays inside the repo. | Failure |
103
+
| No part of `path` or `renamed_from` is a symlink in the projected state, and neither points into `.git`. codeboost checks each folder and the file itself without following links. A plan that needs to change a symlink must declare the link itself; its target is never edited through it. | Failure |
103
104
| File operations are valid in the projected repo state immediately before the item runs (see below). `edit` and `delete` need an existing path; `add` needs an unused path; `rename` needs an existing `renamed_from` and an unused destination `path`. | Failure |
104
105
| `renamed_from` is set only for kind `rename`. | Failure |
105
106
| The same path is not declared twice in one item. | Failure |
@@ -108,6 +109,8 @@ The schema checks the shape. codeboost then checks the meaning. A **failure** bl
108
109
| A `cmd` changes a dependency or a script codeboost runs. | The task stops in "needs approval" when it runs, as for any such change |
109
110
| `questions` is not empty. | The plan shows the questions at the top; answer them or approve anyway |
110
111
112
+
**After each run, codeboost checks the result too.** A plan check alone cannot stop an agent from creating a new symlink and writing through it. So after each invocation, before committing, codeboost: rejects any change outside the declared files (as today, out of scope); rejects a new or changed symlink that the item did not declare, and any symlink whose target leaves the repo or enters `.git`; and rejects any change to `.git` itself (config, hooks, refs, objects other than new ones). codeboost's own git commands on the task folder run with hooks turned off (`core.hooksPath=/dev/null`), so a planted hook never runs.
113
+
111
114
**Projected file state.** Start with the paths at the plan's base commit, then walk items in their listed execution order. Check an item's file operations against the state before that item; after it passes, apply its declared additions, deletions, and renames to the projected state before checking the next item. No repo files change during validation. A path may participate in only one operation per item, counting both the source and destination of a rename. If an item uses a path created or renamed by an earlier item, it must depend on that item, directly or through other dependencies.
112
115
113
116
For example, P1 may add `src/new.go`, then P2 with `depends_on: [P1]` may edit it. Likewise, P1 may rename `src/old.go` to `src/new.go`, then P2 may edit the new path. Editing a missing path, adding an existing path, or renaming onto an occupied path blocks approval. Recompute the projected state from the base commit after each plan edit.
Copy file name to clipboardExpand all lines: prompts/plan-author.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -58,7 +58,7 @@ Write revision {{revision}} of the plan for issue {{issue_number}}. Follow these
58
58
1.**One concern per item.** Split unrelated changes into separate items. Keep tests for a change in the same item, or in a test item that depends on it. Put docs changes in their own item.
59
59
2.**Declare every file.** List every file the item will add, edit, rename, or delete. The carrying-out agent may touch only declared files. If you are not sure a file needs to change, declare it and say why in `change`.
60
60
3.**Say what changes, file by file.** In each file's `change`, name the functions and behavior that change. Do not give line numbers.
61
-
4.**Make it checkable.** Give every item at least one acceptance entry. Prefer a `cmd` built from the allowed commands, so codeboost can run it. Add a `check` for behavior a command cannot show.
61
+
4.**Make it checkable.** Give every item at least one acceptance entry. Prefer a `cmd` built from the allowed commands, so codeboost can run it. A `cmd` is one program and its literal arguments, run without a shell: no pipes, redirects, `;`, `&&`, `$( )`, or variables. Add a `check` for behavior a command cannot show.
62
62
5.**Order and dependencies.** List items in the order they should run. `depends_on` may name only earlier items.
63
63
6.**Paths.** Paths start at the repo root, use forward slashes, and never contain `..`. For a new file use kind `add`; for a move use `rename` with `renamed_from`.
64
64
7.**Ask, don't guess.** When the issue leaves a real choice open, make the most reasonable plan and put the open choice in `questions`.
Copy file name to clipboardExpand all lines: schema/plan-edit.schema.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -275,7 +275,7 @@
275
275
],
276
276
"properties": {
277
277
"type": {
278
-
"description": "cmd: a shell command codeboost runs in the container; it passes when it exits 0. check: a statement the review agent judges against the code.",
278
+
"description": "cmd: one program and its literal arguments, for example go test ./... -run TestRetry. codeboost splits it into arguments and runs it in the container without a shell, so pipes, redirects, ;, &&, $(), backticks, and variables are not allowed; the program must be on the repo's allowed command list. It passes when it exits 0. check: a statement the review agent judges against the code.",
Copy file name to clipboardExpand all lines: schema/plan.schema.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -167,7 +167,7 @@
167
167
],
168
168
"properties": {
169
169
"type": {
170
-
"description": "cmd: a shell command codeboost runs in the container; it passes when it exits 0. check: a statement the review agent judges against the code.",
170
+
"description": "cmd: one program and its literal arguments, for example go test ./... -run TestRetry. codeboost splits it into arguments and runs it in the container without a shell, so pipes, redirects, ;, &&, $(), backticks, and variables are not allowed; the program must be on the repo's allowed command list. It passes when it exits 0. check: a statement the review agent judges against the code.",
0 commit comments