runtime-controller

module
v0.0.0-...-147a9b8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2026 License: MIT

README

Runtime Controller

Runtime Controller owns the platform lifecycle of one logical Runtime Environment per Agent. Agent Controller issues explicit Initialize, Update, Disable, Enable, and Delete commands; Runtime Controller realizes them as private Docker compute and workspace resources and reports a platform-neutral result. It is written in Go.

The cross-service identity is agent_id plus an opaque runtime_revision. Physical generations, deployment digests, container IDs, and volume names stay private to this service. Docker is the only implemented platform adapter; a Kubernetes adapter is planned and would be added in-process, not as a separate service.

Workload authentication and configured Registry transport use the shared Go module, with the caller-context forwarding policy. RC retains its Controller-only route grants, Docker boundary and separate Runtime instance credential lifecycle.

Startup rejects published PostgreSQL passwords under the development secret policy. The exact ANTNEST_ALLOW_PUBLIC_DEV_SECRETS=true opt-in emits a variable-only WARN; it never restores the retired Registry token.

Responsibilities

  • Issue and atomically seal distinct RC/ACP Runtime tokens with each private compute generation. Deliver only caller hashes through a verified root-only, read-only receiver volume; exact recovery retains credentials. Privately resolve ACP's current connection for Controller; ordinary projections remain secret-free. See the instance connection contract.

  • Idempotently Initialize, Update, Disable, Enable, Inspect, and Delete one Agent Runtime Environment.

  • Allocate internal immutable compute generations and expose only an opaque Runtime revision to callers.

  • Map one language-neutral Runtime configuration to deterministic Docker compute and workspace resources, and bind each generation permanently to one deployment digest.

  • Resolve an installed image reference to an immutable image ID through a read-only platform query; never build or implicitly pull images.

  • Complete creation after confirmed platform create/start, without waiting for health (see creation and observation).

  • Report the current Docker IPv4 from the configured management-network attachment as runtime_endpoint in Inspect/List (contract revision 19). Controller reads this address before binding Egress traffic; it is independent of MCP URLs and execution readiness. Controller forwards the fresh address for Egress attachment opens and restart rebinding; see peer binding. Missing/invalid management IPv4 is an individual unknown-health inspection with runtime_peer_unavailable; it does not fail the whole inventory.

  • Consume platform health and List/Watch events, verify Runtime /status on Healthy observations and explicit reads, and normalize the facts into an ordered, time-retained observation journal.

  • Create and retain the Agent workspace as part of lifecycle commands; workspace operations are never exposed as a cross-service API.

  • Prepare per-Agent system Skill volumes from exact Skill Registry versions and mount them read-only into Runtime containers.

  • Freeze the Runtime Skill maintenance verifier keys into each accepted lifecycle operation. Key IDs follow the shared RuntimeSpec grammar and fixtures; invalid operator configuration fails at startup before creating Runtimes.

  • Serialize all mutations for one Agent across Controller replicas, and elect one platform-Watch consumer.

  • Emit structured logs, traces, and low-cardinality metrics.

Non-responsibilities

  • It does not decide when an Agent is initialized, updated, disabled, enabled, or deleted.
  • It does not own Agent desired state, active binding, or Run admission.
  • It does not dispatch Runs or proxy MCP Tool calls.
  • It does not allocate Tunnel IPs, persist Egress policy, or call Runtime Egress. Agent Controller supplies the Egress attachment in the Runtime configuration.
  • It does not run a Runtime reverse-connection server.
  • It does not authenticate end users or expose a public API.

Runtime instance bootstrap

ANTNEST_RUNTIME_INSTANCE_KEY_FILE is required: a private regular mode-0600 file containing exactly 32 raw CSPRNG bytes, mounted read-only by deployment. Retain it with RC's journal across restarts. It is separate from Controller's credential key and the workload-token directory; losing or changing it makes existing instance authority unavailable. There is no automatic reissue during recovery. Development has no legacy data to migrate; rebuild a fresh test stack.

