Repository navigation
feat(container): define CLI workload plans - #122
Conversation
Validate isolated workspace requests and build deterministic container arguments. Record the protocol choices and core test cases before adding the shell driver. Refs: #117
tbhb-agent-reviewer
left a comment
There was a problem hiding this comment.
Harness: Claude Code; model: claude-opus-5-5; effort: high. Independent review of RFC-36 run 20 (#117).
Verdict: request changes.
Network model: shape B. container::run always emits --network none for both isolated and deny, rejects host, creates no named network, and the spec says plainly that it follows RFC-37 runs 8 and 31 and is not an --internal bridge. A broker socket, when present, mounts at /run/broker.sock via --volume and must be mode 0600 and read-only. No shape A enforcement boundary; no security finding here. A property test pins --network none for every admitted run.
Stack order (122 plan -> 123 guest -> 126 shell -> 127 host test) is right, and each PR is under 800 units (380, 517, 629, 267).
Blocking:
- CI
checkis red at7db74be. The failure is theghostty-vt-sysbuild fetchingtranslate-cfrom codeberg.org (NameServerFailure), so it looks transient, but this PR needs a greencheckat its head before merge. Re-run it. - AGENTS.md requires a unit test for each core case.
validatehas about a dozen refusal branches (runtime/isolation, name length, image empty/leading dash/NUL, credential refs, zero resources, relative mounts, parent traversal, CLI separators, driver-owned targets, workspace/cache separators) andsession_userhas four (missing, non-numeric, below 10000, above 60000). Only host network, one reserved mount, and one reviewer success are tested. See inline.
Non-blocking: run validates mount targets against driver-owned roots but not their ancestors; a mount at /run or / would shadow /run/hypervisor. Consider refusing targets that are ancestors of /workspace, /cache, or /run/hypervisor.
Host test: not run. container ls -a at review time showed rfc37-r32-wa and rfc37-r33-wa running and holding both container slots, so I did not start a VM and did not touch those containers. check:host at fae8621c remains a merge blocker for the whole stack. Exact command once a slot is free:
cd <worktree of feat/117-container-driver at fae8621c>
CARGO_BUILD_JOBS=2 mise run check:host
container ls -a
container volume ls # expect no hv-r20-* container or hv-r20-*-cache volumeExercise each CLI validation branch and guest UID refusal. Refuse mounts that shadow driver-owned guest paths through ancestor targets. Refs: #117
tbhb-agent-reviewer
left a comment
There was a problem hiding this comment.
Harness: Claude Code; model: claude-opus-5-5; effort: high. Re-review of RFC-36 run 20 (#117), round 2.
Verdict: request changes, for the host test only.
All round 1 items are verified and resolved. Validation refusal cases and invalid guest users each have tests, a comma-rejection proptest is added, and mount targets that are ancestors of driver-owned paths are refused. CI check is green. The PR is 467 units. The network model is still shape B (--network none).
One remaining blocker: the host test. It has not run. container ls -a at re-review still shows rfc37-r32-wa and rfc37-r33-wa running and holding both slots, so I did not start a VM or touch those containers. check:host covers the whole stack and must pass before any of #122, #123, #126, or #127 merges. Exact command once a slot is free:
cd <worktree of feat/117-container-driver at b9e9909>
CARGO_BUILD_JOBS=2 mise run check:host
container ls -a # expect no hv-r20-* container
container volume ls # expect no hv-r20-*-cache volumeRecord the SHA, the result, and the cleanup output. I will approve after a passing run at the current head.
tbhb-agent-reviewer
left a comment
There was a problem hiding this comment.
Harness: Claude Code; model: claude-opus-5-5; effort: high. Re-review of RFC-36 run 20 (#117), round 3: host test.
Verdict: request changes. The host test failed; all other items stay resolved.
Host test failed at b9e9909. I ran CARGO_BUILD_JOBS=2 mise run check:host on an unsandboxed Mac (macOS 26.5.1, container 1.4.1), using one container slot. It exited 101.
- Fixture path bug (observed).
guest:buildran and producedtarget/aarch64-unknown-linux-musl/debug/host-shimat the workspace root, a static aarch64 ELF. The test resolvesstd::env::current_dir().join("target/..."), but cargo runs integration tests with the cwd set to the package root (crates/hypervisord), so it panicked attests/container_driver.rs:43(build guest agent with mise run guest:build first). Becausecontainer_driverran first and failed, the launchd and seatbelt host tests in the samecheck:hostdid not run. Useenv!("CARGO_MANIFEST_DIR")/../../target/...orCARGO_TARGET_DIR. The panic also leaked the fixture directory/private/tmp/hv-r20-<pid>, becauseFixture::newcreates it before the assert and beforeFixtureexists to clean it up (I removed it). - Identity handshake fails (observed in a diagnostic rerun). In a throwaway worktree I symlinked the guest binary into the expected path and reran only
--test container_driver.driver.startfailed:guest agent did not complete its identity handshake(tests/container_driver.rs:190). In a second rerun with cleanup disabled, the guest was healthy./run/hypervisorwas the 0700 tmpfs holdingagent(mounted ro),workload.json, the shim control socket, andagent.sock(root 0600, listening). The host socket from--publish-socketexisted (srwxr-xr-x, not 0600). Still, a host-side connect, then{"Control":"Ping"}\n, got an immediate EOF with zero bytes, both with and without a write half-close. So the published-socket relay from host to guest agent does not work as configured.
Inference, untested: --publish-socket maps to Containerization's UnixSocketConfiguration(direction: .outOf) (apple/container 1.4.1 RuntimeService.swift:1165). The guest-side relay may resolve /run/hypervisor/agent.sock outside the container's mount namespace, where the --tmpfs /run/hypervisor mount added in #126 hides it. I tried a rerun without the tmpfs, but my session's permission policy blocked that edit, so it is unverified. Other candidates are the relay's half-close handling, or the relay binding before the guest listener exists. RFC-36 run 1 only verified the --volume (into-guest) direction, and the spec already flags this direction as proposed and untested. One fallback is the verified direction: the host listens on a socket mounted into the guest with --volume, and the guest agent dials out.
Also observed: the published host socket was srwxr-xr-x, so the new assert_private_sockets check depends on the driver's post-handshake chmod, which never ran here.
Cleanup: I deleted my diagnostic containers hv-r20-17000 and hv-r20-24962, their cache volumes, and the fixture directories. container ls -a now shows only buildkit and another run's rfc37-r11-b, and container volume ls is empty. I did not touch other runs' containers.
Exact rerun after the fix, at the new head:
CARGO_BUILD_JOBS=2 mise run check:host
container ls -a; container volume ls # expect no hv-r20-* leftovers
ls -d /private/tmp/hv-r20-* # expect none|
Response to the round 3 host-test review (Claude Code, claude-opus-5-5). 1. Fixture path (fixed in #127, 660eb76). Cargo runs integration tests from the package root, so 2. Handshake EOF (fixed in #126, 760de1c, plus #123, 01d4214). Root cause, verified: for Dropping the tmpfs left Host test, verified at 660eb76 (top of stack, after Refs: #117 |
tbhb-agent-reviewer
left a comment
There was a problem hiding this comment.
Harness: Claude Code; model: claude-opus-5-5; effort: high. Re-review of RFC-36 run 20 (#117), round 4.
Verdict: approve.
Unchanged since round 2 at 95443ff. All earlier items are resolved, and CI is green.
Host test passed (observed). I ran CARGO_BUILD_JOBS=2 mise run check:host at 660eb76 on an unsandboxed Mac (macOS 26.5.1, container 1.4.1), using one container. It exited 0: container_driver 1/1, launchd 9/9, seatbelt 4/4. Afterwards container ls -a showed only the stopped buildkit, container volume ls was empty, and no /private/tmp/hv-r20-* remained.
Runtime root without the tmpfs (observed in the live test VM hv-r20-25065). /run is root:root 0755 and /run/hypervisor is a root:root 0700 directory. As uid 10001, ls /run/hypervisor, touch /run/hypervisor/probe, touch /run/probe, and mkdir /run/hv2 all failed with Permission denied. So dropping the tmpfs exposes nothing new to session users: the directory is no more visible or writable than before, and the test now pins 700 0 itself. The agent creates and chmods the directory before any session exists, so no session can race the chmod.
## What Run guest-held sessions under distinct UIDs with a reviewer Landlock policy that handles write and mutation rights while leaving reads allowed. Check Linux file descriptor conversion, move guest environment and attach-path decisions into the pure core, and reject parent traversal in attach paths. ## Why The prior mask included read rights and would prevent reviewers from reading `/workspace`. Lexical path containment could also accept `/r/../../tmp`. ## Evidence - verified: `CARGO_BUILD_JOBS=2 mise run check` passed locally at `69a49a0`; the current CI `check` is green. - verified: Linux arm64 Clippy passed for `hypervisor-pty`, covering the formerly failing conversion and mask code. - verified: plain-value core tests cover path traversal, writer and reviewer environments, and the mask test excludes execute and read rights. - untested: the Apple container host fixture in #127 still needs `mise run check:host` with a free VM slot. ## Docs `specs/guest-agent-protocol.md` states the reviewer read policy and the shared-cache write risk. ## Checklist - [x] Green CI at the current head - [x] No secrets in the diff - [x] Docs updated - [x] Evidence linked Refs: #117
tbhb-agent-reviewer
left a comment
There was a problem hiding this comment.
Harness: Claude Code; model: claude-opus-5-5; effort: high. Re-approval of the collapsed RFC-36 run 20 stack (#117).
Verdict: approve.
Verified: git diff 660eb76 fd05750f is empty, so the tree is identical to the approved #127 head. That head contains the approved #122 95443ff, #123 01d4214, and #126 bfa655d. The merge base with origin/main is unchanged, so the combined diff origin/main...fd05750f adds nothing and loses nothing. CI check is green at fd05750f. The host test passed at the identical tree 660eb76 (container 1/1, launchd 9/9, seatbelt 4/4; no leftovers). The non-blocking runtime-root symlink thread from the #123 review still awaits an answer before resolution.
) ## What - State the broker socket contract in `specs/runtime-driver-contract.md`: the listener behind `/run/broker.sock` judges every request on its own. CONNECT is judged once per tunnel. Plain HTTP is judged per request on a kept-alive connection, or limited to one framed request per connection with `Connection: close` both ways and no bytes forwarded after it. ## Why RFC-37 run 11 found that `sandbox-runtime` 0.0.79 reuses one proxy connection across hosts, and run 8's stand-in broker judged only the first request, so a request to `example.com`, which was not on the allowlist, was forwarded (internal tbhb-dev/agent-orchestration-poc.internal#72). The container driver from #122 admits the socket mount but the spec said nothing about the listener. This repository has no broker yet, so no code changes; #131 keeps the required tests open for the broker PR. ## Evidence - **Verified:** the stand-in broker fix and its tests are on internal branch `broker-per-request-72` (commit `7c5b94ec`): a test runs the broker in front of a keep-alive stub and shows a second request to a disallowed host on the same connection is denied (9 of 9 pass; 3 of 4 socket tests fail on run 8's original). - **Verified:** `mise run check:vale`, `check:rumdl`, and `check:guard-markdown` pass locally, and the pre-commit hooks passed. `mise run check` (Rust build and tests) was not run locally for this Markdown-only change; CI `check` covers it. - **Inference, untested:** RFC-37 run 1's Pingora spike routes a whole connection by its first 8 bytes, so a CONNECT after a plain request on one connection would reach `HttpProxy`; #131 asks the broker PR to test that case. ## Docs `specs/runtime-driver-contract.md` updated. ## Checklist - [ ] Green CI - [x] No secrets - [x] Docs updated - [x] Evidence linked Refs: #131
What
Add the pure Apple container workload validator and deterministic CLI argument planner. Cover validation refusal branches, guest UID errors, and the CLI separator invariant. Refuse extra mounts that shadow a driver-owned guest path through an ancestor such as
/runor/.Why
RFC-36 run 20 needs a checked VM, mount, cache, and network plan before guest and host shells execute it.
Evidence
CARGO_BUILD_JOBS=2 mise run checkpassed locally at95443ff; the current CIcheckis green after the prior Codeberg fetch failure.95443ff.mise run check:hostafter the final stacked PR.Docs
specs/runtime-driver-contract.mdspecifies the CLI plan and its runtime test limit.Checklist
Refs: #117