diff --git a/experiments/perception/worker/observatory_portable/Dockerfile.worker-006-agent b/experiments/perception/worker/observatory_portable/Dockerfile.worker-006-agent new file mode 100644 index 0000000..22a495f --- /dev/null +++ b/experiments/perception/worker/observatory_portable/Dockerfile.worker-006-agent @@ -0,0 +1,57 @@ +FROM nvcr.io/nvidia/tritonserver:26.06-py3@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794 + +ARG NODEDC_SOURCE_REVISION +ARG NODEDC_BUILD_CONTEXT_SHA256 +ARG SOURCE_DATE_EPOCH + +ENV PYTHONPATH=/opt/nodedc/mission-core/src \ + PYTHONNOUSERSITE=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /opt/nodedc/mission-core + +COPY src/k1link ./src/k1link +COPY config ./config +COPY experiments/perception/worker/observatory_portable/worker-006-agent-build-context.json \ + ./release/worker-006-agent-build-context.json + +RUN case "${NODEDC_SOURCE_REVISION}" in \ + *[!0-9a-f]* ) exit 64 ;; \ + * ) ;; \ + esac \ + && test "${#NODEDC_SOURCE_REVISION}" -eq 40 \ + && case "${NODEDC_BUILD_CONTEXT_SHA256}" in \ + *[!0-9a-f]* ) exit 64 ;; \ + * ) ;; \ + esac \ + && test "${#NODEDC_BUILD_CONTEXT_SHA256}" -eq 64 \ + && case "${SOURCE_DATE_EPOCH}" in \ + ''|*[!0-9]* ) exit 64 ;; \ + * ) ;; \ + esac \ + && test "${SOURCE_DATE_EPOCH}" -gt 0 \ + && find /opt/nodedc/mission-core -type d -exec chmod 0555 {} + \ + && find /opt/nodedc/mission-core -type f -exec chmod 0444 {} + \ + && python3 -c "import k1link.observatory.m49_worker_container_main as entrypoint; import k1link.observatory.m49_worker_service as composition; assert callable(entrypoint.main); assert callable(composition.compose_installed_m49_worker_service)" + +LABEL org.opencontainers.image.title="NODE.DC Observatory Worker 006 agent" \ + org.opencontainers.image.source="NODEDC_MISSION_CORE" \ + org.opencontainers.image.revision="${NODEDC_SOURCE_REVISION}" \ + com.nodedc.product="mission-core" \ + com.nodedc.stack="ndc-mission-core-observatory" \ + com.nodedc.role="observatory-worker-agent" \ + com.nodedc.managed-by="mission-core-worker-release" \ + com.nodedc.worker-contour="worker-006" \ + com.nodedc.authority="observation-only" \ + com.nodedc.models="external" \ + com.nodedc.base-image.sha256="58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794" \ + com.nodedc.build-context.sha256="${NODEDC_BUILD_CONTEXT_SHA256}" + +# The current private token and sealed release mounts are provisioned for root. +# Runtime confinement is mandatory in the install plan: read-only rootfs, +# cap-drop ALL, no-new-privileges, bounded resources and no device requests. +USER 0:0 + +ENTRYPOINT ["python3", "-m", "k1link.observatory.m49_worker_container_main"] +CMD [] diff --git a/experiments/perception/worker/observatory_portable/worker-006-agent-build-context.json b/experiments/perception/worker/observatory_portable/worker-006-agent-build-context.json new file mode 100644 index 0000000..ba75be0 --- /dev/null +++ b/experiments/perception/worker/observatory_portable/worker-006-agent-build-context.json @@ -0,0 +1,76 @@ +{ + "schema_version": "missioncore.observatory-worker-agent-build-context/v1", + "artifact_id": "observatory-worker-006-agent", + "worker_id": "worker-006", + "source_contract": { + "required_state": "clean-committed-snapshot", + "revision_format": "full-git-sha1", + "materialization": "git-archive", + "timestamp_source": "selected-commit-committer-epoch", + "uncommitted_files_allowed": false + }, + "base_image": { + "reference": "nvcr.io/nvidia/tritonserver:26.06-py3@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794", + "sha256": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794", + "pull_allowed": false + }, + "dockerfile": "experiments/perception/worker/observatory_portable/Dockerfile.worker-006-agent", + "context_digest": { + "algorithm": "sha256", + "subject": "git-archive-tar-bytes", + "archive_format": "tar", + "path_order": "git-archive-native-order" + }, + "context_entries": [ + { + "path": "src/k1link", + "kind": "tree", + "image_path": "/opt/nodedc/mission-core/src/k1link" + }, + { + "path": "config", + "kind": "tree", + "image_path": "/opt/nodedc/mission-core/config" + }, + { + "path": "experiments/perception/worker/observatory_portable/Dockerfile.worker-006-agent", + "kind": "file", + "image_path": null + }, + { + "path": "experiments/perception/worker/observatory_portable/worker-006-agent-build-context.json", + "kind": "file", + "image_path": "/opt/nodedc/mission-core/release/worker-006-agent-build-context.json" + } + ], + "required_configuration": [ + "config/observatory-portable-run-definitions.json", + "config/observatory-worker-runtime-candidates.json", + "config/perception/m49-tgs-portable-v2.json", + "config/perception/lab-v1-eomt-ddrnet-portable-v2.json" + ], + "runtime": { + "pythonpath": "/opt/nodedc/mission-core/src", + "entrypoint": [ + "python3", + "-m", + "k1link.observatory.m49_worker_container_main" + ], + "composition_module": "k1link.observatory.m49_worker_service", + "models_baked_into_image": false, + "executor_releases_baked_into_image": false + }, + "forbidden_context": [ + "model weights", + "recorded K1 sessions", + "worker bearer credentials", + "executor release payloads", + "runtime work directories" + ], + "authority": { + "commands_enabled": false, + "actuation_allowed": false, + "navigation_or_safety_accepted": false, + "production_accepted": false + } +} diff --git a/experiments/perception/worker/observatory_portable/worker-006-agent-install-plan.template.json b/experiments/perception/worker/observatory_portable/worker-006-agent-install-plan.template.json new file mode 100644 index 0000000..4c6feba --- /dev/null +++ b/experiments/perception/worker/observatory_portable/worker-006-agent-install-plan.template.json @@ -0,0 +1,227 @@ +{ + "schema_version": "missioncore.observatory-worker-agent-install-plan/v1", + "state": "planned-not-built", + "artifact_id": "observatory-worker-006-agent", + "worker_id": "worker-006", + "build": { + "source_revision": null, + "source_revision_requirement": "full clean committed Git revision", + "source_date_epoch": null, + "build_context_sha256": null, + "context_manifest": "experiments/perception/worker/observatory_portable/worker-006-agent-build-context.json", + "dockerfile": "experiments/perception/worker/observatory_portable/Dockerfile.worker-006-agent", + "materialization": { + "method": "git archive", + "include_only_context_manifest_entries": true, + "reject_dirty_worktree": true, + "archive_format": "tar", + "archive_paths_source": "context manifest context_entries in declared order", + "build_context_sha256_subject": "exact git-archive tar bytes" + }, + "base_image": { + "reference": "nvcr.io/nvidia/tritonserver:26.06-py3@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794", + "sha256": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794", + "must_exist_locally": true, + "pull_allowed": false + }, + "docker_build": { + "network": "none", + "pull": false, + "no_cache": true, + "provenance": false, + "platform": "linux/amd64", + "context_input": "exact git-archive tar bytes", + "required_build_args": [ + "NODEDC_SOURCE_REVISION", + "NODEDC_BUILD_CONTEXT_SHA256", + "SOURCE_DATE_EPOCH" + ] + }, + "required_preflight": [ + "Git worktree and index are clean", + "selected revision resolves to exactly one commit", + "selected revision equals HEAD", + "base image inspect ID equals the pinned SHA-256", + "context archive contains exactly the context manifest entries" + ], + "image_tag_template": "ndc/mission-core-observatory-worker-agent:" + }, + "build_acceptance": { + "required_labels": { + "org.opencontainers.image.revision": "", + "com.nodedc.product": "mission-core", + "com.nodedc.stack": "ndc-mission-core-observatory", + "com.nodedc.role": "observatory-worker-agent", + "com.nodedc.worker-contour": "worker-006", + "com.nodedc.authority": "observation-only", + "com.nodedc.models": "external", + "com.nodedc.base-image.sha256": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794", + "com.nodedc.build-context.sha256": "" + }, + "smoke": { + "network": "none", + "read_only_rootfs": true, + "platform": "linux/amd64", + "entrypoint": [ + "python3", + "-c", + "import k1link.observatory.m49_worker_container_main as e; import k1link.observatory.m49_worker_service as c; assert callable(e.main); assert callable(c.compose_installed_m49_worker_service)" + ], + "expected_result": "exit-0" + } + }, + "runtime": { + "container_name": "ndc-observatory-worker-006-agent", + "fixed_environment": { + "MISSIONCORE_OBSERVATORY_WORKER_BASE_URL": "http://127.0.0.1:18080", + "MISSIONCORE_OBSERVATORY_WORKER_TOKEN_FILE": "/run/secrets/observatory-worker.token", + "MISSIONCORE_OBSERVATORY_WORKER_WORK_ROOT": "/work", + "MISSIONCORE_OBSERVATORY_WORKER_DEFINITIONS_FILE": "/opt/nodedc/mission-core/config/observatory-portable-run-definitions.json", + "MISSIONCORE_OBSERVATORY_WORKER_RUNTIME_REGISTRY_FILE": "/opt/nodedc/mission-core/config/observatory-worker-runtime-candidates.json", + "MISSIONCORE_OBSERVATORY_M49_INSTALLATION_RECEIPT_FILE": "/release/worker-installation-receipt.json" + }, + "user": { + "uid": 0, + "gid": 0, + "reason": "existing private token and sealed executor-release mounts are root-readable; changing their ownership is a separate reviewed runtime transition" + }, + "hardening": { + "read_only_rootfs": true, + "cap_drop": [ + "ALL" + ], + "no_new_privileges": true, + "init": true, + "pids_limit": 256, + "cpus": 12, + "memory_bytes": 17179869184, + "tmpfs": [ + "/tmp:rw,nosuid,nodev,noexec,size=256m" + ], + "published_ports": [], + "device_requests": [] + }, + "docker_engine_control": { + "transport": "Docker Engine HTTP API over Unix domain socket; Docker CLI is not used", + "authority": "host-equivalent Docker Engine control", + "risk": "a compromised Worker agent can control the Docker host; agent namespace and rootfs hardening do not reduce authority granted by this socket", + "socket_mount": { + "type": "bind", + "host_path": "/var/run/docker.sock", + "container_path": "/var/run/docker.sock", + "mode": "read-write", + "exception_to_read_only_mount_policy": true + }, + "allowed_operations": [ + "inspect exact installed image identity", + "create one sealed LAB V1 sibling", + "start exact created container ID", + "wait exact created container ID", + "read bounded tail logs for failed exact created container ID", + "force-delete exact created container ID" + ], + "sibling_runtime_fences": { + "exact_image_sha256s": { + "eomt": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794", + "ddrnet": "591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd" + }, + "commands_and_entrypoints": "exact values sealed in the LAB V1 runner installation receipt; never supplied by a queued job", + "bind_sources": "only exact Worker-host asset roots sealed in the LAB V1 runner installation receipt and descendants of its exact work-root binding", + "bind_targets": "fixed /opt/nodedc/assets and /run/nodedc targets; exactly one writable target at /run/nodedc/output", + "network": "none", + "read_only_rootfs": true, + "cap_drop": [ + "ALL" + ], + "no_new_privileges": true, + "privileged": false, + "init": true, + "pids_limit": 512, + "memory_swap_equals_memory": true, + "memory_bytes": "exact sealed component value; admitted range 1073741824..137438953472", + "nano_cpus": "exact sealed component value; admitted range 100000000..64000000000", + "timeout_seconds": "exact sealed component value; admitted range 1..86400", + "gpu_request": { + "driver": "nvidia", + "count": 1, + "capabilities": [ + [ + "gpu" + ] + ] + }, + "required_unique_labels": [ + "com.nodedc.authority=observation-only", + "com.nodedc.component=", + "com.nodedc.definition-sha256=", + "com.nodedc.managed-by=mission-core-worker", + "com.nodedc.request-sha256=", + "com.nodedc.product=mission-core", + "com.nodedc.release-candidate-sha256=", + "com.nodedc.stack=observatory" + ], + "name_template": "ndc-observatory-lab-v1--", + "cleanup": "always force-delete only the exact 64-hex container ID returned by the successful create response" + } + }, + "mounts": [ + { + "container_path": "/release", + "mode": "read-only", + "purpose": "sealed external executor release" + }, + { + "container_path": "/work", + "mode": "read-write", + "purpose": "bounded job workspace" + }, + { + "container_path": "/run/secrets", + "mode": "read-only", + "purpose": "private bearer token file" + } + ], + "external_assets": { + "model_images": "pre-existing exact local Docker images selected only by installed executor adapters", + "model_weights": "pre-existing exact local read-only assets selected only by installed executor adapters", + "baked_into_agent_image": false, + "queued_jobs_may_select_paths_or_commands": false + }, + "lab_v1_runner_installation_input": { + "state": "loader-contract-not-yet-defined", + "environment_variable": null, + "container_path": null, + "reason": "the current local runner module defines an in-memory sealed installation receipt but no environment loader or installed receipt path" + }, + "protected_contours": [ + "K1 connection and control", + "Simulation and Gaussian", + "legacy LAB and Rerun profiles" + ] + }, + "receipt_skeleton": { + "schema_version": "missioncore.observatory-worker-agent-image-receipt/v1", + "receipt_state": "not-built", + "worker_id": "worker-006", + "source_revision": null, + "source_date_epoch": null, + "build_context_sha256": null, + "base_image_sha256": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794", + "image": { + "tag": null, + "id": null, + "size_bytes": null + }, + "smoke": { + "network": "none", + "result": "not-run" + }, + "models_baked_into_image": false, + "authority": { + "commands_enabled": false, + "actuation_allowed": false, + "navigation_or_safety_accepted": false, + "production_accepted": false + } + } +} diff --git a/tests/test_observatory_worker_agent_image_artifact.py b/tests/test_observatory_worker_agent_image_artifact.py new file mode 100644 index 0000000..b95580c --- /dev/null +++ b/tests/test_observatory_worker_agent_image_artifact.py @@ -0,0 +1,277 @@ +from __future__ import annotations + +import json +import re +from pathlib import Path +from typing import cast + +from k1link.observatory import portable_lab_v1_local_runners as local_runners + +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +ARTIFACT_ROOT = ( + REPOSITORY_ROOT / "experiments" / "perception" / "worker" / "observatory_portable" +) +DOCKERFILE = ARTIFACT_ROOT / "Dockerfile.worker-006-agent" +CONTEXT_MANIFEST = ARTIFACT_ROOT / "worker-006-agent-build-context.json" +INSTALL_PLAN = ARTIFACT_ROOT / "worker-006-agent-install-plan.template.json" +BASE_SHA256 = "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794" +BASE_REFERENCE = f"nvcr.io/nvidia/tritonserver:26.06-py3@sha256:{BASE_SHA256}" +ENTRYPOINT = ["python3", "-m", "k1link.observatory.m49_worker_container_main"] +AUTHORITY = { + "commands_enabled": False, + "actuation_allowed": False, + "navigation_or_safety_accepted": False, + "production_accepted": False, +} + + +def _document(path: Path) -> dict[str, object]: + value: object = json.loads(path.read_text(encoding="utf-8")) + assert isinstance(value, dict) + return cast(dict[str, object], value) + + +def test_agent_dockerfile_is_offline_commit_bound_and_model_free() -> None: + payload = DOCKERFILE.read_text(encoding="utf-8") + + assert payload.splitlines()[0] == f"FROM {BASE_REFERENCE}" + assert "ARG NODEDC_SOURCE_REVISION" in payload + assert "ARG NODEDC_BUILD_CONTEXT_SHA256" in payload + assert "ARG SOURCE_DATE_EPOCH" in payload + assert "org.opencontainers.image.revision=\"${NODEDC_SOURCE_REVISION}\"" in payload + assert "com.nodedc.build-context.sha256=\"${NODEDC_BUILD_CONTEXT_SHA256}\"" in payload + assert "com.nodedc.models=\"external\"" in payload + assert "PYTHONPATH=/opt/nodedc/mission-core/src" in payload + assert 'ENTRYPOINT ["python3", "-m", "k1link.observatory.m49_worker_container_main"]' in payload + assert "CMD []" in payload + assert "COPY src/k1link ./src/k1link" in payload + assert "COPY config ./config" in payload + assert "COPY . ." not in payload + assert re.search(r"(?im)^\s*(?:run\s+)?(?:apt|apk|yum|dnf|pip|uv)\b", payload) is None + assert "curl " not in payload.lower() + assert "wget " not in payload.lower() + assert "git clone" not in payload.lower() + + +def test_build_context_is_only_repo_source_configuration_and_artifact_contract() -> None: + document = _document(CONTEXT_MANIFEST) + + assert document["schema_version"] == "missioncore.observatory-worker-agent-build-context/v1" + assert document["worker_id"] == "worker-006" + assert document["authority"] == AUTHORITY + source = cast(dict[str, object], document["source_contract"]) + assert source == { + "required_state": "clean-committed-snapshot", + "revision_format": "full-git-sha1", + "materialization": "git-archive", + "timestamp_source": "selected-commit-committer-epoch", + "uncommitted_files_allowed": False, + } + context_digest = cast(dict[str, object], document["context_digest"]) + assert context_digest == { + "algorithm": "sha256", + "subject": "git-archive-tar-bytes", + "archive_format": "tar", + "path_order": "git-archive-native-order", + } + base = cast(dict[str, object], document["base_image"]) + assert base == { + "reference": BASE_REFERENCE, + "sha256": BASE_SHA256, + "pull_allowed": False, + } + + entries = cast(list[dict[str, object]], document["context_entries"]) + paths = [cast(str, row["path"]) for row in entries] + assert paths == [ + "src/k1link", + "config", + "experiments/perception/worker/observatory_portable/Dockerfile.worker-006-agent", + "experiments/perception/worker/observatory_portable/worker-006-agent-build-context.json", + ] + for relative in paths: + assert (REPOSITORY_ROOT / relative).exists() + assert not any( + token in relative.lower() + for relative in paths + for token in ("model", "weight", "session", "credential", "runtime") + ) + + required = cast(list[str], document["required_configuration"]) + assert required == [ + "config/observatory-portable-run-definitions.json", + "config/observatory-worker-runtime-candidates.json", + "config/perception/m49-tgs-portable-v2.json", + "config/perception/lab-v1-eomt-ddrnet-portable-v2.json", + ] + assert all((REPOSITORY_ROOT / relative).is_file() for relative in required) + runtime = cast(dict[str, object], document["runtime"]) + assert runtime["entrypoint"] == ENTRYPOINT + assert runtime["composition_module"] == "k1link.observatory.m49_worker_service" + assert runtime["models_baked_into_image"] is False + assert runtime["executor_releases_baked_into_image"] is False + + +def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receipt() -> None: + document = _document(INSTALL_PLAN) + + assert document["schema_version"] == "missioncore.observatory-worker-agent-install-plan/v1" + assert document["state"] == "planned-not-built" + build = cast(dict[str, object], document["build"]) + assert build["source_revision"] is None + assert build["source_date_epoch"] is None + assert build["build_context_sha256"] is None + materialization = cast(dict[str, object], build["materialization"]) + assert materialization == { + "method": "git archive", + "include_only_context_manifest_entries": True, + "reject_dirty_worktree": True, + "archive_format": "tar", + "archive_paths_source": "context manifest context_entries in declared order", + "build_context_sha256_subject": "exact git-archive tar bytes", + } + base = cast(dict[str, object], build["base_image"]) + assert base["reference"] == BASE_REFERENCE + assert base["sha256"] == BASE_SHA256 + assert base["must_exist_locally"] is True + assert base["pull_allowed"] is False + docker_build = cast(dict[str, object], build["docker_build"]) + assert docker_build == { + "network": "none", + "pull": False, + "no_cache": True, + "provenance": False, + "platform": "linux/amd64", + "context_input": "exact git-archive tar bytes", + "required_build_args": [ + "NODEDC_SOURCE_REVISION", + "NODEDC_BUILD_CONTEXT_SHA256", + "SOURCE_DATE_EPOCH", + ], + } + assert build["required_preflight"] == [ + "Git worktree and index are clean", + "selected revision resolves to exactly one commit", + "selected revision equals HEAD", + "base image inspect ID equals the pinned SHA-256", + "context archive contains exactly the context manifest entries", + ] + + acceptance = cast(dict[str, object], document["build_acceptance"]) + labels = cast(dict[str, str], acceptance["required_labels"]) + assert labels["org.opencontainers.image.revision"] == "" + assert labels["com.nodedc.base-image.sha256"] == BASE_SHA256 + assert labels["com.nodedc.models"] == "external" + smoke = cast(dict[str, object], acceptance["smoke"]) + assert smoke["network"] == "none" + assert smoke["read_only_rootfs"] is True + assert smoke["platform"] == "linux/amd64" + assert smoke["expected_result"] == "exit-0" + + runtime = cast(dict[str, object], document["runtime"]) + environment = cast(dict[str, str], runtime["fixed_environment"]) + assert environment == { + "MISSIONCORE_OBSERVATORY_WORKER_BASE_URL": "http://127.0.0.1:18080", + "MISSIONCORE_OBSERVATORY_WORKER_TOKEN_FILE": "/run/secrets/observatory-worker.token", + "MISSIONCORE_OBSERVATORY_WORKER_WORK_ROOT": "/work", + "MISSIONCORE_OBSERVATORY_WORKER_DEFINITIONS_FILE": ( + "/opt/nodedc/mission-core/config/observatory-portable-run-definitions.json" + ), + "MISSIONCORE_OBSERVATORY_WORKER_RUNTIME_REGISTRY_FILE": ( + "/opt/nodedc/mission-core/config/observatory-worker-runtime-candidates.json" + ), + "MISSIONCORE_OBSERVATORY_M49_INSTALLATION_RECEIPT_FILE": ( + "/release/worker-installation-receipt.json" + ), + } + user = cast(dict[str, object], runtime["user"]) + assert (user["uid"], user["gid"]) == (0, 0) + assert cast(str, user["reason"]).strip() + hardening = cast(dict[str, object], runtime["hardening"]) + assert hardening["read_only_rootfs"] is True + assert hardening["cap_drop"] == ["ALL"] + assert hardening["no_new_privileges"] is True + assert hardening["published_ports"] == [] + assert hardening["device_requests"] == [] + assert "docker_socket_mount" not in hardening + assert '"docker_socket_mount": false' not in INSTALL_PLAN.read_text(encoding="utf-8") + + docker_control = cast(dict[str, object], runtime["docker_engine_control"]) + assert docker_control["transport"] == ( + "Docker Engine HTTP API over Unix domain socket; Docker CLI is not used" + ) + assert docker_control["authority"] == "host-equivalent Docker Engine control" + assert "compromised Worker agent" in cast(str, docker_control["risk"]) + socket_mount = cast(dict[str, object], docker_control["socket_mount"]) + assert socket_mount == { + "type": "bind", + "host_path": str(local_runners._DOCKER_SOCKET), + "container_path": str(local_runners._DOCKER_SOCKET), + "mode": "read-write", + "exception_to_read_only_mount_policy": True, + } + assert docker_control["allowed_operations"] == [ + "inspect exact installed image identity", + "create one sealed LAB V1 sibling", + "start exact created container ID", + "wait exact created container ID", + "read bounded tail logs for failed exact created container ID", + "force-delete exact created container ID", + ] + fences = cast(dict[str, object], docker_control["sibling_runtime_fences"]) + assert fences["exact_image_sha256s"] == { + "eomt": local_runners._EOMT_IMAGE_SHA256, + "ddrnet": local_runners._DDRNET_IMAGE_SHA256, + } + assert local_runners._EOMT_IMAGE_SHA256 == BASE_SHA256 + assert "queued job" in cast(str, fences["commands_and_entrypoints"]) + assert "exact Worker-host asset roots" in cast(str, fences["bind_sources"]) + assert fences["network"] == "none" + assert fences["read_only_rootfs"] is True + assert fences["cap_drop"] == ["ALL"] + assert fences["no_new_privileges"] is True + assert fences["privileged"] is False + assert fences["pids_limit"] == 512 + assert fences["memory_swap_equals_memory"] is True + assert fences["gpu_request"] == { + "driver": "nvidia", + "count": 1, + "capabilities": [["gpu"]], + } + assert fences["required_unique_labels"] == [ + "com.nodedc.authority=observation-only", + "com.nodedc.component=", + "com.nodedc.definition-sha256=", + "com.nodedc.managed-by=mission-core-worker", + "com.nodedc.request-sha256=", + "com.nodedc.product=mission-core", + "com.nodedc.release-candidate-sha256=", + "com.nodedc.stack=observatory", + ] + assert cast(str, fences["cleanup"]).endswith( + "exact 64-hex container ID returned by the successful create response" + ) + external = cast(dict[str, object], runtime["external_assets"]) + assert external["baked_into_agent_image"] is False + assert external["queued_jobs_may_select_paths_or_commands"] is False + lab_v1_input = cast(dict[str, object], runtime["lab_v1_runner_installation_input"]) + assert lab_v1_input == { + "state": "loader-contract-not-yet-defined", + "environment_variable": None, + "container_path": None, + "reason": ( + "the current local runner module defines an in-memory sealed installation " + "receipt but no environment loader or installed receipt path" + ), + } + + receipt = cast(dict[str, object], document["receipt_skeleton"]) + assert receipt["receipt_state"] == "not-built" + assert receipt["source_revision"] is None + assert receipt["source_date_epoch"] is None + assert receipt["build_context_sha256"] is None + assert receipt["base_image_sha256"] == BASE_SHA256 + image = cast(dict[str, object], receipt["image"]) + assert image == {"tag": None, "id": None, "size_bytes": None} + assert receipt["models_baked_into_image"] is False + assert receipt["authority"] == AUTHORITY