Commands and operations
How exec runs a command in a Box (argv without a shell, no stdin or TTY, your own guest account, sudo and API keys), its results and limits, the grant and running Box it needs, generations, operation states, who can read an operation, idempotency keys and running several commands at once.
Exec runs one command in a running Box and returns its exit code and output once the command ends. Each exec, snapshot and restore request creates an operation: a durable record of the request that you read until it finishes. For step-by-step instructions, see Run commands. For an interactive shell or a long session, use SSH.
A command is an argv, not a shell line
argvis an array. Its first item is the program and the rest are its arguments. Cubicle runs it directly, without a shell.- Pipes, redirects,
&&, globs and$VARIABLESneed a shell:["sh", "-c", "ls /var/log | wc -l"]. - The command's standard input is
/dev/null. It has no TTY and runs in a new session, so programs that prompt for input read end-of-file. - The exec ends when the command has exited and its standard output and error are both closed. A
background process that keeps either of them open holds the exec until
timeoutMs, when the command is killed and the result hastimedOut: trueand exit code124. - Exec is for commands that finish. Do not rely on a process you start in the background to keep running after the exec returns.
- Output arrives only when the command ends. Streaming output and a file upload or download API are not available yet; see Move files into and out of a Box.
The command runs as your guest account
-
An exec runs as the guest account of whoever sent it, person or service account. SSH logins use the same account.
-
The working directory is
cwdwhen you set it, or the account's home directory. -
Cubicle starts the command with only these environment variables; your local environment is not passed on. Profile files such as
~/.profileare not read; use["bash", "-lc", "…"]for a login shell's environment.Variable Value HOMEThe account's home directory USER,LOGNAMEThe account name SHELL/bin/bashPATH/usr/local/bin:/usr/bin:/binLANGC.UTF-8 -
Exec has no option to set other environment variables. Run commands shows how to pass values to a command.
Root and sudo
Once you have added an SSH key to your account, your guest account has passwordless sudo, so a
command can run as root: ["sudo", "id", "-u"] prints 0.
Commands sent with an API key cannot rely on sudo
- A guest account is locked and expired in the Box unless its owner holds a grant on the Box and has added an SSH key. Service accounts cannot add SSH keys, so every service account's guest account is locked and expired.
- Exec still runs commands as a locked account.
sudochecks whether the account has expired, though, so do not rely onsudofrom it. A program that uses an API key may not be able to install system packages, for example withapt-get.
Instead, install software that does not need root into the home directory and run it by its full
path, because exec's PATH does not include directories such as ~/.local/bin. For system
packages, have a person install them over SSH, take a snapshot of that Box and create the
program's Boxes from the snapshot; see Build a template Box.
Limits
Each exec has a time limit, timeoutMs, and an output limit, maxOutputBytes, which stdout and
stderr share. Set a timeout and an output limit
shows how to set them, and Limits and defaults lists their values.
Results
A finished exec has a result with exitCode, stdout, stderr, timedOut and truncated.
The SDK's ExecResult adds the operationId.
Read the result explains each field and the exit codes
that come from Cubicle rather than from your program.
- A non-zero exit code is a result, not an error: the operation is still
done. - Check
timedOut, not exit code124, to detect a timeout: the command itself can exit with124.
const slow = await box.exec(["sleep", "5"], { timeoutMs: 1000 })
// => { exitCode: 124, stdout: "", stderr: "", timedOut: true, truncated: false, operationId: "…" }Exec needs a grant and a running Box
- Exec needs an explicit grant on the Box, which its creator gets.
The organization's owner or admin role is not enough: without a grant the request returns
403 forbidden,an explicit box grant is required. - The Box's desired state must be
running, or the request returns409 conflict,box is not running. The Box must also actually be running when the command reaches its Node, or the exec fails, so wait forssh.ready, or use the SDK'swake: true, which starts the Box when it is not meant to be running and waits until it is ready.
An exec is bound to a generation
An exec records the Box's desired.generation when it is accepted (see
generations).
- Before the command starts, a start, stop or hibernation moves the Box to a new generation,
and the exec fails with
box is not running at the requested generation. After a deletion it fails withbox deletedorbox no longer exists. It fails withrequester is not authorized on this boxwhen its sender has lost the grant or membership in the meantime. - While the command runs, a stop, hibernation (including automatic hibernation), deletion or
host restart interrupts it, and the exec ends
unknown. A start on a Box that is already running does not interrupt it.
Operations record every request
Exec, snapshot and restore requests return 202 with an operation. Read it with
GET /v1/operations/{operation} (reference), or let
the SDK wait for it. Its key fields:
| Field | Meaning |
|---|---|
id, boxId | The operation's ID and its Box's ID. |
type | exec, snapshot or restore. |
state | Where the operation is; see the table below. |
result | For a done exec, its result. For snapshots and restores, reserved; do not read it. |
error | Why the operation failed or is unknown, otherwise null. |
createdAt, startedAt, completedAt | ISO 8601 timestamps; the last two are null until they happen. |
The SDK's Operation type lists every field.
| State | Meaning |
|---|---|
pending | Accepted, and waiting for the Box's Node to pick it up. |
running | The Node is carrying it out. |
done | Finished. A done exec has its result, whatever its exit code. |
failed | Ended with the reason in error, for example box is not running at the requested generation. For an exec, this is about running the command, not its exit code. |
unknown | Exec only. The Node lost track of the command: its service restarted while the command ran, or a lifecycle change interrupted it. The command may or may not have run, in whole or in part. |
done,failedandunknownare final.- An
unknownexec is never run again. Cubicle cannot tell whether the command ran, so it leaves the decision to you. Run it again only if repeating it is safe. - The SDK rejects with a
CubicleOperationErrorwhen an operation endsfailed(codeoperation_failed) orunknown(codeoperation_unknown). The CLI printsexec failed:orexec unknown:and exits with status 1.
Who can read an operation
- Any active member of the Box's organization can read its operations, without a grant:
GET /v1/operations/{operation}for one, orGET /v1/boxes/{box}/operations(reference) for the Box's 50 most recent, newest first. The Dashboard's Operations tab shows the same list. - An exec's
result, with its output, is visible only to the account that sent it. Everyone else seesresult: null. - An operation does not include the command's
argv. - An API key sees only operations in its own organization. Others return
404. - You can still read an operation by ID after its Box is deleted.
Idempotency keys make retries safe
Send an Idempotency-Key header with an exec: when the response is lost and you retry with the
same key and body, you get the original operation back and the command does not run twice. The
SDK sends a new key with every exec call and reuses it when it retries that request itself; the
CLI's exec sends none, so running it again runs the command again.
Idempotency keys has the full rules, and
Retry safely with idempotency keys
shows each surface.
Several commands can run at once
- Several execs on one Box can run at the same time.
- Each Node carries out a limited number of operations at once. Others wait as
pending. - An exec does not block start, stop, hibernate or delete. Stop, hibernate and delete interrupt it; a start on the running Box does not, and its Node applies that start once the exec has finished.
- A snapshot or restore has the Box to itself: start, stop, hibernate and delete return
409 conflictwhile one is pending or running. See Snapshots.
Related
Run commands
Run commands from the SDK, the CLI and the API.
Box lifecycle
States, generations and readiness.
Organizations and access
Grants, roles and service accounts.
Errors and retries
How the SDK reports failed and unknown operations.
Limits and defaults
Every size, timeout and rate limit in one place.
SSH into a Box
Interactive shells and editors over SSH.
Box lifecycle
Box states (pending, provisioning, starting, running, stopping, stopped, hibernating, hibernated, deleting, failed, unknown), the requests that move a Box between them, auto-hibernation 60 minutes after each start, what stop, hibernate and delete keep, restartRequired after host restarts, failures, deletion, desired vs observed state and generations, and ssh.ready with what each ssh.reason means and what to do.
Snapshots
Cubicle snapshots are offline, disk-only copies of a stopped Box stored on its Node, used to restore the Box in place or create new Boxes, with rules for status, sizing, organization snapshots, hibernation and retention.

