Protocol
The runtime protocol is the binary attach boundary for external instances such as REPL clients, editors, batch workers, and test harnesses.
This document defines the wire format closely enough to implement both ends of the connection without an additional schema layer.
Scope
The protocol exists to:
- attach one client instance to a long-lived
vox-runtime; - open or attach that client to a runtime-managed interactive session;
- load, reload, run, and unload script artifacts for that instance;
- move arguments and results across the boundary;
- manage runtime-owned handles for large values;
- expose runtime cache and
Econmaintenance operations.
The protocol does not model REPL history, completion menus, or client-side synthetic source assembly. Those stay in the client.
The transport connection and the interactive session are distinct concepts. A connection is the ordered byte stream used by one attached client. A session is the shareable interactive environment that may later be revisited or shared with other clients while it still has attached endpoints or has been marked as reserved.
Connection Model
- transport: any ordered byte stream such as a Unix socket or TCP connection;
- endianness: little-endian for all fixed-width integers and floats;
- lifetime: one connection equals one attached client instance, not the entire lifetime of a shared interactive session;
- concurrency: the client may pipeline requests and match responses by
request_id; - isolation: interactive bindings are scoped to a runtime session rather than ambiently shared across all clients;
- sharing: library mounts, caches,
Econstate, and handle storage are owned by the runtime and may be shared across connections; - disconnect: dropping the connection releases connection-owned references; when a session reaches zero attached endpoints, the runtime may recycle it unless that session is reserved.
The first frame on every connection must be HELLO.
IPC Model
The runtime protocol is the IPC surface for Vox tools that share one runtime.
Normal same-runtime transfer uses these methods:
- inline copy for small serializable values;
- handle passing for large or opaque runtime-owned values;
- callable references for functions, compiled entry points, and retained closures that the runtime can represent safely;
- automatic runtime cache reuse instead of explicit client-to-client cache copy.
Cross-runtime movement is different from same-runtime IPC. It uses explicit export/import operations and versioned bundles rather than raw handle reuse.
Frame Format
Every message begins with this fixed 24-byte header:
offset size field
0 4 magic = 0x56585254 // "VXRT"
4 2 version
6 1 kind
7 1 opcode
8 4 flags
12 4 request_id
16 4 target_id
20 4 payload_len
Rules:
versionis0on the initialHELLOrequest and the selected protocol version on every later frame;request_idis chosen by the client for requests and copied by the server into the matching response;target_idis0when the opcode does not act on an existing object;payload_lenmay be0;- after the header, exactly
payload_lenbytes follow.
kind values:
0: request1: success response2: error response3: event
flags are a bitset:
0x0000_0001: payload contains diagnostics0x0000_0002: payload contains an inline value0x0000_0004: payload contains a handle result
All other bits are reserved and must be sent as 0.
Opcodes
opcode is a one-byte enum:
0x01:HELLO0x02:PING0x03:OPEN_SESSION0x04:EVALUATE_SESSION0x05:DROP_SESSION_ITEM0x06:RESET_SESSION0x07:SNAPSHOT_SESSION0x08:RESTORE_SESSION0x09:RUN_SESSION_SCRIPT0x0a:SET_SESSION_XOPT0x0b:CLOSE_SESSION0x0c:LIST_SESSIONS0x0d:SET_SESSION_RESERVED0x0e:SET_SESSION_OPT0x0f:GET_SESSION_OPT0x14:DUMP_SESSION_OPT0x10:MOUNT_LIBRARY0x11:UNMOUNT_LIBRARY0x20:LOAD_SCRIPT0x21:RELOAD_SCRIPT0x22:UNLOAD_SCRIPT0x23:SET_XOPT0x24:RUN_SCRIPT0x25:GET_OPT0x26:DUMP_OPT0x30:RETAIN_HANDLE0x31:DESCRIBE_HANDLE0x32:RELEASE_HANDLE0x33:READ_HANDLE_DATA0x40:REFRESH_ECON0x41:CACHE_STATS0x42:CLEAR_CACHE0x7f:SHUTDOWN
The server must reject unknown opcodes with ERR_UNSUPPORTED_OPCODE.
Primitive Encodings
The protocol uses only these primitive encodings:
u8,u16,u32,u64i64f64bytes:u32 lenfollowed bylenraw bytesstring:bytescontaining UTF-8
There is no map or self-describing object envelope at the frame level.
Optimization modes use these u8 values:
0:NOpt1:IOpt2:SOpt
Optimization settings are encoded as:
u8 default_xopt // 0=NOpt, 1=IOpt, 2=SOpt
u8 reserved[3]
u32 override_count
repeated override_count:
string object // function name; "module" is reserved for the module
u8 object_xopt // 0=NOpt, 1=IOpt, 2=SOpt
u8 reserved[3]
Optimization status rows are encoded as:
u32 status_count
repeated status_count:
string object
u8 requested_xopt // 0=NOpt, 1=IOpt, 2=SOpt
u8 rank // 0=pending, 1=baseline, 2=interactive,
// 3=sealed-ownership, 4=sealed-demand,
// 5=sealed-materialization
u8 has_artifact
u8 mir_available
u8 wasm_available
u32 artifact_id // present only when has_artifact != 0
Optimization dump kinds use these u8 values:
0: MIR text1: wasm bytes rendered as diagnostic text
Value Encoding
Arguments and inline results use the Value encoding below:
tag: u8
payload: tag-specific
Tags:
0x00:null0x01:boolfollowed byu8(0or1)0x02:intfollowed byi640x03:floatfollowed byf640x04:string0x05:tuplefollowed byu32 count, thencountencoded values0x06:recordfollowed byu32 field_count, then repeatedstring nameplus encoded value0x07:handlefollowed byu32 handle_id
Encoding rules:
- values smaller than the negotiated inline limit are sent inline;
- large host values must be returned as
handle; - a client may send a previously received
handlevalue back as an argument; - when a result does not fit the inline limit, the runtime prefers
returning a
handleover copying the value into the response. - inline values are copy-transferred, not shared by later mutation.
The protocol deliberately avoids textual field names outside inline records.
Serialized Handle Data Encoding
When a handle exposes pure-serializable data, READ_HANDLE_DATA returns bytes
encoded with the same primitive style as Value, but with one extra tag for
lists and without nested handles.
Tags:
0x00:null0x01:bool0x02:int0x03:float0x04:string0x05:tuple0x06:record0x08:list
Rules:
- nested
handlevalues are not valid inside serialized handle data; - opaque handles such as functions must reject
READ_HANDLE_DATA; - clients may fetch the full byte stream eagerly or page it in chunks.
Function transfer rules:
- functions normally cross the process boundary as callable references, not raw executable blobs;
- top-level functions and compiled script entry points are addressable by runtime-issued callable ids or by symbol plus revision metadata;
- closures may only be transferred when the runtime can retain their captured environment safely as a runtime-owned callable object.
Diagnostics
Compilation and runtime failures may carry diagnostics. A diagnostic block is:
u32 count
repeat count times:
u8 severity // 0=error, 1=warning, 2=note
string code
string message
string source_name // empty when unavailable
u32 start_byte
u32 end_byte
Diagnostics are optional on success and recommended on compile failures.
Handshake
HELLO is mandatory and must be the first request.
The HELLO request must use header version = 0. The HELLO response must
return the selected version both in the header and in the payload.
Request payload:
u16 min_version
u16 max_version
u32 client_caps
u32 max_inline_value_bytes
Response payload:
u16 selected_version
u16 reserved
u32 server_caps
u32 instance_id
u32 max_payload_bytes
u32 max_inline_value_bytes
Handshake rules:
- the server selects one version within the requested range;
- if there is no overlap, the server replies with
ERR_VERSION_MISMATCH; instance_ididentifies the attached instance in logs and metrics only;- both sides must honor the smaller of the client and server inline limits.
Operation Payloads
This section defines the exact payload for each opcode. target_id in the
frame header identifies the object being acted on when required.
PING
Request payload: empty.
Success response payload:
u64 runtime_uptime_ms
OPEN_SESSION
target_id must be 0.
Request payload:
u8 open_mode // 0=attach, 1=create, 2=attach_or_create
u8 selector_kind // 0=anonymous, 1=session_id, 2=session_name
u8 reserved[2]
u32 session_id // present only when selector_kind = 1
string session_name // present only when selector_kind = 2
Success response payload:
u32 session_id
Rules:
selector_kind = 0withopen_mode = createopens a fresh anonymous session;selector_kind = 1attaches to an existing session by id;selector_kind = 2withopen_mode = attachattaches to an existing named session;selector_kind = 2withopen_mode = createcreates a fresh named session and fails if that name already exists;selector_kind = 2withopen_mode = attach_or_createreattaches when the name already exists or creates the named session otherwise;- session ids are runtime-issued and may be reused on later requests across multiple connections attached to the same runtime instance.
CLOSE_SESSION
target_id is session_id.
Request payload: empty.
Success response payload: empty.
Closing a session endpoint decrements that session’s attached-endpoint count on the current connection.
LIST_SESSIONS
target_id must be 0.
Request payload: empty.
Success response payload:
u32 session_count
SessionSummary sessions[session_count]
Where each SessionSummary is:
u32 session_id
u8 has_name
u8 reserved
u8 reserved_bytes[2]
u64 attached_endpoints
string session_name // present only when has_name = 1
SET_SESSION_RESERVED
target_id is session_id.
Request payload:
u8 reserved // 0=false, 1=true
u8 reserved_bytes[3]
Success response payload: empty.
EVALUATE_SESSION
target_id is session_id.
Request payload:
string submission
Success response payload:
- empty when the submission only changes session state and yields no result;
- otherwise the same result encoding used by
RUN_SCRIPT.
DROP_SESSION_ITEM
target_id is session_id.
Request payload:
string name
Success response payload:
u8 removed // 0=false, 1=true
RESET_SESSION
target_id is session_id.
Request payload: empty.
Success response payload: empty.
SNAPSHOT_SESSION
target_id is session_id.
Request payload: empty.
Success response payload:
string snapshot_source
RESTORE_SESSION
target_id is session_id.
Request payload:
string label
string snapshot_source
Success response payload: empty.
RUN_SESSION_SCRIPT
target_id is session_id.
Request payload:
string logical_path
string source_text
Success response payload:
- the same result encoding used by
RUN_SCRIPT.
SET_SESSION_XOPT
target_id is session_id.
Compatibility command for setting only the session default optimization mode.
New clients use SET_SESSION_OPT.
Request payload:
u8 default_xopt // 0=NOpt, 1=IOpt, 2=SOpt
u8 reserved[3]
Success response payload: empty.
SET_SESSION_OPT
target_id is session_id.
Request payload:
u8 xopt // 0=NOpt, 1=IOpt, 2=SOpt
u8 reserved[3]
u32 object_count
string objects[object_count]
If object_count is 0, the runtime updates the session default mode. If
objects are supplied, module updates the module/default mode and function
names update per-function overrides. The session recompiles its active artifact
when one exists.
Success response payload: empty.
GET_SESSION_OPT
target_id is session_id.
Request payload:
u8 has_object
u8 reserved[3]
string object // present only when has_object != 0
Success response payload:
OptimizationStatus[]
With no object, the response includes the module and all known function objects.
DUMP_SESSION_OPT
target_id is session_id.
Request payload:
u8 dump_kind // 0=MIR, 1=wasm
u8 reserved[3]
string object // empty or "module" for the module
Success response payload:
u8 present
u8 reserved[3]
u8 dump_kind // present only when present != 0
u8 reserved[3]
string object // present only when present != 0
string text // present only when present != 0
MOUNT_LIBRARY
Request payload:
u8 source_kind // 0=filesystem path, 1=manifest bytes, 2=bundle bytes
u8 reserved[3]
bytes source
Source kind 2 contains the complete .voxlib file. It is the portable mount
form used by remote runners; the server validates and retains its manifest,
wasm implementation, and metadata. Source kind 1 registers a manifest for a
host implementation already available inside the runtime process. Source kind
0 is reserved and is not accepted by the current server.
Success response payload:
u32 library_id
u64 library_revision
UNMOUNT_LIBRARY
target_id is library_id.
Request payload: empty.
Success response payload: empty.
LOAD_SCRIPT
target_id must be 0.
Request payload:
u8 source_kind // 0=source text, 1=precompiled artifact
u8 default_xopt // 0=NOpt, 1=IOpt, 2=SOpt
u8 reserved[2]
OptimizationSettings optimization_settings
string logical_path
bytes source
default_xopt and optimization_settings.default_xopt must match. The
duplicated byte keeps the module/default mode visible in the fixed part of the
payload while allowing per-object overrides to travel with the same request.
Success response payload:
u32 script_id
u64 script_revision
u32 parameter_count
u8 result_is_handle_capable
If compilation produces diagnostics but still yields a runnable artifact, the server may return success with the diagnostics flag set.
RELOAD_SCRIPT
target_id is script_id.
Request payload matches LOAD_SCRIPT.
Success response payload:
u64 script_revision
u32 parameter_count
u8 result_is_handle_capable
UNLOAD_SCRIPT
target_id is script_id.
Request payload: empty.
Success response payload: empty.
SET_XOPT
target_id must be 0.
Request payload:
u8 default_xopt // 0=NOpt, 1=IOpt, 2=SOpt
u8 reserved[3]
Success response payload: empty.
Current runtime behavior:
SET_XOPTupdates the connection default used by laterLOAD_SCRIPTandRELOAD_SCRIPTrequests;RUN_SCRIPTmay usexopt_override = 255to execute with the artifact’s compiled optimization level, or0,1, or2for a one-off run override. The tree-walk fallback preserves source behavior for all modes.
RUN_SCRIPT
target_id is script_id.
Request payload:
u8 xopt_override // 255=use script default, else 0/1/2
u8 reserved[3]
u32 arg_count
Value args[arg_count]
Success response payload:
- when the result is inline: one encoded
Value; - when the result is large:
u32 handle_id.
The server must return exactly one result value.
GET_OPT
target_id is script_id.
Request payload:
OptimizationSettings optimization_settings
Success response payload:
OptimizationStatus[]
This lets clients query the optimization state of a loaded artifact using the same metadata they submitted at compile time.
DUMP_OPT
target_id is script_id.
Request payload:
u8 dump_kind // 0=MIR, 1=wasm
u8 reserved[3]
string object // empty or "module" for the module; function name for MIR
Success response payload:
u8 present
u8 reserved[3]
u8 dump_kind // present only when present != 0
u8 reserved[3]
string object // present only when present != 0
string text // present only when present != 0
MIR dumps are available for the module and function bodies when the artifact contains MIR. Wasm dumps are currently module-level only.
RETAIN_HANDLE
target_id is handle_id.
Request payload:
u32 extra_refs
Success response payload:
u32 handle_id
u32 retained_refs
DESCRIBE_HANDLE
target_id is handle_id.
Request payload: empty.
Success response payload:
u32 handle_id
string type_name
u64 approx_size_bytes
u32 ref_count
u32 handle_flags
string summary
handle_flags currently uses:
0x0000_0001: pure-serializable0x0000_0002: externally pinned
RELEASE_HANDLE
target_id is handle_id.
Request payload:
u32 release_refs // usually 1
Success response payload:
u32 remaining_refs
READ_HANDLE_DATA
target_id is handle_id.
This operation is valid only for pure-serializable handles.
Request payload:
u64 offset
u32 max_bytes
Success response payload:
u64 total_bytes
bytes chunk
chunk contains a slice of the serialized handle-data byte stream beginning at
offset.
REFRESH_ECON
Request payload:
string econ_key
Success response payload:
u64 econ_version
u64 invalidated_cache_entries
CACHE_STATS
Request payload: empty.
Success response payload:
u64 artifact_entries
u64 pure_cache_entries
u64 pure_cache_bytes
u64 live_handles
CLEAR_CACHE
Request payload:
u8 scope // 0=all, 1=artifacts, 2=pure-cache
u8 reserved[3]
Success response payload:
u64 cleared_entries
SHUTDOWN
Request payload: empty.
Success response payload: empty.
Only privileged clients may issue this opcode.
Error Model
An error response uses kind = 2 and this payload:
u32 error_code
string message
optional diagnostic block
Recommended error codes:
1:ERR_VERSION_MISMATCH2:ERR_BAD_FRAME3:ERR_UNSUPPORTED_OPCODE4:ERR_UNKNOWN_LIBRARY5:ERR_UNKNOWN_SCRIPT6:ERR_UNKNOWN_HANDLE7:ERR_COMPILE_FAILED8:ERR_RUNTIME_FAILED9:ERR_BAD_ARGUMENT10:ERR_PERMISSION_DENIED
ERR_BAD_FRAME is fatal to the connection.
Events
Events are optional and never replace the required response to a request.
If implemented, supported events are:
0x80:HANDLE_DROPPEDwith payloadu32 handle_id0x81:ECON_INVALIDATEDwith payloadstring econ_keyplusu64 version
Clients must ignore unknown event opcodes.
Performance Rules
- keep the header fixed-width and branch-light to parse;
- do not use JSON, text keys, or per-message schema negotiation;
- prefer integer ids and handle passing over value copying;
- allow request pipelining on one connection;
- keep script artifacts connection-local while keeping durable interactive state session-local.