Repository navigation
Conversation
❌ 5 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
ebursztein
added a commit
that referenced
this pull request
Oct 7, 2026
Link assigned issue #342 and source PRs #338, #339 and #340. Keep completed parser components available while removing Inspect VM/workload and Dockerfile/Compose phases from this agent sprint. Shared 0.7 includes all carried code and latest main; complete integration and runtime/package qualification remain Pierre’s work.
tholop
added a commit
that referenced
this pull request
Oct 8, 2026
Add OCI container execution mode (`execution_mode="container"` / `execution_mode="auto"`, `image=...`, and `compose.yaml` / `docker-compose.yml` config parsing) on top of Capsem VM sandboxes: - `inspect_capsem/containers/compose.py` + `compose_fields.py` + `compose_inputs.py` + `compose_interpolation.py` + `compose_service.py` + `compose_values.py`: single bounded Compose parser built on `ComposeInputs`, `ComposeLimits` / `_BoundedSafeLoader`, and `InterpolationBudget` with redacted diagnostics, default-deny host environment interpolation (`SAMPLE_METADATA_*` allowlist with `.env` spoofing rejection), support for `image`, `command`/`entrypoint`, `environment`, `working_dir`, `user`, `volumes` (read-write and `:ro` bind mounts with symlink and project-root containment), `healthcheck`, `cpus`/`mem_limit`, and fail-closed validation on unsupported service keys, network isolation overrides, and multi-service topologies. - `inspect_capsem/containers/runtime.py` + `controller.py`: OCI container staging (`prepare_oci_workload_container`, `_stage_oci_bind_volumes`) and `ContainerController` protocol. - `inspect_capsem/_compose.py`, `_controller.py`, `_exec.py`, `_files.py`, `_lifecycle.py`, `config.py`, `sandbox.py`: thread container execution mode, non-root `user` execution (`su -m` / `setpriv`), `/workspace` staging, and `SdkCapsemController` `registry_ca_pem` / `Registry(ca_pem=...)` plumbing (moved from PR #340 into PR #339 so #339's hermetic loopback TLS OCI workload fixture authenticates without #340 while `CapsemSandboxConfig` continues to reject `registry_ca_pem` in untrusted task configs). - Unit tests (`tests/containers/*`, `tests/test_config.py`, `tests/test_sandbox*.py`) and hermetic loopback TLS OCI workload acceptance (`tests/oci_workload_fixture.py`, `tests/live_acceptance.py`). Proves #310 / #311 / #342 acceptance criteria: - [x] Compose config coercion (`compose.yaml` / `docker-compose.yml`) and `CapsemSandboxConfig(image=...)` select `execution_mode="container"` and route `exec` to `ExecTarget.WORKLOAD`. - [x] Hermetic OCI workload acceptance (`tests/oci_workload_fixture.py` + `tests/live_acceptance.py`) builds a digest-pinned OCI image from the guest initrd busybox, serves it over loopback TLS with a per-run CA passed via `SdkCapsemController(registry_ca_pem=...)`, admits its digest in `settings.toml`, runs `sample_init`, `exec` (`ExecTarget.WORKLOAD`), `write_file`/`read_file` (text and binary), and `eval_async` with `SandboxEnvironmentSpec("capsem", ...)` without pulling from Docker Hub, and verifies `history(layer=EXEC)` + `session.db` `exec_events` (`target="workload"`) and zero leaked VMs after `sample_cleanup`.
tholop
force-pushed
the
feat/inspect-capsem
branch
from
October 8, 2026 13:07
6f9656f to
8708553
Compare
tholop
force-pushed
the
feat/inspect-capsem-containers
branch
from
October 8, 2026 13:07
99a088b to
3cb33cd
Compare
This was referenced Oct 8, 2026
Add OCI container execution mode (`execution_mode="container"`, `image=...`, and `compose.yaml` / `docker-compose.yml` config parsing) on top of Capsem VM sandboxes: - `inspect_capsem/containers/compose.py` + `compose_fields.py` + `compose_inputs.py` + `compose_interpolation.py` + `compose_service.py` + `compose_values.py`: single bounded Compose parser built on `ComposeInputs`, `ComposeLimits` / `_BoundedSafeLoader`, and `InterpolationBudget` with redacted diagnostics, default-deny host environment interpolation (`SAMPLE_METADATA_*` allowlist with `.env` spoofing rejection), support for `image`, `command`/`entrypoint`, `environment`, `working_dir`, `user`, `volumes` (read-write and `:ro` bind mounts with symlink and project-root containment), `healthcheck`, `cpus`/`mem_limit`, and fail-closed validation on unsupported service keys, network isolation overrides, and multi-service topologies. - `inspect_capsem/containers/runtime.py` + `controller.py`: OCI container staging (`prepare_oci_workload_container`, `_stage_oci_bind_volumes`) and `ContainerController` protocol. - `inspect_capsem/_compose.py`, `_controller.py`, `_exec.py`, `_files.py`, `_lifecycle.py`, `config.py`, `sandbox.py`: thread container execution mode, non-root `user` execution (`su -m` / `setpriv`, skipping privilege switching when already running at target UID/GID inside non-root workloads and raising `PermissionError` when requesting a root or different user switch inside a `no-new-privileges` non-root workload), `/workspace` staging, and `SdkCapsemController` `registry_ca_pem` / `Registry(ca_pem=...)` plumbing (moved from PR #340 into PR #339 so #339's hermetic loopback TLS OCI workload fixture authenticates without #340 while `CapsemSandboxConfig` continues to reject `registry_ca_pem` in untrusted task configs). - Unit tests (`tests/containers/*`, `tests/test_config.py`, `tests/test_sandbox*.py`) and hermetic loopback TLS OCI workload acceptance (`tests/oci_workload_fixture.py`, `tests/live_acceptance.py`). Proves #310 / #311 / #342 acceptance criteria: - [x] Compose config coercion (`compose.yaml` / `docker-compose.yml`) and `CapsemSandboxConfig(image=...)` select `execution_mode="container"` and route `exec` to `ExecTarget.WORKLOAD`. - [x] Hermetic OCI workload acceptance (`tests/oci_workload_fixture.py` + `tests/live_acceptance.py`) builds a digest-pinned OCI image from the guest initrd busybox, serves it over loopback TLS with a per-run CA passed via `SdkCapsemController(registry_ca_pem=...)`, admits its digest in `settings.toml`, runs `sample_init`, `exec` (`ExecTarget.WORKLOAD`), `write_file`/`read_file` (text and binary), and `eval_async` with `SandboxEnvironmentSpec("capsem", ...)` without pulling from Docker Hub, and verifies `history(layer=EXEC)` + `session.db` `exec_events` (`target="workload"`) and zero leaked VMs after `sample_cleanup`.
tholop
force-pushed
the
feat/inspect-capsem
branch
from
October 9, 2026 15:49
8708553 to
ec91d32
Compare
tholop
force-pushed
the
feat/inspect-capsem-containers
branch
from
October 9, 2026 15:49
3cb33cd to
6ea58de
Compare
tholop
marked this pull request as draft
October 9, 2026 15:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lets Inspect AI evaluations that declare a Docker image or a single-service
compose.yamlrun on Capsem0.7's native OCI container runtime (Hypervisor.create(image=...)andExecTarget.WORKLOAD), without an in-guest Docker daemon (tracking issue #311, handoff issue #342). This PR only touches the Inspect AI provider (integrations/inspect-ai) and its hermetic gate fixture (tests/ironbank/test_sdk_live.py); it adds no container functionality tocapsem-service, the guest runtime, or the SDKs. Stacked on #338.Changes
0.7container translation (integrations/inspect-ai/inspect_capsem/):execution_mode="container"andimage: str | None = NonetoCapsemSandboxConfig(requiring an explicitimageorcompose_filewhenexecution_mode="container", with no ambient default image) and deletes the legacy0.6in-guestdockerdbackend.0.7's bounded single-service Compose parser (ComposeInputs,ComposeLimits/_BoundedSafeLoader,InterpolationBudget,compose_service.py) to mapimage,command/entrypoint,environment,working_dir,user, bind and declared empty namedvolumes,healthcheck, and${VAR}interpolation (SAMPLE_METADATA_*) ontoHypervisor.create(image=...)plusExecTarget.WORKLOADexecs.0.7cannot honour (network_mode: none, multi-service topologies,portsmappings, undeclared/external named volumes, anonymous single-target volumes, andbuild:/Dockerfilebefore feat(inspect-ai): add host-side Dockerfile and Compose build image support #340;init: trueandexposeemit an explicit warning and are stripped) — see Known Limitations below./workspace, preserves file modes on single-file and directory bind mounts, creates empty target directories for declared top-level namedvolumes, and stages tool bundles (onedir) with0700permissions.userexecution (su -m/setprivwhen the container runs as root; when the imageUSERis already non-root under0.7'sno-new-privileges,exec(user=<matching image user>)succeeds as a no-op with normalizedUSER/LOGNAME/HOME, whileexec(user="root")or switching to a different user fails loudly withPermissionError).SAMPLE_METADATA_*allowlist with.envspoofing rejection), symlink-safe bind-mount containment (os.path.realpathwithin the project directory, refusing any bind whose host source or container target basename isdocker.sock), and operator-onlySdkCapsemController(registry_ca_pem=...)constructor injection (rejected in task configs).CAPSEM_INSPECT_ALLOWED_HOST_ENVandCAPSEM_INSPECT_ALLOWED_HOST_PATHS; task configs (CapsemSandboxConfig.allowed_host_envandallowed_host_paths) can only narrow — never widen — the operator's settings.Installed-Package Qualification & Live Benchmarks (#342)
integrations/inspect-ai/tests,linux/x86_64, Python3.12.14):ruff check,ruff format --check, andty check --error-on-warning --python-platform all: PASS (0findings).pytest integrations/inspect-ai/tests -q: 92 passed (97%line coverage), coveringtests/containers/*, offline wheel + sdist installation (test_package_install.py), non-root containerexec(user=...)identity checks, and fast-failValueErrorrefusal on Composebuild:/Dockerfile.tests/ironbank/test_sdk_live.py::test_inspect_ai_live_vm_sandbox_acceptance,134.57s):capsem-0.7.0wheel +inspect-capsem-sandbox-0.1.0wheel and sdist offline (--no-deps,--no-install-local) into isolated prefixes outside the repo and runsimage_package_acceptance.pyandlive_acceptance.pyunderpython -I. Both archives emittedSDK_IMAGE_PACKAGE_ACCEPTANCE_OK,INSPECT_CAPSEM_CONTAINER_ACCEPTANCE_OK, andINSPECT_CAPSEM_VM_ACCEPTANCE_OK.tests/oci_workload_fixture.py) over a loopback TLS registry with a throwaway CA (SdkCapsemController(registry_ca_pem=ca_pem), zero public network access), runssample_init(image=...),ExecTarget.WORKLOADexecs, text/binary file roundtrips, and real Inspecteval_async(accuracy=1.000), verifieshistory(layer=EXEC)+session.dbexec_events(target="vm"andtarget="workload"), and confirms foreign persistent VM survival and0leaked VMs.tests/ironbank/test_sdk_live.py(test_inspect_ai_live_vm_sandbox_acceptance)0.7VM + hermetic OCI workload)eval_async+ 2self_check(43/43, large-binary skipped)134.57s0SDK_IMAGE_PACKAGE_ACCEPTANCE_OK+INSPECT_CAPSEM_CONTAINER_ACCEPTANCE_OK+INSPECT_CAPSEM_VM_ACCEPTANCE_OKintegrations/inspect-ai/tests/live_acceptance.py0.7VM + OCI container44/44VM +44/44containerself_check)121.16s0/workspacewrite+read, non-rootdeveloper(1000:1000) chown/exec/env,onedir0700, and numeric1000:1000Compose userinspect_evals/humanevaldefault)success(0errors)12s00.7assetsinspect_evals/gsm8kdefault)success(0errors)12s00.7assetsinspect_evals/agentdojo(with_injections=false)default)success(0errors)11s00.7assetscompose_bind_smokepython:3.11-slim)success(accuracy=1.000,0errors)17s0${SAMPLE_METADATA_*}in OCI workload containerinspect_harbor/terminal_bench_2(build-pov-ray)image:)success(0errors)43s0bashtool loop +harbor_scorerbigcodebench/swe_bench(default) /cybench(2-service)ValueError6s–7s0network_mode: noneand multi-service Compose before VM creationinspect_evals/swe_bench_verified_mini(-T allow_internet=true)~655 MBcompressed /~1.5 GBuncompressed)3/3completed (2/3 = 0.667score) only with a locally patched service (launch.py:484timeout=30 -> 300, raisedCREATE_READY_TIMEOUT); fails on stock0.7(HTTP 500 / 504 during image unpack)902s0Known Limitations & Follow-Ups (Non-Blocking)
Single-service container evals whose images fit the stock
0.7pull/unpack budget and use default networking work end to end (terminal_bench_2,compose_bind_smoke, and the hermetic OCI workload acceptance above run on unpatched0.7). The items below reflect what0.7exposes today:0.7create budget — e.g.swe_bench_verified_mini(~655 MBcompressed / ~1.5 GBuncompressed). Warm host cache: ~36sinlaunch.py:assemble()plus> 30sinumoci unpacktripssubprocess.run(..., timeout=30)atguest/artifacts/container/launch.py:484(HTTP 500 after68s). Cold cache: pull (104–108s) + unpack exceedsCREATE_READY_TIMEOUT = 110s(crates/capsem-service/src/container_setup.rs:28, HTTP 504create_timeout;inspect-capsemdeletes the half-created VM viaCreateTimeoutError.vm_id). Typical eval images (< 500 MB) are unaffected;POST /images/pull(0.7: Polish Python SDK for the image runtime #303) can pre-warm the host cache ahead ofcreate, and Minimal Capsem runtime with OCI application images (replaces profiles) #289's planned.erofs/ unpack-once path removes most of the unpack cost.oci-cache-lock30s stripe-lock contention under concurrent creates of the same image —Puller::blob(crates/capsem-assets/src/oci/pull.rs:570) acquires the exclusive per-digest blob stripe lockcache.lease(&descriptor.digest)(locks/<hex[..2]>.lock,LockMode::Exclusive) before callingBlobCache::copy_hit(crates/capsem-assets/src/oci/cache.rs:219-246) and holds it across the entireverified_copySHA-256 read/write pass even aftercopy_hitopens the read-only blob descriptor and dropslookup_lease; meanwhilelock_with(cache.rs:488) pollsoci-cache-lockfor30sinstead ofPULL_TIMEOUT(300s). On stock0.7, in a 4-way concurrent evaluation (max_sandboxes=4) on a400 MiBimage,3/4samples fail after30swithOCI cache lock timed outon cold cache and2/4–3/4fail on warm cache (0leaked VMs). A small standalone service fix PR (fix/oci-cache-lock-lease) releases the stripe lock incopy_hit_with_leaseas soon asopen_fileopens the cached blob descriptor and alignslock_withwithPULL_TIMEOUT(300s), taking both cold and warm 4-way creates to4/4passing (0errors).ProvisionRequest— benchmarks whose Compose file setsnetwork_mode: none(swe_bench_verified_minidefaultallow_internet=Falseandbigcodebench) are rejected at parse time rather than silently given network access, because0.7has no per-VM egress control (swe_bench_verified_miniruns with-T allow_internet=true).inspect-capsemfails closed when a Compose file defines more than one service (e.g.cybenchdefault+victim), matching Minimal Capsem runtime with OCI application images (replaces profiles) #289's single-workload scope for0.7.build:is layered on top in feat(inspect-ai): add host-side Dockerfile and Compose build image support #340 (Integrate and qualify Inspect AI on current 0.7 (Pierre handoff) #342 step 4) — in this PR (feat(inspect-ai): add OCI container execution mode and Compose parser #339),compose_fields.py,_compose.py, andconfig.pyfail fast withValueErrorwhen a task specifiesbuild:/Dockerfileinstead of a pre-builtimage:. feat(inspect-ai): add host-side Dockerfile and Compose build image support #340 stacks on this PR to add the default-offHostBuildGrant(CAPSEM_INSPECT_HOST_BUILD=1) build path.