docs: add initial RVBox design and protocol contracts

This commit is contained in:
2026-08-21 09:02:57 +00:00
commit 97c2d5cf45
10 changed files with 1138 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
syntax = "proto3";
package rvbox.v1;
option go_package = "github.com/rvbox/rvbox/gen/go/rvbox/v1;rvboxv1";
import "google/protobuf/timestamp.proto";
import "rvbox/v1/common.proto";
// One AgentEnvelope is carried in one binary WebSocket message.
message AgentEnvelope {
// Empty only for ClientHello. All other envelopes are fenced to a session.
string session_id = 1;
uint64 session_generation = 2;
string message_id = 3;
oneof payload {
ClientHello client_hello = 10;
ServerWelcome server_welcome = 11;
CommandDispatch command_dispatch = 12;
CommandAccepted command_accepted = 13;
CommandEvent command_event = 14;
EventAck event_ack = 15;
StdinWrite stdin_write = 16;
CloseStdin close_stdin = 17;
SignalCommand signal_command = 18;
ScriptChunk script_chunk = 19;
ScriptCommit script_commit = 20;
ClientCapacity client_capacity = 21;
ReconcileRequest reconcile_request = 22;
ReconcileSnapshot reconcile_snapshot = 23;
AgentError error = 24;
ClientOutputTruncated client_output_truncated = 25;
}
}
message ClientHello {
// Configured hostname; opaque 1-128 ASCII characters.
string client_id = 1;
ProtocolRange supported_protocol = 2;
string daemon_version = 3;
Platform platform = 4;
string architecture = 5;
string daemon_cwd = 6;
repeated ShellType supported_shells = 7;
string reconnect_uuid = 8;
uint32 max_running_commands = 9;
uint32 max_queued_commands = 10;
google.protobuf.Timestamp sent_at = 11;
}
message ServerWelcome {
ProtocolVersion selected_protocol = 1;
// AgentEnvelope.session_id and .session_generation are canonical.
google.protobuf.Timestamp server_time = 2;
}
message CommandDispatch {
string issue_uuid = 1;
uint64 command_revision = 2;
uint64 target_session_generation = 3;
google.protobuf.Timestamp issue_time = 4;
ExecutionSpec spec = 5;
}
message CommandAccepted {
string issue_uuid = 1;
uint64 command_revision = 2;
bool accepted = 3;
ControlError rejection = 4;
}
// Cumulative durable acknowledgement of client CommandEvent values.
message EventAck {
string issue_uuid = 1;
uint64 through_event_seq = 2;
}
message StdinWrite {
string issue_uuid = 1;
uint64 write_seq = 2;
bytes data = 3;
bool append_newline = 4;
}
message CloseStdin {
string issue_uuid = 1;
uint64 write_seq = 2;
}
message SignalCommand {
string issue_uuid = 1;
uint64 command_revision = 2;
SignalKind signal = 3;
}
message ScriptChunk {
string issue_uuid = 1;
uint64 offset = 2;
bytes data = 3;
bytes sha256 = 4;
}
message ScriptCommit {
string issue_uuid = 1;
uint64 size_bytes = 2;
bytes sha256 = 3;
}
message ClientCapacity {
uint32 running_commands = 1;
uint32 queued_commands = 2;
uint32 max_running_commands = 3;
uint32 max_queued_commands = 4;
}
message ReconcileRequest {
repeated ReconcileTarget targets = 1;
}
message ReconcileTarget {
string issue_uuid = 1;
uint64 last_server_event_seq = 2;
uint64 command_revision = 3;
}
// Sent only for requests in ReconcileRequest; server-confirmed terminal history
// is intentionally not requested.
message ReconcileSnapshot {
string issue_uuid = 1;
bool known_to_client = 2;
CommandLifecycle lifecycle = 3;
uint64 last_client_event_seq = 4;
uint64 command_revision = 5;
}
// Sent before retained replay data when an offline client had to rotate
// unacknowledged output to remain within its hard spool limits.
message ClientOutputTruncated {
string issue_uuid = 1;
OutputTruncation truncation = 2;
}
message AgentError {
ControlError error = 1;
bool close_session = 2;
}
+217
View File
@@ -0,0 +1,217 @@
syntax = "proto3";
package rvbox.v1;
option go_package = "github.com/rvbox/rvbox/gen/go/rvbox/v1;rvboxv1";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
// An inclusive protocol-version range advertised during registration.
message ProtocolRange {
uint32 major = 1;
uint32 min_minor = 2;
uint32 max_minor = 3;
}
message ProtocolVersion {
uint32 major = 1;
uint32 minor = 2;
}
enum Platform {
PLATFORM_UNSPECIFIED = 0;
PLATFORM_LINUX = 1;
PLATFORM_DARWIN = 2;
PLATFORM_WINDOWS = 3;
PLATFORM_OTHER_UNIX = 4;
}
enum ShellType {
SHELL_TYPE_UNSPECIFIED = 0;
SHELL_SH = 1;
SHELL_BASH = 2;
SHELL_CMD = 3;
SHELL_POWERSHELL = 4;
}
enum Compression {
COMPRESSION_UNSPECIFIED = 0;
COMPRESSION_NONE = 1;
COMPRESSION_ZSTD = 2;
}
enum CommandLifecycle {
COMMAND_LIFECYCLE_UNSPECIFIED = 0;
COMMAND_QUEUED = 1;
COMMAND_DISPATCHED = 2;
COMMAND_ACCEPTED = 3;
COMMAND_RUNNING = 4;
COMMAND_SUCCEEDED = 5;
COMMAND_FAILED = 6;
COMMAND_TERMINATED = 7;
COMMAND_CANCELLED = 8;
COMMAND_INTERRUPTED = 9;
}
enum StreamKind {
STREAM_KIND_UNSPECIFIED = 0;
STREAM_STDOUT = 1;
STREAM_STDERR = 2;
}
enum SignalKind {
SIGNAL_KIND_UNSPECIFIED = 0;
SIGNAL_HUP = 1;
SIGNAL_INT = 2;
SIGNAL_TERM = 3;
SIGNAL_KILL = 4;
SIGNAL_USR1 = 5;
SIGNAL_USR2 = 6;
}
// These flags are composable. Their numeric limits are local administrator
// policy rather than part of the interoperable protocol.
enum ExecutionProfile {
EXECUTION_PROFILE_UNSPECIFIED = 0;
EXECUTION_PROFILE_LIGHT = 1;
EXECUTION_PROFILE_CPU_MEDIUM = 2;
EXECUTION_PROFILE_CPU_HEAVY = 3;
EXECUTION_PROFILE_MEM_MEDIUM = 4;
EXECUTION_PROFILE_MEM_HEAVY = 5;
EXECUTION_PROFILE_DISK_MEDIUM = 6;
EXECUTION_PROFILE_DISK_HEAVY = 7;
}
message ScriptDescriptor {
string filename = 1;
uint64 size_bytes = 2;
bytes sha256 = 3;
}
// Execution settings sent to the client. A script's bytes travel separately.
message ExecutionSpec {
ShellType shell_type = 1;
string cwd = 2;
map<string, string> env_overrides = 3;
repeated ExecutionProfile execution_profiles = 4;
oneof source {
string command_text = 5;
ScriptDescriptor script = 6;
}
}
message CommandRecord {
string issue_uuid = 1;
string target_client_id = 2;
google.protobuf.Timestamp issue_time = 3;
google.protobuf.Timestamp server_receipt_time = 4;
ExecutionSpec spec = 5;
CommandLifecycle lifecycle = 6;
uint64 last_event_seq = 7;
int32 exit_code = 8;
google.protobuf.Timestamp terminal_time = 9;
bool output_truncated = 10;
uint64 retained_compressed_bytes = 11;
}
message LifecycleChange {
CommandLifecycle lifecycle = 1;
int32 exit_code = 2;
string detail = 3;
}
// data is compressed according to compression. uncompressed_size is mandatory
// when compression is ZSTD and must be validated before decompression.
message OutputChunk {
StreamKind stream = 1;
Compression compression = 2;
bytes data = 3;
uint64 uncompressed_size = 4;
uint64 compressed_size = 5;
}
message ResourceSnapshot {
uint64 resident_memory_bytes = 1;
uint64 virtual_memory_bytes = 2;
google.protobuf.Duration cpu_time = 3;
uint64 read_bytes = 4;
uint64 write_bytes = 5;
string process_state = 6;
string wait_reason = 7;
bool suspected_hung = 8;
string diagnostic_detail = 9;
}
enum OutputTruncationSource {
OUTPUT_TRUNCATION_SOURCE_UNSPECIFIED = 0;
OUTPUT_TRUNCATION_SOURCE_CLIENT_SPOOL = 1;
OUTPUT_TRUNCATION_SOURCE_SERVER_COMMAND_WINDOW = 2;
OUTPUT_TRUNCATION_SOURCE_SERVER_CLIENT_CAP = 3;
}
// Persistent query metadata for a missing contiguous event range. It is not a
// CommandEvent and therefore does not consume an event_seq.
message OutputTruncation {
uint64 first_removed_event_seq = 1;
uint64 last_removed_event_seq = 2;
uint64 removed_compressed_bytes = 3;
uint64 removed_uncompressed_bytes = 4;
string reason = 5;
OutputTruncationSource source = 6;
}
message StdinAcknowledgement {
uint64 write_seq = 1;
bool stdin_closed = 2;
string detail = 3;
}
message SignalResult {
SignalKind signal = 1;
bool accepted = 2;
bool graceful_delivery_attempted = 3;
bool forced_termination_used = 4;
string detail = 5;
}
message ScriptUploadStatus {
uint64 received_bytes = 1;
bool complete = 2;
string detail = 3;
}
// Client-originated ordered history. event_seq is strictly increasing per
// issue_uuid and is reused exactly on retransmission.
message CommandEvent {
string issue_uuid = 1;
uint64 event_seq = 2;
google.protobuf.Timestamp observed_at = 3;
oneof payload {
LifecycleChange lifecycle = 10;
OutputChunk output = 11;
ResourceSnapshot resource = 12;
StdinAcknowledgement stdin_ack = 13;
SignalResult signal_result = 14;
ScriptUploadStatus script_status = 15;
}
}
message ControlError {
enum Code {
CODE_UNSPECIFIED = 0;
INVALID_ARGUMENT = 1;
NOT_FOUND = 2;
OFFLINE = 3;
CAPACITY_EXHAUSTED = 4;
CONFLICT = 5;
UNSUPPORTED = 6;
PROTOCOL_ERROR = 7;
TRANSIENT = 8;
INTERNAL = 9;
}
Code code = 1;
string message = 2;
bool retryable = 3;
string issue_uuid = 4;
}
+148
View File
@@ -0,0 +1,148 @@
syntax = "proto3";
package rvbox.v1;
option go_package = "github.com/rvbox/rvbox/gen/go/rvbox/v1;rvboxv1";
import "rvbox/v1/common.proto";
service Control {
rpc ListClients(ListClientsRequest) returns (ListClientsResponse);
rpc GetClient(GetClientRequest) returns (GetClientResponse);
rpc ListCommands(ListCommandsRequest) returns (ListCommandsResponse);
rpc GetCommand(GetCommandRequest) returns (GetCommandResponse);
rpc RunCommand(RunCommandRequest) returns (RunCommandResponse);
rpc RunCommandAndFollow(RunCommandRequest) returns (stream CommandEvent);
rpc FollowCommand(FollowCommandRequest) returns (stream CommandEvent);
rpc AppendStdin(AppendStdinRequest) returns (AppendStdinResponse);
rpc CloseStdin(CloseStdinRequest) returns (CloseStdinResponse);
rpc SignalCommand(ControlSignalCommandRequest) returns (ControlSignalCommandResponse);
rpc GetOutput(GetOutputRequest) returns (GetOutputResponse);
}
message ClientSummary {
string client_id = 1;
bool connected = 2;
google.protobuf.Timestamp connected_at = 3;
google.protobuf.Timestamp last_seen_at = 4;
uint32 running_commands = 5;
uint32 queued_commands = 6;
Platform platform = 7;
string architecture = 8;
string daemon_version = 9;
string daemon_cwd = 10;
repeated ShellType supported_shells = 11;
}
message ListClientsRequest {
uint32 page_size = 1;
string page_token = 2;
}
message ListClientsResponse {
repeated ClientSummary clients = 1;
string next_page_token = 2;
}
message GetClientRequest {
string client_id = 1;
}
message GetClientResponse {
ClientSummary client = 1;
ControlError error = 2;
}
message ListCommandsRequest {
string client_id = 1;
bool include_terminal = 2;
uint32 page_size = 3;
string page_token = 4;
}
message ListCommandsResponse {
repeated CommandRecord commands = 1;
string next_page_token = 2;
ControlError error = 3;
}
message GetCommandRequest {
string client_id = 1;
string issue_uuid = 2;
}
message GetCommandResponse {
CommandRecord command = 1;
ResourceSnapshot latest_resource = 2;
ControlError error = 3;
}
// For script execution, script_content contains the bytes whose descriptor is
// placed in spec.script. The server chunks it for the Agent protocol.
message RunCommandRequest {
string target_client_id = 1;
ExecutionSpec spec = 2;
bytes script_content = 3;
}
message RunCommandResponse {
string issue_uuid = 1;
CommandLifecycle lifecycle = 2;
ControlError error = 3;
}
message FollowCommandRequest {
string client_id = 1;
string issue_uuid = 2;
uint64 after_event_seq = 3;
bool include_existing = 4;
}
message AppendStdinRequest {
string client_id = 1;
string issue_uuid = 2;
bytes data = 3;
bool append_newline = 4;
}
message AppendStdinResponse {
uint64 write_seq = 1;
ControlError error = 2;
}
message CloseStdinRequest {
string client_id = 1;
string issue_uuid = 2;
}
message CloseStdinResponse {
uint64 write_seq = 1;
ControlError error = 2;
}
message ControlSignalCommandRequest {
string client_id = 1;
string issue_uuid = 2;
SignalKind signal = 3;
}
message ControlSignalCommandResponse {
uint64 command_revision = 1;
ControlError error = 2;
}
message GetOutputRequest {
string client_id = 1;
string issue_uuid = 2;
repeated StreamKind streams = 3;
uint64 after_event_seq = 4;
uint32 page_size = 5;
}
message GetOutputResponse {
repeated CommandEvent events = 1;
string next_page_token = 2;
bool output_truncated = 3;
ControlError error = 4;
repeated OutputTruncation truncations = 5;
}