refactor(worker): externalize runtime registries

This commit is contained in:
DCCONSTRUCTIONS
2026-08-31 19:47:07 +03:00
parent 104e54f4dd
commit 732cc139fd
4 changed files with 194 additions and 50 deletions
@@ -12,11 +12,12 @@ ENV PYTHONPATH=/opt/nodedc/mission-core/src \
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 \
RUN mkdir -p /run/nodedc/registries \
&& chmod 0555 /run/nodedc /run/nodedc/registries \
&& case "${NODEDC_SOURCE_REVISION}" in \
*[!0-9a-f]* ) exit 64 ;; \
* ) ;; \
esac \
@@ -45,6 +46,7 @@ LABEL org.opencontainers.image.title="NODE.DC Observatory Worker 006 agent" \
com.nodedc.worker-contour="worker-006" \
com.nodedc.authority="observation-only" \
com.nodedc.models="external" \
com.nodedc.runtime-registries="external-read-only" \
com.nodedc.base-image.sha256="58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794" \
com.nodedc.build-context.sha256="${NODEDC_BUILD_CONTEXT_SHA256}"
@@ -27,11 +27,6 @@
"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",
@@ -43,11 +38,24 @@
"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"
"embedded_configuration": [],
"external_runtime_files": [
{
"role": "portable-run-definition-registry",
"environment_variable": "MISSIONCORE_OBSERVATORY_WORKER_DEFINITIONS_FILE",
"container_path": "/run/nodedc/registries/observatory-portable-run-definitions.json",
"binding": "exact-read-only-regular-file",
"identity_owner": "post-image runtime installation",
"baked_into_image": false
},
{
"role": "portable-worker-runtime-registry",
"environment_variable": "MISSIONCORE_OBSERVATORY_WORKER_RUNTIME_REGISTRY_FILE",
"container_path": "/run/nodedc/registries/observatory-worker-runtime-candidates.json",
"binding": "exact-read-only-regular-file",
"identity_owner": "post-image runtime installation",
"baked_into_image": false
}
],
"runtime": {
"pythonpath": "/opt/nodedc/mission-core/src",
@@ -57,11 +65,15 @@
"k1link.observatory.m49_worker_container_main"
],
"composition_module": "k1link.observatory.m49_worker_service",
"configuration_baked_into_image": false,
"runtime_registries_baked_into_image": false,
"models_baked_into_image": false,
"executor_releases_baked_into_image": false
},
"forbidden_context": [
"model weights",
"run-definition registries",
"runtime-candidate registries",
"recorded K1 sessions",
"worker bearer credentials",
"executor release payloads",
@@ -55,6 +55,7 @@
"com.nodedc.worker-contour": "worker-006",
"com.nodedc.authority": "observation-only",
"com.nodedc.models": "external",
"com.nodedc.runtime-registries": "external-read-only",
"com.nodedc.base-image.sha256": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794",
"com.nodedc.build-context.sha256": "<build-context-sha256>"
},
@@ -76,9 +77,41 @@
"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"
"MISSIONCORE_OBSERVATORY_WORKER_DEFINITIONS_FILE": "/run/nodedc/registries/observatory-portable-run-definitions.json",
"MISSIONCORE_OBSERVATORY_WORKER_RUNTIME_REGISTRY_FILE": "/run/nodedc/registries/observatory-worker-runtime-candidates.json",
"MISSIONCORE_OBSERVATORY_M49_INSTALLATION_RECEIPT_FILE": "/release/worker-installation-receipt.json",
"MISSIONCORE_OBSERVATORY_LAB_V1_INSTALLATION_RECEIPT_FILE": "/release/lab-v1-worker-installation-receipt.json"
},
"runtime_registry_files": {
"binding": "individual read-only bind files",
"identity_timing": "filled and verified after coordinator image build",
"included_in_coordinator_image_or_image_receipt": false,
"required": [
{
"role": "portable-run-definition-registry",
"environment_variable": "MISSIONCORE_OBSERVATORY_WORKER_DEFINITIONS_FILE",
"host_path": null,
"container_path": "/run/nodedc/registries/observatory-portable-run-definitions.json",
"byte_length": null,
"sha256": null,
"mode": "read-only"
},
{
"role": "portable-worker-runtime-registry",
"environment_variable": "MISSIONCORE_OBSERVATORY_WORKER_RUNTIME_REGISTRY_FILE",
"host_path": null,
"container_path": "/run/nodedc/registries/observatory-worker-runtime-candidates.json",
"byte_length": null,
"sha256": null,
"mode": "read-only"
}
],
"required_preflight": [
"fill one exact absolute host path, byte length and SHA-256 for each registry after the coordinator image identity exists",
"reject missing, symbolic-link, non-regular or identity-mismatched registry files before container start",
"bind each registry as its own read-only file at the fixed container path",
"reject any registry path inside the coordinator image"
]
},
"user": {
"uid": 0,
@@ -121,9 +154,13 @@
"force-delete exact created container ID"
],
"sibling_runtime_fences": {
"exact_image_sha256s": {
"eomt": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794",
"ddrnet": "591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd"
"component_image_identity": {
"source": "/release/lab-v1-worker-installation-receipt.json",
"schema_version": "missioncore.observatory-portable-lab-v1-worker-installation-ready-receipt/v1",
"fields": "component_installations.<eomt-or-ddrnet>.image_sha256",
"image_kind": "derived adapter image",
"base_image_ids_are_component_ids": false,
"queued_job_override_allowed": false
},
"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",
@@ -179,6 +216,16 @@
"container_path": "/run/secrets",
"mode": "read-only",
"purpose": "private bearer token file"
},
{
"container_path": "/run/nodedc/registries/observatory-portable-run-definitions.json",
"mode": "read-only",
"purpose": "exact external portable RunDefinition registry file"
},
{
"container_path": "/run/nodedc/registries/observatory-worker-runtime-candidates.json",
"mode": "read-only",
"purpose": "exact external portable runtime candidate registry file"
}
],
"external_assets": {
@@ -188,10 +235,12 @@
"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"
"state": "external-installed-receipt-required",
"environment_variable": "MISSIONCORE_OBSERVATORY_LAB_V1_INSTALLATION_RECEIPT_FILE",
"container_path": "/release/lab-v1-worker-installation-receipt.json",
"mode": "read-only",
"owns_component_image_identities": true,
"queued_jobs_may_override_component_images": false
},
"protected_contours": [
"K1 connection and control",
@@ -217,6 +266,7 @@
"result": "not-run"
},
"models_baked_into_image": false,
"runtime_registries_baked_into_image": false,
"authority": {
"commands_enabled": false,
"actuation_allowed": false,
@@ -5,8 +5,6 @@ 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"
@@ -23,6 +21,14 @@ AUTHORITY = {
"navigation_or_safety_accepted": False,
"production_accepted": False,
}
DOCKER_SOCKET = "/var/run/docker.sock"
DEFINITIONS_CONTAINER_PATH = (
"/run/nodedc/registries/observatory-portable-run-definitions.json"
)
RUNTIME_REGISTRY_CONTAINER_PATH = (
"/run/nodedc/registries/observatory-worker-runtime-candidates.json"
)
LAB_V1_RECEIPT_CONTAINER_PATH = "/release/lab-v1-worker-installation-receipt.json"
def _document(path: Path) -> dict[str, object]:
@@ -45,8 +51,11 @@ def test_agent_dockerfile_is_offline_commit_bound_and_model_free() -> None:
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 config" not in payload
assert "COPY . ." not in payload
assert "mkdir -p /run/nodedc/registries" in payload
assert "chmod 0555 /run/nodedc /run/nodedc/registries" in payload
assert 'com.nodedc.runtime-registries="external-read-only"' 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()
@@ -85,7 +94,6 @@ def test_build_context_is_only_repo_source_configuration_and_artifact_contract()
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",
]
@@ -97,19 +105,41 @@ def test_build_context_is_only_repo_source_configuration_and_artifact_contract()
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 document["embedded_configuration"] == []
assert "required_configuration" not in document
external_runtime_files = cast(
list[dict[str, object]], document["external_runtime_files"]
)
assert external_runtime_files == [
{
"role": "portable-run-definition-registry",
"environment_variable": "MISSIONCORE_OBSERVATORY_WORKER_DEFINITIONS_FILE",
"container_path": DEFINITIONS_CONTAINER_PATH,
"binding": "exact-read-only-regular-file",
"identity_owner": "post-image runtime installation",
"baked_into_image": False,
},
{
"role": "portable-worker-runtime-registry",
"environment_variable": (
"MISSIONCORE_OBSERVATORY_WORKER_RUNTIME_REGISTRY_FILE"
),
"container_path": RUNTIME_REGISTRY_CONTAINER_PATH,
"binding": "exact-read-only-regular-file",
"identity_owner": "post-image runtime installation",
"baked_into_image": False,
},
]
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["configuration_baked_into_image"] is False
assert runtime["runtime_registries_baked_into_image"] is False
assert runtime["models_baked_into_image"] is False
assert runtime["executor_releases_baked_into_image"] is False
forbidden = cast(list[str], document["forbidden_context"])
assert "run-definition registries" in forbidden
assert "runtime-candidate registries" in forbidden
def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receipt() -> None:
@@ -162,6 +192,7 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
assert labels["org.opencontainers.image.revision"] == "<source-revision>"
assert labels["com.nodedc.base-image.sha256"] == BASE_SHA256
assert labels["com.nodedc.models"] == "external"
assert labels["com.nodedc.runtime-registries"] == "external-read-only"
smoke = cast(dict[str, object], acceptance["smoke"])
assert smoke["network"] == "none"
assert smoke["read_only_rootfs"] is True
@@ -174,16 +205,40 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
"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_DEFINITIONS_FILE": DEFINITIONS_CONTAINER_PATH,
"MISSIONCORE_OBSERVATORY_WORKER_RUNTIME_REGISTRY_FILE": (
"/opt/nodedc/mission-core/config/observatory-worker-runtime-candidates.json"
RUNTIME_REGISTRY_CONTAINER_PATH
),
"MISSIONCORE_OBSERVATORY_M49_INSTALLATION_RECEIPT_FILE": (
"/release/worker-installation-receipt.json"
),
"MISSIONCORE_OBSERVATORY_LAB_V1_INSTALLATION_RECEIPT_FILE": (
LAB_V1_RECEIPT_CONTAINER_PATH
),
}
registry_files = cast(dict[str, object], runtime["runtime_registry_files"])
assert registry_files["binding"] == "individual read-only bind files"
assert registry_files["identity_timing"] == (
"filled and verified after coordinator image build"
)
assert registry_files["included_in_coordinator_image_or_image_receipt"] is False
registry_rows = cast(list[dict[str, object]], registry_files["required"])
assert [row["container_path"] for row in registry_rows] == [
DEFINITIONS_CONTAINER_PATH,
RUNTIME_REGISTRY_CONTAINER_PATH,
]
assert all(
row["host_path"] is None
and row["byte_length"] is None
and row["sha256"] is None
and row["mode"] == "read-only"
for row in registry_rows
)
registry_preflight = cast(list[str], registry_files["required_preflight"])
assert any("after the coordinator image identity exists" in row for row in registry_preflight)
assert any("symbolic-link" in row for row in registry_preflight)
assert any("read-only file" in row for row in registry_preflight)
assert any("inside the coordinator image" in row for row in registry_preflight)
user = cast(dict[str, object], runtime["user"])
assert (user["uid"], user["gid"]) == (0, 0)
assert cast(str, user["reason"]).strip()
@@ -205,8 +260,8 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
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),
"host_path": DOCKER_SOCKET,
"container_path": DOCKER_SOCKET,
"mode": "read-write",
"exception_to_read_only_mount_policy": True,
}
@@ -219,11 +274,18 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
"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 "exact_image_sha256s" not in fences
assert fences["component_image_identity"] == {
"source": LAB_V1_RECEIPT_CONTAINER_PATH,
"schema_version": (
"missioncore.observatory-portable-lab-v1-worker-installation-ready-"
"receipt/v1"
),
"fields": "component_installations.<eomt-or-ddrnet>.image_sha256",
"image_kind": "derived adapter image",
"base_image_ids_are_component_ids": False,
"queued_job_override_allowed": False,
}
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"
@@ -254,15 +316,32 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
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
mounts = cast(list[dict[str, object]], runtime["mounts"])
registry_mounts = [
row
for row in mounts
if cast(str, row["container_path"]).startswith("/run/nodedc/registries/")
]
assert registry_mounts == [
{
"container_path": DEFINITIONS_CONTAINER_PATH,
"mode": "read-only",
"purpose": "exact external portable RunDefinition registry file",
},
{
"container_path": RUNTIME_REGISTRY_CONTAINER_PATH,
"mode": "read-only",
"purpose": "exact external portable runtime candidate registry file",
},
]
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"
),
"state": "external-installed-receipt-required",
"environment_variable": "MISSIONCORE_OBSERVATORY_LAB_V1_INSTALLATION_RECEIPT_FILE",
"container_path": LAB_V1_RECEIPT_CONTAINER_PATH,
"mode": "read-only",
"owns_component_image_identities": True,
"queued_jobs_may_override_component_images": False,
}
receipt = cast(dict[str, object], document["receipt_skeleton"])
@@ -274,4 +353,5 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
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["runtime_registries_baked_into_image"] is False
assert receipt["authority"] == AUTHORITY