Files
alknet/docs/architecture/crates/docker/README.md
T
glm-5.2 ab963d0e46 docs(arch): resolve OQ-050 — include docker/system/events in v1
Resolve the deferred docker system events subscription question.
docker/system/events is now a v1 Subscription operation using the
same StreamingHandler pattern already wired for logs, exec, and
image/pull. The internal ownership-store subscription for stale-entry
cleanup on destroy events is a follow-up refinement.

Scrub hedging language from ADR-060 and docker specs:
- Remove 'marginal gain' / 'future feature is additive' framing
- Replace 'no reaper' / 'not promptly cleaned up' with clean
  statement that events subscription provides the prompt-cleanup path
- Remove stale-entry policy from ADR-060's two-way door classification
2026-07-10 07:31:55 +00:00

12 KiB

status, last_updated
status last_updated
draft 2026-07-08

alknet-docker

The docker operations crate for the ALPN-as-service architecture: a thin, single-host bollard wrapper that exposes docker container and image operations as call-protocol operations on the shared alknet/call ALPN, plus (behind a tty feature) a DockerTtyBackend implementing alknet-tty's TtyBackend trait for interactive terminal sessions into containers.

What

alknet-docker does two things:

  1. Call operations. A set of OperationSpec-registered operations (docker/container/*, docker/image/*, docker/system/events) on the shared alknet/call ALPN, mapping bollard's docker API to the call protocol's Query / Mutation / Subscription dispatch paths. Lifecycle (create/start/stop/remove/list/inspect) is Query/ Mutation; logs, non-interactive exec, image pull, and system events are Subscription (streaming via StreamingHandler, ADR-049). The operations declare AccessControl against the ADR-050 container-as-resource model. Decided in ADR-058.

  2. DockerTtyBackend (behind the tty feature). An impl TtyBackend (ADR-053) wrapping bollard::attach_container() and bollard::exec::start_exec with tty: true, for interactive terminal sessions into containers over alknet/tty. This is the TtyBackend row the alknet-tty spec left open ("future, out of scope here"). Decided in ADR-061.

The two use cases the crate serves (per the user's brief and the system docs):

  • Disposable dev containers (the common case by volume) — a coordinator spawns a container for an implementation agent or an isolated env. alknet-docker's create records ownership (OwnershipStore::record); the coordinator owns the container for its lifetime; remove revokes. Interactive sessions into the container go through DockerTtyBackend on alknet/tty.
  • Long-running hosted services (the production server case) — rarely-changing services (reverse-proxy, postgres, redis, gitea on dev1, per /workspace/system/dev1/docker.md) created by an operator via docker compose, not via alknet. alknet-docker's operations (start/stop/inspect/logs) manage them; the operator role reaches them via the static-resource fallback (ADR-060 §3).

Documents

Document Status Description
overview.md draft Crate purpose, two-role design (call ops + DockerTtyBackend), dependencies, ALPN, label namespace, feature gates, assembly-layer wiring
docker-operations.md draft The operation surface: lifecycle (Query/Mutation), logs/exec/pull (Subscription via StreamingHandler), access control, label namespace, teardown coupling
docker-tty-backend.md draft DockerTtyBackend (impl TtyBackend): attach vs exec mode, TtyHandle field mapping, TtyControl → bollard resize/signal, exit_code Drop-kill (ADR-056)

Applicable ADRs

ADR Title Relevance
058 alknet-docker Registers on alknet/call Docker ops are call-protocol operations, not a separate ALPN; raw-carriage dissolved by alknet-tty extraction
059 bollard 0.21 Dependency and Feature Selection Version pin (0.21, verified current); features http+pipe+time, no ssl/ssh/websocket/buildkit
060 Container Resource Model and Label Namespace ADR-050 application: alknet.managed/alknet.owner labels; list owned_only flag; hosted-services static-resource fallback; handler-driven revoke + autonomous-death tolerance
061 DockerTtyBackend in alknet-docker Backend in alknet-docker behind tty feature; attach/exec mode split; POC drive_attach_raw as reference
003 Crate Decomposition alknet-docker depends on alknet-core + alknet-call (ops) and alknet-tty (tty feature); no handler-depends-on-handler violation
012 Call Protocol Stream Model The wire format docker ops use (EventEnvelope, no carriage field)
017 Call Protocol Client and Adapter Contract from_call re-export — the proxy pattern's mechanism for hub→worker docker management
023 Operation Error Schemas Docker ops declare domain error codes (CONTAINER_NOT_FOUND, IMAGE_NOT_FOUND, etc.)
024 Operation Registry Layering Docker ops register in the curated Layer 0 at startup
029 Peer-Graph Routing Model Head→worker docker management via PeerRef / invoke_peer
032 Forwarded-For Identity End-user identity as metadata when a coordinator proxies docker ops
049 Streaming Handler for Subscriptions StreamingHandler for logs/exec/pull; exit code on final call.responded
050 Dynamic Resource Ownership Containers as AccessControl resources; the model ADR-060 applies
052 alknet-tty Wire Format The alknet/tty wire format DockerTtyBackend sessions use
053 TtyBackend Trait and TtyHandle The trait DockerTtyBackend implements
055 Exit Code on a Control Chunk The exit-chunk ordering DockerTtyBackend's exit_code feeds into
056 Backend Cleanup on Session Cancel DockerTtyBackend's exit_code future Drop kills the container/exec
014 Secret Material Flow and Capability Injection Capabilities is for secret material only — the Docker handle is not secret (ADR-062)
062 Docker Client and OwnershipStore Injection via Closure Capture Closure capture at registration; Capabilities for secrets only; matches from_openapi pattern
063 Exit Code on a Terminal call.responded for Non-Interactive Exec { "exitCode": N, "terminal": true } on final call.responded; call.completed stays empty

Relevant Open Questions

OQ Title Status Relevance
OQ-048 Network and volume operation surface deferred(scope) Network/volume CRUD deferred; v1 is containers + images
OQ-049 Image build (buildkit) scope deferred(scope) buildkit feature deferred; v1 has image/pull + image/list + image/inspect
OQ-050 Docker system events subscription resolved docker/system/events included in v1 as a Subscription operation; internal ownership-store subscription for cleanup is a follow-up refinement
OQ-051 Container create options surface deferred(scope) Full CreateContainerOptions (mounts, port bindings, networks) surface deferred to v1 implementation

Key Design Principles

  1. Single-host, bollard-specific. alknet-docker talks to one local docker daemon over /var/run/docker.sock. The fleet case (multiple daemons on different machines) is a call-protocol concern — a CallClient per remote daemon, each running alknet-docker locally — not a bollard-feature concern. No ssl/ssh features, no remote daemon over TLS. See overview.md and ADR-058.

  2. Call operations on alknet/call, not a separate ALPN. Docker ops are ordinary call-protocol operations with OperationSpecs, AccessControl, and service discovery. They inherit from_call re-export and peer routing. The one operation that didn't fit (interactive attach) moved to alknet/tty via DockerTtyBackend. No carriage field on call.requested, no parallel dispatch surface. See ADR-058.

  3. Containers are runtime-spawned resources (ADR-050). create records ownership; remove revokes; exec/stop/inspect check ownership via OperationSpec.resource_id_path. The hosted-services case (operator-managed, pre-existing containers) works through the static-resource fallback. Two labels (alknet.managed, alknet.owner) mark alknet-spawned containers for the list filter and the cross-check. See docker-operations.md and ADR-060.

  4. Interactive terminal is a tty concern, not a docker concern. DockerTtyBackend implements alknet-tty's TtyBackend trait, behind a tty feature. The wire format, the session lifecycle, and the exit-chunk ordering live in alknet-tty; the backend produces bollard-backed handles. This is the same inversion as LocalTtyBackend (local process) and SshTtyBackend (SSH). See docker-tty-backend.md and ADR-061.

  5. bollard 0.21, verified current. The POC used a local 0.21 checkout; the crate depends on published 0.21 from crates.io (verified as latest). Features: http + pipe (default, local daemon) + time (log timestamps). No websocket (reliable attach only), no ssl/ssh (fleet is call-protocol), no buildkit (deferred). See ADR-059.

  6. Mechanical mapping, no feasibility risk. The POC validated the hard parts (raw carriage attach, logs subscription, exec with exit code). The remaining lifecycle operations (create/start/stop/remove/ list/inspect) are mechanical bollard wrapping — Query/Mutation with single call.responded responses, "the boring case" (POC §"What the POC Does NOT Validate" #4). See docker-operations.md.

References

  • docs/research/alknet-docker/poc-summary.md — the POC that validated the hard parts (interactive attach, logs subscription, exec with exit code) and surfaced the open unknowns this spec set resolves
  • /workspace/alknet-docker-poc/ — the POC source (src/ops.rs DockerOps, src/raw.rs chunk codec, src/frame.rs EventEnvelope mirror, tests/integration.rs 6 tests against a live daemon)
  • /workspace/bollard/ — bollard 0.21.0 source (the local checkout the POC used; identical API surface to the published 0.21)
  • /workspace/@alkdev/dispatch/ — the dispatch POC (bollard 0.18, dispatch.managed=true labels, SSH-tunnel fleet model — the prior art this crate generalizes and the friction it removes)
  • /workspace/system/dev1/docker.md — the production hosted-services use case (reverse-proxy, postgres, redis, gitea on dev1)
  • /workspace/@alkdev/reverse-proxy/deploy/docker-compose.yml — the reverse-proxy's docker setup (operator-created, not alknet-spawned)
  • docs/architecture/crates/tty/ — the alknet-tty spec (DockerTtyBackend is the row tty-backend.md left open)