Repository navigation
Conversation
Adapted from 8a146e3. Preserve the current runtime, pinned-image, qualification and transition models and canonical functional-schema identities while retaining the source-package inheritance seam.
…list
Allow callers to attach optional key/value string labels to VMs at
creation time, inherit or override them on fork, and return them on
every SandboxInfo entry in /vms/list and /vms/{id}/info so external
orchestrators and garbage collectors can identify their own VMs.
- capsem-api / capsem-service / OpenAPI: add optional labels
(Option<HashMap<String, String>>) to ProvisionRequest, ForkRequest,
SandboxInfo, VmInstance, SessionCreationParams, and PersistentVmEntry,
validated server-side via naming::validate_vm_labels (<= 64 entries,
keys reuse validate_vm_name: 1..=64 ASCII chars starting with
[A-Za-z0-9] and containing only [A-Za-z0-9_-]; values <= 255 UTF-8
bytes with no control chars) and normalized via
naming::non_empty_labels so {} is treated identically to None.
- Fork inheritance: /vms/{id}/fork inherits the source VM's labels when
ForkRequest.labels is omitted/null, replaces them when a non-empty map
is provided, and clears them when {} is provided.
- List cache fingerprint: include running and inactive VM labels in
list_response_fingerprint via append_labels_fingerprint so label
differences invalidate cached /vms/list responses.
- SDKs (Python, Rust, TypeScript): thread optional labels through
Hypervisor.create / CreateOptions and VM.fork / ForkOptions and
regenerate OpenAPI models across all three SDKs.
- CLI & TUI: add repeatable -l / --label KEY=VALUE to capsem create and
capsem fork, and display labels in the TUI session detail pane.
…nd discovery
Add structured error codes (`ErrorCode::VmNotFound`, `ErrorCode::CreateTimeout`,
`ErrorCode::ExecTimeout`) to `ErrorResponse` in `capsem-api` and `capsem-service`,
returning HTTP 504 (`StatusCode::GATEWAY_TIMEOUT`) with `timeout_secs` on guest
command execution timeouts and replacing the `pub fn AppError` constructor shim
with `AppError::new`.
In `sdk/python`:
- Parse `ErrorResponse` once in `HttpError.__init__` (`response` and `code`) and
dispatch `VmNotFoundError`, `CreateTimeoutError`, and `ExecTimeoutError`
(subclassing `CapsemTimeoutError(TimeoutError, CapsemError)`) from `_ERROR_DISPATCH`
keyed by `models.ErrorCode`, with pickle round-trip support (`__reduce__`/`__setstate__`).
- Add host gateway URL and bearer token discovery (`discover_gateway`,
`capsem_run_dir`, `Hypervisor.connect`). Note: `capsem_run_dir` is the one
client-side path helper retained as an exception to relying on the server for
path rules because `discover_gateway` resolves `~/.capsem/run/gateway.{port,token}`
on the host before any gateway connection exists; guest file path sanitization
is delegated entirely to the gateway (`POST/GET /vms/{id}/files/*`).
…ration Port the inspect-capsem Inspect AI SandboxEnvironment integration onto the 0.7 Python SDK surface in VM-only mode (container/image execution deferred to the follow-up container commit): - Split the implementation across focused modules under integrations/inspect-ai/inspect_capsem/ (config.py, _controller.py, _exec.py, _files.py, _lifecycle.py, _registry.py, _tools.py, _transfer.py, sandbox.py) with every module under 250 lines at 100 columns. - Encapsulate private CapsemSandboxEnvironment state inside sandbox.py, return SandboxConnection(type="capsem", command="capsem shell <id>"), and bound process-owned VM teardown at interpreter exit. - Derive staged file transfer part sizes from MAX_REQUEST_BODY_BYTES, scope VM cleanup to exact managed-by + prefix labels, and clean up unnamed VMs on 504 CreateTimeoutError via CreateTimeoutError.vm_id. - Wire integrations/inspect-ai into capsem-gate, CI scope routing, and live VM ironbank acceptance.
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 service parser supporting `image`, `command`/`entrypoint`, `environment`, `working_dir`, `user`, `volumes` (read-write and `:ro` bind mounts with symlink containment), `healthcheck`, `cpus`/`mem_limit`, and `x-default` / `default` service selection, with fail-closed validation on unsupported Compose keys and multi-service topologies. - `inspect_capsem/containers/runtime.py` + `controller.py`: OCI container staging (`stage_bind_mounts`, `wait_for_healthcheck`, `is_root_user_spec`) and `ContainerController` protocol. - `inspect_capsem/_config_io.py`, `_exec.py`, `_files.py`, `_lifecycle.py`, `config.py`, `sandbox.py`: thread container execution mode, non-root `user` execution (`su -m` / `setpriv`), and `/workspace` staging through `CapsemSandboxEnvironment`. - Unit tests (`tests/containers/*`, `tests/test_config.py`, `tests/test_sandbox*.py`) and live OCI container acceptance tests (`tests/live_acceptance.py`).
…pport
Add host-side Docker/OCI image build and loopback HTTPS OCI v2 registry support (`CapsemSandboxConfig(dockerfile=...)`, `CapsemSandboxConfig(build=...)`, string `"Dockerfile"` / `"Containerfile"` configs, and Compose `build:` / `dockerfile:` service blocks):
- `inspect_capsem/containers/image_build.py`: validate and normalize Compose `build:` / `dockerfile:` blocks with `CAPSEM_INSPECT_ALLOWED_HOST_PATHS` containment (`os.path.realpath`), compute deterministic SHA-256 cache keys over build context trees, run `docker build` + `docker image save` with `CAPSEM_INSPECT_BUILD_TIMEOUT` (default 600s) and `CAPSEM_INSPECT_HOST_BUILD` gating, and ingest saved tarballs into the content-addressed blob cache.
- `inspect_capsem/containers/oci_registry.py`: serve cached OCI v2 manifests and blobs over a loopback HTTPS registry (`127.0.0.1:5055` by default, `CAPSEM_INSPECT_BUILD_REGISTRY_PORT`) with `fcntl.flock`-guarded ephemeral TLS CA/leaf cert generation (`ensure_localhost_tls`).
- `inspect_capsem/_controller.py` + `_lifecycle.py`: pass `registry={"ca_pem": ...}` to `Hypervisor.create` and format actionable `~/.capsem/settings.toml` admission guidance on HTTP 403.
- Unit tests (`tests/containers/test_image_build.py`, `tests/containers/test_compose.py`, `tests/test_compose_config.py`) and user documentation (`web/docs/src/content/docs/usage/inspect-ai.md`).
❌ 6 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-containers
branch
from
October 8, 2026 13:07
99a088b to
3cb33cd
Compare
This was referenced Oct 8, 2026
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
inspect_aievaluations that specify aDockerfile,Containerfile,CapsemSandboxConfig(dockerfile=...),CapsemSandboxConfig(build=...), or Composebuild:block (stacked onfeat/inspect-capsem-containers). Like PR 3, this is Inspect-provider-only: it needs zerocapsem-service/ guest / SDK changes and reuses0.7's native image sessions.os.path.realpathcontainment on build contexts and Dockerfiles againstCAPSEM_INSPECT_ALLOWED_HOST_PATHS, computes deterministic SHA-256 cache keys over build context trees (with parallel build deduplication and uncacheable digest handling), and gatesdocker build/docker image saveonCAPSEM_INSPECT_HOST_BUILD(default1) andCAPSEM_INSPECT_BUILD_TIMEOUT(default600s).0.7(capsem-dev,google/gemini-3.5-flash):inspect_evals/vimgolf_single_turn --limit 2:status=success,2/2samples (0sample errors,accuracy=0.500,103s),0leftover VMs (and fails closed before VM creation whenCAPSEM_INSPECT_ALLOWED_HOST_PATHSis unset).inspect_evals/gdm_intercode_ctf --limit 2(sample_ids=[0, 2], Composebuild: .,max_sandboxes=2):status=success,2/2samples (0sample errors,accuracy=0.500,169s),0leftover VMs.inspect_harbor/terminal_bench_2(1/1sample,0sample errors,0leftover VMs).How It Works
capsem-servicecan only start an image session from an OCI reference it can pull through its host-side puller, so a locally built image has to be reachable as a registry. This PR keeps everything on the host and feeds the existing pull path:inspect_capsem.containers.image_buildrunsdocker build(with--platform,--target,args,dockerfile_inline) against the realpath-contained context, thendocker image save -o <tmp>/image.tar.oci_registry.ingest_docker_save_tarstreams the archive's config and layer blobs in 256 KiB chunks into~/.cache/capsem/inspect-oci-builds(CAPSEM_INSPECT_BUILD_CACHE_DIR), keyed by SHA-256, and writes an OCI image manifest (nothing is read whole into memory; identical build contexts dedupe on the cache key)./v2/<name>/manifests/<digest>and/v2/<name>/blobs/<digest>over HTTPS on127.0.0.1:5055(CAPSEM_INSPECT_BUILD_REGISTRY_PORT), using an ephemeral TLS CA + leaf certificate generated once per cache dir under anfcntl.flockso concurrent evals share it.Hypervisor.create(image="127.0.0.1:5055/inspect-capsem/build@sha256:<manifest>", registry=Registry(ca_pem=...))—capsem-servicepulls from the loopback registry with the supplied CA, unpacks in the guest, and the rest of the lifecycle is identical to PR 3'simage:path.Operator prerequisites (documented in
web/docs/.../inspect-ai.md):dockerandopensslonPATH, and an image source admission rule in~/.capsem/settings.toml([images] sources = [{ name = "local_inspect_builds", registries = ["127.0.0.1:5055"] }]+admit = [{ source = "local_inspect_builds", namespaces = ["inspect-capsem"], images = ["build"] }]). The docs also carry the trust caveat:DockerfileRUNsteps execute in the host Docker daemon, outside the micro-VM boundary, so untrusted benchmark Dockerfiles should only be built when that is acceptable, or disabled withCAPSEM_INSPECT_HOST_BUILD=0.Alternatives Considered / Potential Future Improvements
We kept host-side builds as a separate top-of-stack PR so the mechanism can be revisited without holding up PR 2b / PR 3. Two directions would simplify or improve it; neither is needed for this PR to work:
capsem-service(potential improvement): aPOST /images/import(or local OCI-layout reference) that ingests adocker image save/ OCI layout tarball straight into the host blob cache would remove the loopback HTTPS registry, the ephemeral TLS CA/leaf generation, and the~/.capsem/settings.toml[images]admission step frominspect-capsem, and would let admission policy see "local build" as a first-class source. Fits naturally with the image/cache service contracts in 0.7: Complete service image/cache and managed ephemeral-session contracts #301; we are happy to draft it if you prefer this shape long-term.Dockerfiles inside a dedicated Capsem builder VM (buildkitd/podman) would remove the host Docker daemon requirement and keepRUNsteps inside the VM boundary, at the cost of slower cold builds, a builder image to maintain, and a way to hand the result to the host cache (which again wants the import endpoint above). Rejected for now because it needs new runtime pieces, whereas the host-build path ships with0.7as is.