Issue #974 Shuffle Worker Docker And Image Realization Preflight¶
This note fixes the repository-wide boundaries for issue #974. It is architecture guidance, not an implementation plan. It supersedes the issue
949 preflight only where that note requires a host bind_source, authored¶
realized_children, authored child labels/counts, one template per image,
digest-only templates, blanket rejection of a Docker socket on every spawned
child, or prohibition of verified local alias creation while offline.
ADR-055's three-party
authority rule, ADR-049's participant boundary, and issue #964's
native-resource ownership rules remain authoritative.
The motivating TechVault pack describes portable in-world intent: Orborus's endpoint, environment, lifecycle, and authored spawn templates. It does not select a host path, grant daemon authority, predict runtime children, or define APTL correlation metadata.
Keep The Contracts Separate¶
| Concept | Owner and meaning |
|---|---|
| Portable orchestration demand | RAES RuntimeConfiguration, RuntimeControlInterface, RuntimeOrchestrationAuthority, lifecycle policy, environment, and spawn_templates. These values describe the in-world endpoint and authored workload references; they are neither host authority nor an authorization inventory. |
| Backend capability | DeploymentBackend and the local Compose backend decide whether one selected daemon, endpoint lowering, image preparation, and runtime aliasing can be supported. Capability is not permission. |
| Operator grant | The bounded local policy proposed by ADR-055 is owned by #1129 and is not available in dev. Neither the pack nor this issue creates, narrows, or satisfies an operator grant. |
| Image identity | A digest-qualified reference is immutable identity. A tag in a tag-and-digest reference is an authored local runtime alias which must be made to resolve to that digest. A tag-only or digest-only reference is used as authored; a matching name string is never proof of local identity. |
| Runtime observation | Actual worker/app containers, counts, native IDs, status, image IDs, mounts, and correlations are backend observations made after execution. realized_children is not desired state, an image-preparation input, or an admission prerequisite. Runtime enumeration and ownership on a shared daemon remain #1128's boundary. |
| Resource ownership | Existing workspace/project/attempt identity and durable native-ID receipts establish which resources APTL may observe or clean up. A pack label, tag, name, timestamp, or count does not. |
Do not collapse these concepts into one enlarged authority DTO. In particular, image preparation requirements must not carry future child counts or authored APTL labels, and observed child records must not become the source of image demand.
Architecture Decisions And Guardrails¶
Authority and endpoint selection¶
- Preserve the existing RAES parse/semantic-validation boundary. Resolve an authority's endpoint reference on the same node and validate the portable endpoint target, access, engine, privilege class, lifecycle, environment, and exact templates without copying the RAES models into APTL.
- Treat
local_control_interfaces.bind_sourceas optional portable input, not host policy. The effective source comes only from the backend's selected local endpoint. A pack-supplied source must never redirect Docker commands, grant a mount, or constrain operator policy. Compatibility parsing of a legacy value does not make it authoritative. - Reuse
DockerEndpointBindingMixinfor explicitdocker_socket_path/ supported localDOCKER_HOSTselection,lstatno-symlink socket checks, read/write accessibility, device/inode pinning, daemon-ID attestation, and revalidation. Endpoint selection and operator authorization are separate: an ambientDOCKER_HOSTcan select a candidate but cannot grant it. - Realize the selected source at Orborus's authored in-world target and prove through effective Compose plus runtime inspect that APTL's lowered service reaches the bound daemon. A spawned worker is not a separately admitted service, a pack-authorized child, or an APTL-owned resource in this issue. Do not synthesize a second pack authority or reinterpret a spawn template as a grant.
- A raw Docker socket is host-root-equivalent authority over the selected
daemon. The worker is therefore trusted at that boundary; read-only mount
syntax, labels, networks, or container profiles do not attenuate it. The
product adapter must fail admission when it cannot propagate the selected
endpoint source faithfully (including a non-default/rootless source) rather
than silently fall back to
/var/run/docker.sockon another daemon. - Effective Compose validation and post-start inspection remain default-deny
for every other APTL-lowered service's socket bind, socket-containing
ancestor bind, privileged container, or
DOCKER_HOST/DOCKER_CONTEXToverride. Do not extend that APTL service-containment gate into an invented policy over daemon-created children. - Do not implement the operator-grant policy model in this issue. In particular,
do not add a pack field, environment flag, CLI shortcut, hard-coded TechVault
exception, or permissive default as a substitute. #1129 owns any future
signed strict
DockerAuthorityPolicyextension and its appliance projection; it must consume this typed edge rather than cause a parallel appliance grant.
Exact images and runtime aliases¶
- Derive preparation demand from every
spawn_template, even whenrealized_childrenis empty. Accept every RAES-valid authored image form: tag-and-digest, tag-only, and digest-only. Retain node/authority/template provenance for diagnostics and deduplicate work by authored reference. Do not require one template per image: two semantic templates may legitimately share immutable bytes. - Keep
_compose_spawn_image_realization.pyas the realization owner and_docker_image_identity.pyas the sole Docker-inspect parser. Online mode may pull the authored reference. Offline-staged mode may use only local daemon operations: no pull, manifest inspect, registry resolution, build, or fallback client is permitted. - On the already bound daemon, use the canonical Docker inspect parser and
platform checks. For a digest-qualified reference, prove its
RepoDigests, config image ID, and platform. Where that authored reference also contains a tag, tag the verified local image with that authored tag, inspect both names, and require the same image ID. For tag-only or digest-only input, inspect and use exactly that input; do not manufacture a different alias or silently strengthen the pack contract. - A stale alias must never pass: replace it using a local daemon tag operation and reverify it, or fail before workflow acceptance. Never pull the tag, accept a matching tag string as proof, or rewrite the exact identity to the tag.
- Image and alias preparation completes before the external Shuffle workflow can be accepted. Failure leaves the run unaccepted and emits one bounded diagnostic. Preparation must be idempotent on retry and must revalidate the socket/daemon before mutation so an alias cannot be created on a different daemon.
appliance/input_images.pyclosure alignment is explicitly out of scope. Do not hide a change to its seed-script inventory or appliance archive contract inside this issue. Offline realization may only use already-local daemon operations and must fail cleanly when the authored image cannot be realized.
Correlation, observation, and lifecycle¶
- Do not require the pack to author APTL label syntax, a positive count, or a
future
realized_childrenentry. Delete the corresponding rejection paths; do not replace them with a different correlation convention. - Remove the spawned-child socket rejection. A raw-daemon holder can create a socket-holding child regardless of APTL staging, so this is neither an enforceable authorization boundary nor an actionable ownership fact.
- Existing
WorkspaceOwnership/ResourceReceiptand lifecycle machinery remain the only route to observation or cleanup once #1128 defines shared daemon child ownership. This issue neither enumerates, claims, correlates, supervises, nor cleans up spawned children. - Keep scenario success semantics in the installed TechVault verifier. The core deployment layer realizes the authored image/alias and preserves containment for services it lowers; the plugin proves terminal workflow success, non-empty action results, and exactly one run-correlated TheHive case.
Cross-Cutting Layers The Design Must Pass¶
| Layer | Canonical incumbent | Required result |
|---|---|---|
| Portable shape and semantics | Public RAES runtime models and processor validation in raes_runtime_orchestration.py |
Same-node endpoint references, all RAES-valid authored image-reference forms, lifecycle/environment semantics, and closed model validation pass without an authored host source or child observation. No APTL mirror schema. |
| Environment and secret binding | _environment_names() / _environment_defaults() in raes_base_substrate.py, the existing credential boundary, Docker credential storage, and shared redaction |
Alias selection comes from the authored image reference, never a secret or mutable Shuffle setting. Operator secrets remain name-only until credential binding and never enter image requirements, argv, diagnostics, or inspect logs. Registry credentials use the existing Docker client boundary only in online mode. |
| Deferred local policy shape | Strict AptlConfig models and the signed ApplianceBoundaryPolicy / DockerAuthorityPolicy family |
#974 must not add a policy shape or treat pack data as policy. #1129 will make holder, delegated role, daemon scope, and image/tag mutation independently grantable and fail closed when absent. |
| Control-plane authentication | verify_token, the single-origin BFF Host/CSRF/session gates, and the authenticated lab lifecycle routes |
This issue adds no endpoint or client-supplied authority field. A web-triggered lifecycle operation retains existing authentication; #1129, rather than request or pack content, will supply any server-side operator authorization. |
| Backend capability and endpoint | DeploymentBackend, DockerComposeBackend, DockerEndpointBindingMixin |
One supported local Unix endpoint is selected and pinned by socket plus daemon identity. Non-local schemes, symlinks, inaccessible sockets, swaps, and unsupported product propagation fail closed. |
| Effective configuration | render_realization_compose(), Compose-config validation, and _compose_runtime_orchestration.py |
The backend-selected source is mounted only on the APTL-lowered holder; the in-world target remains authored. No static-Compose fallback or pack-controlled host path. |
| OS/process boundary | DockerComposeBackend._run() / _subprocess_kwargs() and bounded deployment timeouts |
Docker calls use argv lists and the pinned DOCKER_HOST, clear conflicting context, and never place secrets or untrusted shell fragments in argv. Image refs/tags are validated data, not command text. |
| Image identity | _compose_spawn_image_realization.py, _compose_image_realization.py, _docker_image_identity.py |
Extend the canonical local-inspect parser rather than adding tag-string checks. Verify digest identity when authored; create and re-inspect the authored tag alias only for tag-and-digest input. Offline mode has no registry-capable branch. |
| Runtime authority observation | _compose_runtime_observation.py, runtime_authority.py, and RAES runtime observation |
APTL-lowered holders retain their existing containment checks. Spawned-child label/count/socket checks are removed, without treating children as observed or owned; #1128 owns any later child-runtime boundary. |
| Ownership and persistence | _compose_resource_ownership.py, _compose_resource_resolution.py, lifecycle storage, ACES run/attempt identity |
Preserve these controls for APTL-owned resources. Do not use labels, tags, counts, or names to claim a spawned child in #974. |
| Appliance materialization | appliance/input_images.py, appliance/inputs.py, release manifest validation, and aptl-appliance-first-boot |
Preserve the existing signed staged closure. Its alignment to spawn templates is expressly out of scope; #974's offline path still cannot contact a registry. |
| Errors and logs | LabResult, RAES Diagnostic, BackendTimeoutError, get_logger(), and redaction utilities |
Expose stable stage/reason/provenance identifiers, not raw Docker stderr, inspect payloads, environment values, host paths, registry credentials, or secrets. Timeout and offline-unavailable are distinct fail-closed outcomes. |
| Workflow verification | techvault_live_gate.py, scenario verifier plugin seam, and existing bounded polling |
Workflow acceptance happens only after preparation; semantic success remains plugin-owned and run-correlated rather than hard-coded in deployment core. |
Canonical Reuse And Extensibility Seam¶
The generic image layer's durable seam is an immutable tuple of (authored image
reference, selected daemon identity, offline mode, provenance), plus the
canonical locally inspected identity. The one derived alias is the tag already
present in a tag-and-digest authored reference; it is not a Shuffle default or
a new portable field. A future image-reference form extends the one canonical
Docker reference/inspect path, not a product-specific tag parser.
The authority seam for this issue remains portable demand plus backend capability for services APTL actually lowers. #1129 may add its independent operator-grant intersection there. Do not add child roles, delegation edges, or child-image authorization to this seam: raw daemon authority cannot be bounded by them.
Reuse rather than duplicate:
- RAES runtime models and reference validation, not local portable DTO copies;
DockerEndpointBindingMixin, not another socket/daemon resolver;- Docker image identity/parsing and platform checks, extended once for the authored tag/digest distinction rather than reimplemented as tag-string logic;
WorkspaceOwnershipandResourceReceipt, not labels as ownership or a new child registry;- strict config/appliance boundary policy, not environment booleans;
LabResult/RAES diagnostics, bounded timeouts, logging/redaction, and the existing live-gate plugin seam, not a parallel exception or workflow stack;- existing offline staging guards, not seed-script inspection or shell repair.
Required Verification Guardrails¶
Tests must cover both online and offline image paths, exact identity versus alias state, and removal of the invalid SDL rejections. #1129 owns the independent operator-grant decision and #1128 owns child ownership/observation. At minimum, prove:
- tag-only, digest-only, and tag-and-digest template values all realize as
authored;
realized_childrenwithout templates does not create an image closure or fail admission; - a missing exact image or stale tag alias fails, or the alias is replaced by a local tag operation and reinspection proves the expected image ID;
- offline staging invokes no registry-capable command or client and fails before workflow acceptance when local preparation cannot complete;
evidence_refandcountare passed through RAES rather than parsed as APTL Docker-label/count policy, and a spawned child with a socket no longer triggers this issue's rejection;- the clean TechVault live gate reaches terminal success with non-empty action results and exactly one case for the current run.
Prefer command-recording unit tests for the zero-registry invariant plus a real
isolated-daemon integration test for image IDs and local tags. A mocked
docker inspect transcript alone does not prove Docker's alias semantics.
Gotchas And Anti-Patterns¶
- Do not use
realized_childrenas desired state, preparation input, expected count, permission, or ownership evidence. - Do not put host paths, operator grants, APTL labels, workspace IDs, or attempt IDs in the portable pack.
- Do not treat
DOCKER_HOST,soc, a private network, a read-only mount, or a digest-qualified image as an authority grant. - Do not model a worker or app as an APTL-admitted/authorized spawned child; the daemon holder can create either regardless of the staged image list.
- Do not accept a runtime tag's existence, name, or registry digest lookup as proof of its local target. Inspect the local image ID after aliasing.
- Do not let offline mode fall through to
docker pull,docker manifest inspect, registry HTTP, a library resolver, or tag auto-download. - Do not parse
seed-shuffle.sh, duplicate the HTTP 1.4.0 fact in backend defaults, or edit staticdocker-compose.ymlas a fallback fix. - Do not correlate or clean up by image, label, count, name, or timestamp alone; #974 does not enumerate children at all.
- Do not expose Docker stderr, full inspect JSON, environment arrays, selected host paths, credentials, or raw workflow results in diagnostics/logs.
- Do not hard-code TechVault workflow/TheHive semantics in deployment core or turn the verifier into an image/authority controller.
Non-Goals And Boundaries¶
- This issue does not make raw Docker authority safe, attenuated, or suitable for participant workloads; it records one trusted Shuffle management topology for later intersection with operator policy.
- It does not define the general local policy vocabulary owned by #1129, accept ADR-055, or authorize a permissive interim substitute.
- It does not implement shared-daemon child enumeration, ownership, correlation, observation, or cleanup (#1128).
- It does not redesign RAES runtime schemas, require packs to author backend observations, or add APTL-specific portable fields.
- It does not generalize arbitrary remote/TCP Docker endpoints, Docker contexts, Kubernetes, Podman, registries, or cross-daemon image transfer.
- It does not align
appliance/input_images.pywith spawn templates. - It does not change Shuffle workflows, seed application content, TheHive semantics, or scenario success criteria beyond making their selected runtime executable and verifiable.
- It does not replace backend ownership/cleanup, appliance signing, lifecycle, live-gate, logging, redaction, or error-envelope conventions.