The first instance receiver supports token mode with the explicit ANTNEST_SERVICE_AUTH_ALLOW_INSECURE_TRANSPORT=true opt-in on its isolated internal network. Native Runtime TLS/mTLS is unsupported and unsupported profiles fail RC startup; no opt-in is synthesized. The control-service TLS library still validates TLS when configured. Production transport expansion is separate work. The token-profile Runtime, Controller relay and ACP consumers are implemented.

  • It does not read another service's database.
  • It does not hold the Skill maintenance signing key; that belongs to Agent ACP Service.
Authenticated tunnel generation (#111)

RC also seals independent WireGuard endpoint keys and a 32-byte PSK with every Initialize/Update/Enable operation. Exact recovery keeps the accepted key ID. The same root-only private volume now contains tunnel.json; both file digests, mode 0600 and actual read-only mount ownership are checked before start. RuntimeSpec.authentication.tunnel carries only ID, fixed path and digest. Inspect/List reports the live generation's tunnel_key_id; compute completion receipts do not claim a currently verified peer or execution identity.

ANTNEST_RUNTIME_EGRESS_URL and the workload sender file for runtime-egress are required. RC privately registers Egress's endpoint material before any platform effect. Dependency outages return retryable tunnel_registration_unavailable while the admitted operation stays running; retry the same request ID. Deterministic rejection preserves an existing source Runtime. Keys never enter HTTP content capture. Runtime, Egress and Controller adoption plus deployment wiring remain subsequent #111 batches; this producer alone is not a tunnel. See the shared design.

Interfaces

Direction Interface Purpose
Inbound Internal JSON-over-HTTP RPC (control API) Image resolution, lifecycle commands, Inspect/List, operation queries, observation List/Watch, Skill set preparation
Inbound Local-loopback GET /status Liveness and readiness (database, journal/notifications, adapter initialization and cached monitor state)
Outbound Docker Engine API v1.47 over a Unix socket Containers, volumes, networks, events, image inspection
Outbound Runtime GET /status Bounded verification of Runtime identity and execution_id
Outbound Authenticated Skill Registry artifact API Download already-frozen exact Skill versions for preparation
Persistence Private PostgreSQL schema runtime_controller Environment heads, operations, generation claims, observation journal, Skill sets and references

The Runtime status reader recognizes the compiled test_features array while retaining strict unknown-field decoding. Deploy this reader before upgrading Runtime images to the required-field status contract. It also accepts the previous status shape during rollout. This change does not alter readiness criteria or reject test-feature images; image admission is enforced by the operator repository/digest policy in #29.

Configuration

Variable Required Default Description
ANTNEST_RUNTIME_CONTROLLER_DATABASE_URL yes none Controller-private PostgreSQL DSN
ANTNEST_RUNTIME_MANAGEMENT_NETWORK yes none Outbound private Docker network shared with Runtimes; the control listener binds a separate Controller network
ANTNEST_RUNTIME_CONTROLLER_LISTEN no 127.0.0.1:8080 Explicit unicast control IP and port; bind the Controller-purpose network in deployment
ANTNEST_RUNTIME_CONTROLLER_HEALTH_LISTEN no 127.0.0.1:8082 Separate loopback-only readiness listener
ANTNEST_RUNTIME_ALLOWED_IMAGES no ["antnest/antnest-runtime"] Operator JSON array of allowed repositories or exact repository SHA256 manifests; disallowed new selections return 422 before Docker
ANTNEST_RUNTIME_PLATFORM no docker Platform adapter; docker is the only accepted value
ANTNEST_DOCKER_HOST no unix:///var/run/docker.sock Docker Engine URL; only unix:// is accepted. Access to this socket is full control of the Docker daemon
ANTNEST_RUNTIME_CONTROLLER_SCOPE no management network name Ownership scope label written to every managed resource
ANTNEST_RUNTIME_SYSTEM_SKILLS_VOLUME no antnest-system-skills Read-only Skill volume mounted when a request carries no prepared Skill set
ANTNEST_SKILL_REGISTRY_URL no none Private Skill Registry origin; requires native service sender credentials when set. Unset disables preparation with 503
ANTNEST_SERVICE_AUTH_MODE yes none Exact token or mtls; shared TLS/receiver/sender configuration applies
ANTNEST_SERVICE_AUTH_CALLERS_FILE token mode none Startup-loaded per-caller SHA256 JSON; only Controller may use control routes
ANTNEST_SERVICE_AUTH_TOKEN_DIR Registry in token mode none Private per-receiver sender files, read on each request
ANTNEST_RUNTIME_SKILL_PREPARER_IMAGE no antnest/runtime-controller:local Installed image used for never-started, network-less Skill volume preparation containers
ANTNEST_RUNTIME_SKILL_MAINTENANCE_VERIFIERS no empty set JSON {"keys":[...]} with at most two Ed25519 public keys; empty disables Runtime Skill maintenance
ANTNEST_RUNTIME_STATUS_TIMEOUT no 5s Bound for one Runtime /status request
ANTNEST_RUNTIME_MUTATION_TIMEOUT no 2m Complete mutation bound, including lock wait
ANTNEST_RUNTIME_RPC_TIMEOUT no 3m Internal RPC bound; must exceed the mutation timeout
ANTNEST_RUNTIME_RECONCILIATION_TIMEOUT no 2m Inventory reconciliation bound
ANTNEST_RUNTIME_CONTROLLER_MONITOR_MAX_RETRY_DELAY no 30s Maximum observation retry delay, including jitter; must be at least 1s
ANTNEST_OBSERVATION_RETENTION no 168h Observation journal retention
ANTNEST_RUNTIME_SSE_HEARTBEAT no 15s Observation Watch heartbeat interval
ANTNEST_TELEMETRY_CAPTURE_RPC_CONTENT no false true or false; true records complete RPC bodies, including credentials, and is forwarded to new Runtimes

Standard OTEL_* variables configure OTLP http/protobuf export. Selected OTEL_* keys, optionally overridden by ANTNEST_RUNTIME_OTEL_*, are forwarded to managed Runtimes. See operations for details.

Transient observation leadership/readiness query and reconciliation failures retry without ending the process. Failed initial reconciliation releases leadership before retrying; readiness is announced only after reconciliation and the Watch handshake succeed. Retries start at 1s, double to the configured limit, include up to 20% positive jitter within that limit, and reset after Watch readiness. Explicit permanent configuration/schema/programming errors still return to startup supervision. The standard Compose service uses restart: unless-stopped as a fallback for process failures.

Control contract revision 14 adds a required monitor_ready field to /status. Monitor retries and Watch reconnection return HTTP 503 with live: true; a ready Watch or ready-leader probe restores HTTP 200. The route reads cached state and does not call Docker or scan Runtimes. A Watch-only disconnect can leave lifecycle calls working; a Docker API outage makes actual platform calls fail. Brief reconnects have a 503 window handled by existing probe failure thresholds. See operations.

Dependencies

  • PostgreSQL: the private runtime_controller schema. Startup fails if the database is unreachable or carries an unknown future migration.
  • Docker Engine: required for lifecycle, Watch, and image resolution. The management network and system Skill volume are checked during lifecycle calls, not at startup; /status never calls Docker.
  • Managed Runtime /status endpoints on the management network.
  • Skill Registry: optional. Without it, Skill set preparation is unavailable, but lifecycle commands that do not reference a prepared set still work.
  • A prebuilt Antnest Runtime image installed on the Docker host.

Build and test

All control routes require verified Controller workload identity; RC never calls its own API. The workload boundary specifies exact token/mTLS, JSON, listening addresses, Registry transport and Docker socket limitations. The retired ANTNEST_SKILL_REGISTRY_API_TOKEN must be removed; any nonempty value fails startup. Deployment wiring and complete token-profile cross-service E2E passed the final integration batch, recorded in the rollout ledger.

Service-local checks, from services/runtime-controller:

make fmt-check
make lint
make test

make lint uses the repository's checked-in .golangci.yml.

From the repository root:

make test-go                           # unit tests with root integration overlays
make test-runtime-controller-postgres  # repository tests against disposable PostgreSQL
node tests/e2e/service-authentication/runtime-controller/run.mjs # isolated native authentication/network/image/lifecycle gate
make e2e-runtime-controller            # builds images and runs the lifecycle E2E
make e2e-runtime-controller-observation-retry # isolated Docker socket outage/recovery
make integration-stage4-skill-prepare  # Registry to Runtime Controller Skill preparation
docker build -f services/runtime-controller/Dockerfile -t antnest/runtime-controller:local .

Unit tests live alongside the service packages. PostgreSQL and Docker integration sources live in tests/integration/go/runtime-controller, and the deployed lifecycle scenario in tests/e2e/runtime-controller/run.sh. The root Go runner overlays those sources into the owning packages so they can reach package-private details. The lifecycle E2E covers initialization from an empty environment, Controller restart recovery, Runtime restart observation, Update, Disable workspace retention, Enable, and Delete cleanup.

The observation retry E2E builds a separately tagged candidate Controller and uses a private Unix-socket proxy to simulate unavailable Docker at startup and a later socket outage. It verifies HTTP 200/503/200, readiness leases, zero process restarts, and successful Runtime Initialize/Delete. It separately disconnects only Watch and proves a lifecycle call still works while monitor_ready is false, then removes its owned containers, volumes, networks and candidate image.

Opt-in checks:

  • Installed image resolution without creating resources:

    ANTNEST_RUNTIME_CONTROLLER_TEST_DOCKER_SOCKET=/var/run/docker.sock \
    ANTNEST_RUNTIME_CONTROLLER_TEST_IMAGE_TAG=antnest/antnest-runtime:local \
    node tests/integration/go/run.mjs runtime-controller --package internal/platform/docker -- \
      -run '^TestInstalledImageResolution$' -count=1
    
  • Build metadata smoke against an existing development stack with Jaeger: node tests/e2e/runtime-controller/build-image-smoke.mjs --project <compose-project>.

  • Creation and observation E2E: make e2e-observation from the service directory (see creation and observation).

  • Update process-crash recovery: make test-crash-recovery from the service directory (see the crash recovery contract).

Test-only variables: ANTNEST_RUNTIME_CONTROLLER_TEST_DATABASE_URL, ANTNEST_RUNTIME_CONTROLLER_TEST_DOCKER_SOCKET, ANTNEST_RUNTIME_CONTROLLER_TEST_IMAGE_TAG, ANTNEST_RUNTIME_CONTROLLER_TEST_MOVED_IMAGE_TAG, ANTNEST_RUNTIME_CONTROLLER_TEST_URL, ANTNEST_RUNTIME_TEST_CONFIGURATION, ANTNEST_RUNTIME_TEST_AGENT_ID, ANTNEST_RUNTIME_CONTROLLER_CRASH_TEST, ANTNEST_RUNTIME_CONTROLLER_CRASH_IMAGE, ANTNEST_RUNTIME_CRASH_EVIDENCE, and ANTNEST_TEST_REAL_RUNTIME_IMAGE.

Documentation

Managed MCP secret bootstrap uses optional ANTNEST_AGENT_CONTROLLER_URL and the existing authenticated outbound transport. It is required for deployments with secret_env descriptors, not for configurations without managed secrets. Controller owns encrypted values; RC owns generation-private read-only delivery and cleanup. See the delivery contract.

Directories

Path Synopsis
cmd
internal
instanceauth
Package instanceauth owns RC's sealed, per-generation Runtime authority.
Package instanceauth owns RC's sealed, per-generation Runtime authority.
rpc
skillset
Package skillset owns Runtime Controller's independent validation of a frozen, organization-scoped system-Skill collection.
Package skillset owns Runtime Controller's independent validation of a frozen, organization-scoped system-Skill collection.
tunnelclient
Package tunnelclient sends only the private Egress generation registration.
Package tunnelclient sends only the private Egress generation registration.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL