Node Agent API¶
The gRPC service every client talks to: barista.node.v1alpha1.NodeAgent, served
over TCP or a unix domain socket. The barista CLI is a thin client over it, and so
is everything else.
The protobuf schema is the single source of truth. Generated clients exist for Rust and Python; any gRPC-capable language works.
Service¶
Identity and capabilities¶
| RPC | Returns |
|---|---|
GetNodeInfo(GetNodeInfoRequest) |
NodeInfo |
Lifecycle¶
Lifecycle mutations are asynchronous, idempotent operations except SetWake,
which is a last-writer-wins assignment returning the updated Instance.
| RPC | Notes |
|---|---|
CreateInstance(CreateInstanceRequest) |
Takes the spec, an idempotency key, and require_hardware_isolation. |
StartInstance(StartInstanceRequest) |
From CREATED or STOPPED. From STOPPED this is a cold boot. |
StopInstance(StopInstanceRequest) |
grace_seconds before kill. |
PauseInstance(PauseInstanceRequest) |
keep_memory (unset means true), require_memory. |
ResumeInstance(ResumeInstanceRequest) |
Target is instance_id (latest snapshot) or snapshot_id. |
CheckpointInstance(CheckpointInstanceRequest) |
Live snapshot. Requires live_checkpoint. |
CreateSnapshot(CreateSnapshotRequest) |
Retained, optionally named. Declares froze_workload when the source was running. |
DestroyInstance(DestroyInstanceRequest) |
keep_snapshots. |
SetWake(SetWakeRequest) |
Absolute wake timestamp; unset clears the alarm. |
Introspection¶
| RPC | Returns |
|---|---|
GetInstance(GetInstanceRequest) |
Instance |
ListInstances(ListInstancesRequest) |
Filter by states and label_selector; returns bounded pages of at most 256 rows and below the server's encoded response budget. Follow next_page_token for a complete inventory. |
ListSnapshots(ListSnapshotsRequest) |
Empty instance_id lists the whole node. |
DeleteSnapshot(DeleteSnapshotRequest) |
Returns an Operation. |
GetOperation(GetOperationRequest) |
Operation |
CancelOperation(CancelOperationRequest) |
Calls an in-flight operation off and returns it CANCELED. Takes op_id and a human-readable reason; no idempotency key, because it creates no operation. |
WatchEvents(WatchEventsRequest) |
stream Event |
ListInstancesRequest.page_size selects the requested row bound. Zero uses the
server default; values above 256 are refused. page_token is opaque and bound to
the filter that produced it. Reusing it with different states or labels is
refused. ListInstancesResponse.next_page_token is empty only after the filtered
inventory is exhausted. Servers may return fewer rows than requested to keep the
encoded response below the transport limit. The CLI's ls and doctor commands
consume every page automatically.
CancelOperation records a cancellation; it does not interrupt work already
under way. A substrate call in flight runs to completion, and its side effect
may land after the cancellation is recorded. What the cancellation buys is that
the operation's reported outcome is final: the executor's finalization cannot
overwrite the answer you were given, so it stays CANCELED with its reason.
The instance still settles where the work left it. Cancelling moves no
instance itself, but the finalization behind it runs after the work, so the
state it records was measured on the substrate rather than inferred from the verb
— a cancelled stop whose stop succeeded leaves the instance STOPPED, and one
whose stop failed leaves it FAILED. An instance is not left stranded in the
transitional state its submission wrote (STOPPING, PAUSING, RESUMING, …).
The reason is recorded on the operation and on the OPERATION_PROGRESS event, not
in Operation.error, which stays unset for a cancellation. An operation this node
does not know is NOT_FOUND; one that has already settled is
FAILED_PRECONDITION, and the refusal leaves its recorded outcome untouched —
read it back with GetOperation.
Guest passthrough¶
| RPC | Notes |
|---|---|
Exec(stream ExecFrame) → stream ExecFrame |
Bidirectional: stdio, resize, exit status. One stream per exec. |
ReadFile(ReadFileRequest) → stream FileChunk |
offset, limit (0 = to EOF). |
WriteFile(stream WriteFileRequest) → WriteFileResponse |
First frame is WriteOpen. |
Passthrough calls count as user activity and reset the TTL.
Messages¶
InstanceSpec¶
Immutable after create.
message InstanceSpec {
string instance_id = 1; // client-chosen ULID, unique per node
TemplateRef template = 2;
Resources resources = 3; // vcpu, mem_mib, disk_mib
Process process = 4; // start_cmd, ready_cmd, env, workdir
Hooks hooks = 5; // pre_snapshot_cmd, post_restore_cmd + timeouts
uint64 ttl_seconds = 6; // 0 = no TTL; reset on activity
TtlAction ttl_action = 7; // PAUSE (default) | STOP | DESTROY
map<string, string> labels = 8;
EgressPolicy egress = 9;
optional TtlAction idle_action = 10; // absent = ignore workload idle hints
}
idle_action opts the instance into workload idle hints (see the Guest Agent
API's WorkloadService). When present, an idle declaration triggers that action
with the same capability-degradation semantics as ttl_action; absent, idle
declarations have no effect.
TemplateRef¶
message TemplateRef {
OciImageRef oci = 1;
string runtime_bundle_ref = 3; // pinned; must match exactly on resume
string template_hash = 4;
string arch = 5; // aarch64 | x86_64
}
message OciImageRef {
string image = 1; // human-readable label; not identity
string digest = 2; // sha256:… — required
}
An empty digest is rejected with INVALID_SPEC.
Instance¶
message Instance {
InstanceSpec spec = 1;
InstanceState state = 2;
bool ready = 3; // ready_cmd result — not a state
string runtime = 4;
google.protobuf.Timestamp created_at = 5;
google.protobuf.Timestamp updated_at = 6;
google.protobuf.Timestamp ttl_deadline = 7; // absent when no TTL
string latest_snapshot_id = 8;
google.protobuf.Timestamp wake_at = 9; // absent when no alarm
StopReason stop_reason = 10; // absent unless STOPPED
InstanceNetwork network = 11; // absent unless RUNNING & dialable
}
message InstanceNetwork {
string address = 1; // published workload endpoint, host:port
}
network carries the host:port at which the instance's workload is dialable
from wherever the operator declared the node reachable
(--ingress-advertise): the host is that declaration, the port a listener the
node allocated from --ingress-ports and forwards to the workload through the
substrate's ingress. It is resolved from the substrate at read time — never
cached — present only while the instance is RUNNING on a node that publishes
workloads, and stable across pause/resume for the instance's lifetime. It is
absent for a paused or stopped instance, on a node with no ingress advertise
configured, on a runtime that publishes nothing (fake), and when the
substrate could not be asked. A guest-internal sandbox address is never
reported in its place. The workload learns the port to bind as $PORT unless
its spec set one.
Snapshot¶
message Snapshot {
string snapshot_id = 1;
string instance_id = 2;
SnapshotKind kind = 3; // MEMORY_AND_DISK | DISK_ONLY
string cpu_class = 4; // restore-compat key
string template_hash = 5; // invalidation key
string runtime_bundle_ref = 6; // must match exactly on resume
SnapshotTier tier = 7; // LOCAL today; OBJECT_STORE is reserved
uint64 size_bytes = 8;
google.protobuf.Timestamp created_at = 9;
HookOutcome pre_snapshot_hook = 10;
string name = 11; // set for named snapshots
}
Operation¶
message Operation {
string op_id = 1;
string kind = 2; // "create" | "start" | "pause" | …
string instance_id = 3;
OperationState state = 4; // QUEUED | RUNNING | AWAITING_INPUT | DONE | FAILED | CANCELED
string current_step = 5;
ErrorDetail error = 6; // set when FAILED — never for CANCELED
string degraded = 7; // set when it succeeded through a downgrade
google.protobuf.Timestamp created_at = 8;
google.protobuf.Timestamp finished_at = 9;
}
degraded is never empty for a downgraded success, and a downgrade always also
emits a DEGRADATION event.
NodeInfo¶
message NodeInfo {
string node_id = 1;
string arch = 2;
string cpu_class = 3; // snapshot restore-compat key
repeated RuntimeInfo runtimes = 4;
Resources total_resources = 5;
Resources allocatable_resources = 6;
string agent_version = 7;
FleetInfo fleet = 8; // bucket configured, leases held
}
message RuntimeCapabilities {
bool memory_snapshot = 1;
bool disk_snapshot = 2;
bool live_checkpoint = 3;
bool guest_agent = 4;
bool hardware_isolation = 5;
bool lazy_restore = 6;
bool cow_fork = 7;
bool egress_control = 8;
}
RuntimeInfo.health is an enum, not a bool: HEALTHY, UNREACHABLE, or
UNSPECIFIED for an agent too old to report it. UNREACHABLE constrains
mutations only — running sessions keep running and keep being reported as
running.
Event¶
message Event {
uint64 cursor = 1; // monotonic per node
EventType type = 2;
string instance_id = 3;
string op_id = 4;
InstanceState state = 5; // for STATE_CHANGED
string message = 6;
google.protobuf.Timestamp at = 7;
}
WatchEventsRequest.from_cursor of 0 means new events only. A non-zero cursor
replays everything after it, or fails with CURSOR_TOO_OLD if retention has
already deleted those events.
Enums¶
InstanceState — CREATING, CREATED, STARTING, RUNNING,
CHECKPOINTING, PAUSING, PAUSED, RESUMING, STOPPING, STOPPED,
DESTROYING, DESTROYED, FAILED.
OperationState — QUEUED, RUNNING, AWAITING_INPUT, DONE, FAILED,
CANCELED. AWAITING_INPUT is still in flight — a paused operation holds its
session and a restart resolves it. CANCELED is terminal, carries no error, and
is reached through CancelOperation; it means this operation's result will not be
used, not that the work was stopped. See
Lifecycle and operations.
SnapshotKind — MEMORY_AND_DISK, DISK_ONLY.
SnapshotTier — LOCAL, OBJECT_STORE.
TtlAction — PAUSE (the default when unspecified), STOP, DESTROY.
EventType — STATE_CHANGED, OPERATION_PROGRESS, READY_CHANGED,
TTL_WARNING, DEGRADATION, WAKE_FIRED, RESTORED, FENCED, IDLE_FIRED.
SubstrateHealth — HEALTHY, UNREACHABLE, UNSPECIFIED.
ErrorReason — see Errors.
Versioning¶
The package is barista.node.v1alpha1. Breaking changes are gated by buf
breaking: fields are reserved rather than reused, and a break requires explicit
ratification. Hand-written duplicates of contract types are not supported —
generate your client from the schema.