Skip to content

Guest Agent API

barista.guest.v1alpha1.GuestAgent is the contract between a node and the daemon inside its sandboxes. It is internal: the Node Agent is its only client, and it is never exposed to you directly.

It is documented because its semantics are visible through the Node Agent API — readiness, hook outcomes, restore drift — and because knowing what it does tells you what your image has to cooperate with.

Service

service GuestAgent {
  rpc Health(HealthRequest) returns (HealthResponse);
  rpc Exec(stream ExecFrame) returns (stream ExecFrame);
  rpc ReadFile(ReadFileRequest) returns (stream FileChunk);
  rpc WriteFile(stream WriteFileRequest) returns (WriteFileResponse);
  rpc StatPath(StatPathRequest) returns (StatPathResponse);
  rpc RunHook(RunHookRequest) returns (RunHookResponse);
  rpc RunRestoreDuties(RestoreDutiesRequest) returns (RestoreDutiesResponse);
}

Transport and bootstrap

The agent dials the host and authenticates with a per-session token carried in gRPC metadata (barista-instance-token). It never accepts inbound connections.

Runtime Status Injection and channel
hypeman Implemented Guest binary and credential volume at sandbox create; outbound runtime-provided channel.
fake Implemented for tooling Entrypoint wrapper and outbound Docker bridge.
runsc Deferred The transport shape is reserved for the rank-2 tier; no backend is implemented.

The token is a credential with a lifecycle: its volume is created with the session, tagged to the owning node, and reaped when the session goes away — including when the session never made it into the journal.

Health and readiness

message HealthResponse {
  bool alive = 1;
  bool ready = 2;                    // last ready_cmd verdict
  int32 ready_cmd_exit = 3;
  google.protobuf.Timestamp last_user_activity = 4;
  google.protobuf.Timestamp guest_time = 5;   // for clock-drift metrics
}

last_user_activity is the guest's own activity clock, which is what TTL decisions are made against.

Hooks

message RunHookRequest {
  HookKind kind = 1;                 // PRE_SNAPSHOT | POST_RESTORE
  uint32 timeout_ms = 2;
}

message RunHookResponse {
  bool ran = 1;                      // false when no hook is configured
  bool timed_out = 2;
  int32 exit_code = 3;
  string stdout_tail = 4;
  string stderr_tail = 5;
}

The outcome of PRE_SNAPSHOT is recorded on the Snapshot record, so you can tell after the fact whether a snapshot was taken over a quiesced workload or a timed-out one.

Restore duties

This is a separate RPC from RunHook for a specific reason: RunHook runs your commands and cannot carry host-supplied material.

message RestoreDutiesRequest {
  bytes entropy = 1;                          // fresh host CSPRNG bytes — required
  google.protobuf.Timestamp host_time = 2;    // step the guest clock to this
}

message RestoreDutiesResponse {
  uint32 entropy_bytes_mixed = 1;
  bool entropy_credited = 2;                  // credited, or only mixed
  int64 clock_drift_ms = 3;                   // guest minus host, before the step
  bool clock_stepped = 4;
  string degraded = 5;                        // empty when every duty ran as intended
}

Ordering is normative: duties run before POST_RESTORE, so your reconnect command already sees fresh entropy and a stepped clock.

entropy is required. A reseed with nothing to mix cannot de-duplicate two restores of one snapshot, so the agent rejects the request rather than reporting success. Reseeding forces a CRNG reseed as well as mixing, because a restored guest's CRNG key and reseed timer come back byte-identical — mixing alone leaves the first draws repeatable.

entropy_credited and clock_stepped are reported separately from degraded so a sandbox that lacks the capability to credit entropy or set the clock says exactly which duty it could not perform.