Sandboxes and Computers

A Sandbox is a deployed source declaration; a Computer is a durable resource created from it.

export const repo = sandbox({ id: "repo" })
  .image(image("repo").from("node:24-bookworm-slim"))
  .resources({ cpu: 2, memory: "4GiB" })

The builder requires .image(imageBuilder).resources({ cpu, memory }). Memory is expressed as ${bigint}MiB or ${bigint}GiB. Create externally with client.sandboxes.createComputer(declaredId, { key?, idempotencyKey?, secrets? }); the result is a ComputerRef.

client.computers.ref(id) exposes:

API Result
retrieve() Current Computer lifecycle, residency and secret bindings.
members({ cursor?, limit? }) A page of Sessions, Tasks and Commands attached to the Computer.
exec({ command, idempotencyKey, cwd?, env?, stdin?, timeout? }) A durable CommandRef after admission.
delete({ idempotencyKey? }) Deletion receipt.

Computer status describes resource lifecycle: available, deleting, or deleted. Its separate residency describes execution: cold, starting, running, parking, parked, restoring, or unavailable. An unavailable Computer includes an error with a code and message. A parked Computer can resume; parking is not a resource failure. Starting a Task, Actor or Command requests startup or restoration automatically. Secrets are bound at creation using plain string names; plaintext secret values are not part of a Computer request.

Command output and completion

computer.exec() acknowledges command admission, including while its Computer is starting. Reconnect using client.commands.ref(id) from your application backend.

Command API Result
retrieve() Command status and an outcome when available.
cancel({ signal? }) An accepted cancellation receipt with id, targetId and status.
wait({ waitTimeout?, signal? }) Terminal outcome; a nonzero exit is an ordinary exited outcome.
logs({ cursor?, limit? }, { signal? }) One finite page of retained output and gap records.
streamLogs({ after? }, { signal? }) Retained output followed by new output until the output closes.

Command operations are available through the external client. Actor and Task code uses ordinary child processes for commands on its own Computer, or child Tasks for work on another Computer. The in-Run ComputerRef does not expose exec(); the external client’s ClientComputerRef does. A local child-process promise is not a Helmr managed wait.

Each external observer has its own timeout or abort signal. Observing a Command does not create an Actor or Task.

const command = await computer.exec({
  command: ["npm", "test"],
  idempotencyKey: "test:revision-42",
})

for await (const record of command.streamLogs({}, { signal })) {
  if (record.kind === "output") {
    const destination = record.stream === "stdout" ? process.stdout : process.stderr
    destination.write(record.content)
  } else {
    console.warn(`Missing ${record.stream} chunks: ${record.fromSequence}–${record.throughSequence}`)
  }
  // Persist after processing to reconnect with streamLogs({ after: record.cursor }).
}
const outcome = await command.wait()

Output records contain byte chunks (Uint8Array), not necessarily complete text lines or UTF-8 characters. Each record has an opaque cursor and identifies stdout or stderr. Ordering is preserved within each stream; timestamps do not establish a causal order between stdout and stderr.

Finite pages contain items and a nextCursor when records were returned. Follow that cursor to request another page; an empty page does not mean a running command has finished. Streaming handles temporary log-delivery lag without advancing past undelivered output. The command outcome can be available before logs finish reaching their read store.

Call command.cancel() to request termination. Repeated calls return the same retained operation receipt; the receipt confirms intent, not process exit. Use retrieve() or wait() for the outcome. Pending work that has not been placed is cancelled without starting. Placed work retains its execution identity until the worker confirms cleanup; processReconciled reports that separate fact. Commands are independent of Actor and Task lifecycles; cancelling a Run does not cancel a Command on the same Computer.

Breaking iteration or aborting observation does not stop the command. Log records have bounded retention. Missing ranges are explicit gap records; when complete history or a failed producer’s final output cannot be established, observation reports an error instead of a successful empty history. Complete-history reads are unavailable once the Command’s creation time crosses the 90-day retention boundary, even if some newer chunks have not yet expired from storage.