Files
MetaCrate/docs/grid-agent-control-plane.md
Chili Palmer 962d17257d
Some checks failed
CI / rust-skia (Rust only) (push) Successful in 2m44s
CI / required (push) Failing after 2m43s
feat(grid-agent): add portable control plane (#126)
2026-08-18 04:29:12 +00:00

4.7 KiB

Grid-agent control plane v1

The grid agent exposes one transport-neutral request/response and event API. Integrated clients receive an InProcessControlClient; split clients use the same ControlRequestEnvelope, ControlResponseEnvelope, and ControlEvent types over TCP. No terminal, Unix socket, named-pipe, permission-bit, or platform-specific type appears in the core API.

Transport and negotiation

TCP frames are a four-byte unsigned big-endian length followed by one UTF-8 JSON value. Empty, partial, malformed, idle, and oversized frames close the connection within configured deadlines. The first frame must be:

{"frame":"hello","body":{"version":1,"token":"separate-operator-capability"}}

The server answers with a hello containing version 1, the authenticated operator or observer role, or a typed error. Every later client frame is a request; server frames are response or event. Request IDs contain at most 96 ASCII identifier bytes and cannot be replayed on a connection. A caller can cancel an active request by its ID. Responses and events can arrive in either order, so clients correlate responses by request ID.

Plain TCP binds only to IPv4 or IPv6 loopback. Remote control is off by default and a non-loopback address is rejected unless both DER certificate and private key paths are explicitly configured. Remote mode uses Rustls before the hello exchange and emits an operator warning at startup. The TLS client API requires the embedding client to supply its certificate roots and server-name policy. Every split connection must still authenticate with a dedicated control token; observer and operator tokens must differ and configuration rejects reuse of an LLM API key or grid password. Tokens are absent from protocol errors, audit, serialization traits, and diagnostics.

Remote service configuration uses the JSON fields control.remote_tls_certificate_der and control.remote_tls_private_key_der together with a non-loopback control.listen value. Both files are bounded, non-symlink DER files. Omitting either field, or selecting a non-loopback address without both, fails configuration before a listener is created.

Requests

The JSON request envelope is stable and versioned. For example:

{"version":1,"request_id":"health-1","request":{"method":"health"}}

Observers can call health, runtime, list_sessions, list_scheduled_jobs, list_pending_approvals, list_audit_events, and subscribe_events. Operators can additionally call cancel_request, pause_autonomy, resume_autonomy, cancel_action, decide_approval, force_reconnect, expire_conversation, set_roaming_job, inject_operator_message, and graceful_shutdown. Cancellation is a mutation and is operator-only. List requests use an opaque numeric cursor and a page size of 1 through 100.

Runtime projections contain lifecycle/readiness, session generation, safe region and pose fields when known, behavior mode, control-queue utilization, and aggregate budget use. Conversation responses contain metadata only. Audit and approval responses exclude arguments, prompt contents, credentials, authorization headers, capability URLs, model reasoning, and filesystem data. cancel_action binds to the exact bounded action ID carried by behavior audit observations; it can cancel a queued or executing embodied action without preempting unrelated work. The built-in roaming job ID is default-roaming.

Errors are typed as authentication_failed, version_mismatch, permission_denied, invalid_request, replay, not_found, conflict, cancelled, timed_out, busy, backpressure, frame_too_large, idle_timeout, transport_closed, or internal, with a bounded safe message and a retryable flag.

Events, bounds, and reconnect

Events carry monotonically increasing sequence numbers. A subscription can resume after its last observed sequence. If retained history no longer covers that point, its first item is an explicit gap with first_available and last_missed, followed by retained events. Mutation events record the authenticated role/connection principal, operation name, and completed or rejected outcome, never request contents.

Connections, unauthenticated handshakes, in-flight request tasks, subscriptions, command/event queues, replay history, event history, event bytes, event rate, frames, pages, idle time, request time, and writes all have validated hard bounds. A full event or writer queue disconnects the slow consumer instead of blocking the headless service. With zero clients, event publication retains only the configured history and creates no background work. Server shutdown cancels and joins all listener, connection, request, and writer tasks within its deadline.