Files
rvbox/docs/control-plane.md
T

8.0 KiB

RVBox v1 control plane

../protos/rvbox/v1/control.proto defines the canonical control API. rvc uses the Control gRPC service over the local Unix-domain socket. The server also exposes the same unary operations through an optional JSON-RPC 2.0 HTTP adapter for local debugging and batch automation.

Endpoints

  • Unix socket: enabled by default, mode 0600, owned by the server account.
  • HTTP JSON-RPC: disabled unless enabled; default bind 127.0.0.1:6900. It has no authentication by design. Binding it beyond loopback is an explicit deployment choice and requires external protection.

gRPC streams command history and live events through FollowCommand. JSON-RPC remains simple: callers issue work, query command state, poll event/output pages after an event sequence, append stdin, close stdin, or signal a command. It does not invent a separate event-stream protocol.

The JSON-RPC method names are the lower-camel protobuf operation names: listClients, getClient, listCommands, getCommand, runCommand, appendStdin, closeStdin, signalCommand, getOutput, and the three storage incident methods documented below. Parameters and results use protobuf JSON mapping (including base64 strings for bytes and UTC RFC 3339 strings for timestamps). Control response messages contain successful results only. gRPC failures use canonical non-OK status codes with structured RVBox details where needed; the JSON-RPC adapter maps the same domain errors to standard JSON-RPC error objects. getOutput and getCommand are the polling path for what gRPC exposes as follow streams.

rvc stat CLIENT also shows the active durable client-instance ID and the most recent different instance rejected while that client is live. An operator may run rvc client takeover CLIENT INSTANCE-ID; this creates a one-shot 5-minute authorization for that exact pending claim. Its matching reconnect consumes the authorization and fences the old session. If the old session is no longer live, the replacement connects normally without this command. The JSON-RPC method is authorizeClientTakeover.

Both transports enforce the same decoded field limits. A control gRPC request may be at most 16 MiB; a JSON-RPC HTTP body may be at most 24 MiB to accommodate base64 expansion of the 10 MiB script maximum. ExecutionSpec itself may be at most 768 KiB, which also keeps its agent dispatch below the 1 MiB envelope cap.

CLI semantics

rvc stat maps to ListClients, GetClient, ListCommands, and GetCommand. History pages default to 20 commands and may request at most 100. Historical output uses opaque, byte-bounded cursors and may resume within an output event; it is not numbered or paginated by lines. The server returns uncompressed output slices with event sequence, byte offset, stream, observed timestamp, and server receipt timestamp. rvc may render line-oriented human output, but line boundaries are not storage or pagination boundaries.

rvc run always creates a durable command through unary RunCommand, which returns the UUID. Foreground mode then calls FollowCommand with that UUID, after_event_seq=0, and include_existing=true, streaming output until a terminal event. --background returns immediately after RunCommand. Interrupting the CLI, timing out its local wait, or losing the local control connection never cancels remote work. The explicit rvc kill operation is the only termination path. A foreground caller can resume FollowCommand after its last received event sequence without missing durable history.

FollowCommandResponse wraps either a client-sequenced CommandEvent or a server-created retention marker and exposes server receipt/recording time separately from client observation time. A retention marker does not advance the resume cursor. When retained history is incomplete, the server emits the relevant marker before any terminal event on that stream, so a follower never stops on terminal while believing truncated history was complete.

Mutating requests accept an optional request_id. rvc generates one per mutation and reuses it for transport retries; --request-id lets automation reuse it across CLI invocations. The CLI accepts that option globally and drops it for reads. For RunCommand, the supplied request ID is the command's issue_uuid. For stdin, close, signal, repair, and acknowledgement operations it deduplicates that action while issue_uuid or incident_id continues to identify the target. The server generates a request ID when omitted, preserving simple JSON-RPC use.

The server stores the mutation kind, target, immutable request hash, and result under that ID. An identical retry returns the original result; reuse with any different method, target, or content returns CONFLICT. The record is owned by the affected command or incident for quota and retention. A retry after that owner has been reclaimed cannot repeat the action: it returns retained tombstone information where available or NOT_FOUND.

rvc run --queue-ttl controls how long work may wait for server-confirmed acceptance and defaults to 15 minutes; zero means indefinite. rvc stat distinguishes terminal Expired, Expired (awaiting reconciliation), and actual lifecycle with a late-after-expiry warning. It renders terminal Rejected with the client's structured validation/platform reason; Failed means the requested code actually launched.

rvc run --elevated maps directly to ExecutionSpec.elevated; omission is false. Non-Windows clients reject true as unsupported in v1. Windows chooses the effective context from that bit and launch-time login state: normal commands use active-user when possible and otherwise local-service; elevated commands with an active user try active-user-elevated, active-system, then local-system, while logged-out machines use local-system directly. These fallbacks finish before launch_prepared and never retry a process.

Detailed rvc stat CLIENT ISSUE_UUID output shows requested elevation, every attempted Windows context, selection/fallback detail, effective context and process-token SID, and target session ID/owner SID when applicable. active-system is rendered conspicuously as SYSTEM in another user's session, never as that user. A pre-launch rejection shows the structured final context- selection error rather than implying requested code ran.

rvc append turns a string into StdinWrite with append_newline=true unless the caller selects raw mode; --file supplies raw bytes; --attach streams local standard input. CloseStdin is available separately. All stdin actions are acknowledged and idempotent.

Query behavior

Command details expose persisted request fields, effective execution metadata, lifecycle, terminal result, output availability/truncation, current resource snapshot, and suspected_hung diagnostics. The latter is informational and must not be shown as a terminal state. Pagination response cursors are stable within their declared ordering (newest issue time for command lists; increasing event_seq for events/output).

Control failures are never embedded in otherwise-successful response messages. They use canonical gRPC status codes with structured RVBox details; JSON-RPC returns the corresponding JSON-RPC error object, and rvc maps the same domain error to a stable exit code rather than parsing text.

Storage incidents

rvc storage incidents lists unresolved storage incidents by default and can include resolved history. rvc storage repair INCIDENT attempts only a known safe repair. rvc storage acknowledge INCIDENT --note ... accepts documented irrecoverable loss and clears that incident from dirty health. Both mutations use the same optional --request-id behavior as other mutations. An acknowledgement note is required and bounded to 4 KiB. Repair or acknowledgement changes incident state; it does not delete history as part of that action; resolved history later follows the independent audit/incident quota. The JSON-RPC equivalents are listStorageIncidents, repairStorageIncident, and acknowledgeStorageIncident.