The compute service exposes a single gRPC service: compute.v1.ComputeService.
All messages are defined in paper-board/proto/compute/v1/compute.proto. The service
listens on port 50051 (ClusterIP — internal only, no public ingress).
A bidirectional ExecSession RPC for interactive REPL use cases is planned for Phase 5.
It is not part of the v1 surface — message types, lifecycle, and cancellation semantics
will be designed in the Phase 5 brainstorm.
Provisions a gVisor pod and a workspace PVC for a session. Returns immediately with
status: STARTING; the pod becomes READY asynchronously. ExecCommand blocks
internally until the pod is running — callers do not need to poll DescribeSandbox
before calling ExecCommand.
Lazy provisioning. The agents service calls CreateSandbox only on the first
tool_use block in a session. Sessions that complete without any tool call never
create a sandbox and incur no compute cost.
Deletes the sandbox pod and marks the workspace PVC for soft-delete. The PVC is annotated
with compute.paper-board.io/delete-after = now + 24h and physically deleted by the GC
CronJob after the grace window expires.
Server-streaming RPC. Runs a command inside the sandbox pod and streams stdout /
stderr lines as they arrive. The final event is ExecCompleted, which carries the
exit code, wall time, and UsageMetrics.
If the sandbox is still in STARTING state when ExecCommand is called, the server
blocks on WaitForRunning before starting the exec. This means callers can call
ExecCommand immediately after CreateSandbox without polling.
Cancellation. Cancelling the gRPC stream context sends SIGTERM to the in-pod
process. If the process is still running after 10 seconds, SIGKILL is sent.
A final ExecCompleted{ cancelled: true, exit_code: -1 } event is emitted before
the stream closes.
Timeout. Maximum per-call timeout is 600 seconds (10 minutes). Requests with
timeout unset or exceeding 600 s are capped at 600 s server-side.
All workspace I/O paths are validated against WorkspaceSpec.mount_path (default
/workspace):
Paths are cleaned (filepath.Clean) before use; .. segments after cleaning are
rejected with INVALID_ARGUMENT.
Symlinks inside the workspace are followed only if the resolved target is also inside
mount_path; otherwise the operation is rejected with PERMISSION_DENIED.
Per-call data cap: 16 MiB. Clients must chunk larger payloads across multiple calls.