Types
All types in this file mirror the createos-sandbox control-plane JSON HTTP API. Fields use the
server's snake_case names so JSON parses without translation. When the server uses omitempty on
a field, the TypeScript field is marked optional (?): the key is absent from the wire payload
rather than sent as null. List endpoints return a doubly-nested pagination envelope
{ data: { data: [...], pagination: { total, limit, offset, count } } }; rootfs is the lone
exception (a plain view). The server clamps limit to 500; drive paging from the reported
total, not the requested page size. Every type here is re-exported via export * from "./types.js"
in index.ts, making the entire surface public.
See also: Client · Sandbox · Sub-APIs
At a glance
- Package:
@nodeops-createos/sandbox(npm) - Import:
import { createClient } from "@nodeops-createos/sandbox" - Base URL:
https://api.sb.createos.sh(override withCREATEOS_SANDBOX_BASE_URL) - Auth: API key via the
apiKeyoption orCREATEOS_SANDBOX_API_KEY
JSend envelope
The control plane wraps every response in one of three JSend shapes. The SDK
unwraps these transparently; consumers only see them when using the raw
CreateosSandboxHttp transport directly.
export interface SuccessEnvelope<T> {
status: "success";
data: T;
}
export interface FailEnvelope {
status: "fail";
/** Field-keyed object or plain string depending on the endpoint. */
data: Record<string, unknown> | string;
}
export interface ErrorEnvelope {
status: "error";
message: string;
code: number;
}
export type JSendEnvelope<T> =
| SuccessEnvelope<T>
| FailEnvelope
| ErrorEnvelope;Client & request options
RetryOptions
Exponential-backoff retry policy. Omit a field to keep its default.
export interface RetryOptions {
maxRetries?: number; // Extra attempts after the first. Default 2 (3 total).
baseDelayMs?: number; // Base backoff delay in ms. Default 500.
maxDelayMs?: number; // Backoff ceiling in ms. Default 30000.
}RetryReason
export type RetryReason = "network" | "status" | "rate-limit";RequestHookContext
Delivered to ClientHooks.onRequest. All credentials are pre-redacted.
| Field | Type | Notes |
|---|---|---|
url | string | URL with userinfo stripped and sensitive query params redacted |
method | string | Uppercase HTTP method |
headers | Record<string, string> | Credentials replaced by "redacted" |
attempt | number | 1 for first try, 2+ for retries |
ResponseHookContext
Extends RequestHookContext.
| Field | Type | Notes |
|---|---|---|
status | number | HTTP status code |
durationMs | number | Elapsed time for the fetch call, in ms |
requestId | string | undefined | Server-supplied request id, when present |
RetryHookContext
Extends Omit<ResponseHookContext, "status">.
| Field | Type | Notes |
|---|---|---|
status | number | undefined | HTTP status, or undefined for network errors |
reason | RetryReason | Why the SDK is retrying |
delayMs | number | Sleep time before next attempt |
ClientHooks
Optional lifecycle hooks for zero-dep observability. Every payload is pre-redacted. Throws inside a hook are swallowed. Hooks are awaited in the request path; keep hook work cheap or dispatch slow work without returning the promise.
export interface ClientHooks {
onRequest?: (ctx: RequestHookContext) => void | Promise<void>;
onResponse?: (ctx: ResponseHookContext) => void | Promise<void>;
onRetry?: (ctx: RetryHookContext) => void | Promise<void>;
}CreateosSandboxClientOptions
Construction options for CreateosSandboxClient. All fields are optional.
| Field | Type | Notes |
|---|---|---|
apiKey | string? | Sent as X-Api-Key. Falls back to CREATEOS_SANDBOX_API_KEY env var |
authHeaders | HeadersInit? | Auth headers used instead of an API key |
baseUrl | string? | Control-plane base URL. Falls back to CREATEOS_SANDBOX_BASE_URL, then https://api.sb.createos.sh |
fetch | typeof fetch? | Custom fetch implementation (useful in tests) |
timeoutMs | number? | Per-request timeout in ms. Default 60000. 0 disables |
retry | RetryOptions | false? | Retry policy, or false to disable retries entirely |
userAgent | string? | Overrides the User-Agent header |
hooks | ClientHooks? | Lifecycle hooks for observability |
RequestOptions
Per-call overrides accepted by every SDK method.
| Field | Type | Notes |
|---|---|---|
signal | AbortSignal? | Cancel the request and any in-flight retry backoff |
headers | HeadersInit? | Merged into this request, overriding client defaults |
timeoutMs | number? | Per-request timeout in ms, overrides client default |
retry | RetryOptions | false? | Retry policy for this request, overrides client default |
ExecOptions
export type ExecOptions = RequestOptions;Alias for RequestOptions; passed to Sandbox.runCommand and Sandbox.streamCommand.
Catalog & identity
Shape
A sandbox sizing preset. Returned by listShapes().
| Field | Type | Notes |
|---|---|---|
id | string | Shape id passed as CreateSandboxRequest.shape, e.g. s-1vcpu-256mb |
vcpu | number | Virtual CPU count |
mem_mib | number | Memory in MiB |
default_disk_mib | number | Default overlay disk size when the create request omits disk_mib |
cpu_quota_pct | number? | cgroup v2 cpu.max quota as a percent of one CPU; absent = unlimited |
RootfsEntry
Metadata for one built-in rootfs image in the catalog.
| Field | Type | Notes |
|---|---|---|
name | string | Catalog name |
description | string? | Human-readable description |
deprecated | boolean? | True when this image is being retired |
successor | string? | Recommended replacement when this image is deprecated |
RootfsData
export interface RootfsData {
/** Available rootfs catalog names usable as `CreateSandboxRequest.rootfs`. */
rootfs: string[];
/** Name used when a create request omits `rootfs`. */
default: string;
/** Rich per-rootfs metadata; absent when the catalog is empty. */
entries?: RootfsEntry[];
}Response from listRootfs(). entries is absent when the catalog is empty. This endpoint returns
a plain view, not the paginated envelope used by all other list routes.
HostStatus
export type HostStatus = "active" | "draining" | "dead";Scheduling state of a worker host.
HostPublic
A worker host visible to the caller. Returned by listHosts().
| Field | Type | Notes |
|---|---|---|
id | string | |
status | HostStatus | |
free_mib | number | Schedulable memory currently free, in MiB |
vm_count | number | Sandboxes currently placed on the host |
rootfses | string[]? | Rootfs images cached on the host; absent (omitempty) when none |
WhoAmIStatsView
Per-state sandbox counts for the calling identity.
| Field | Type | Notes |
|---|---|---|
running | number | Sandboxes currently running |
paused | number | Sandboxes currently paused |
other | number | Sandboxes in any other state |
total | number | Total non-destroyed sandboxes |
WhoAmIView
Identity behind the configured API key. Returned by whoami().
| Field | Type | Notes |
|---|---|---|
user_id | string | Stable id of the authenticated user |
stats | WhoAmIStatsView | Sandbox counts grouped by lifecycle state |
Sandbox
NetworkEntry
References an overlay network by id in CreateSandboxRequest.networks.
export interface NetworkEntry {
id: string;
}CreateSandboxRequest
Body of POST /v1/sandboxes. Only shape is required.
Note:
bandwidth_quota_bytesis not settable at create time; the server returns400for any non-zero value. Grow the quota post-create withSandbox.rechargeBandwidth().
node_selectorandingress_*fields are not described in the published OpenAPI spec; verify against the live API.
| Field | Type | Notes |
|---|---|---|
shape | string | Required. A shape id from listShapes() |
rootfs | string? | Rootfs catalog name or template id/name; empty = host default |
name | string? | User-facing VM name, unique per user; empty = auto-generated |
networks | NetworkEntry[]? | Overlay networks to join at create time |
disk_mib | number? | Overlay disk size in MiB; 0 = shape default |
egress | string[]? | Egress allowlist; empty / ["*"] = allow all |
envs | Record<string, string>? | Env vars injected into every command inside the VM |
ssh_pubkeys | string[]? | OpenSSH public keys authorized for the SSH gateway |
host_id | string? | Pin placement to a specific host id; empty = scheduler picks |
node_selector | Record<string, string>? | Scheduler placement labels (k8s NodeSelector semantics) |
ingress_enabled | boolean? | Opt the sandbox into HTTP ingress at create time |
disks | DiskAttachment[]? | Disks to mount into the VM at boot |
region | string? | Pin to a region; must equal the server's region (no cross-region routing) |
auto_pause_after_seconds | number? | Idle auto-pause timeout in seconds (60-86400) |
start_paused | boolean? | If true, the sandbox boots into paused state |
CreateSandboxResponse
Result of POST /v1/sandboxes, returned before the SDK fetches the full SandboxView.
Records the resolved placement and boot timing.
| Field | Type | Notes |
|---|---|---|
id | string | |
name | string | |
ip | string | The VM's private IP |
shape | string | |
rootfs | string | |
vcpu | number | |
mem_mib | number | Memory in MiB |
disk_mib | number | Overlay disk size in MiB |
spawn_ms | number | Wall-clock time to boot the VM, in ms |
egress | string[] | Resolved egress allowlist |
bandwidth_quota_bytes | number | Transferable byte quota; -1 = unmetered |
ingress_url_template | string? | Ingress URL template with literal <port> placeholder; set when ingress is on |
SandboxStatus
Lifecycle state of a sandbox. Transitional states (pausing, resuming, forking, creating,
destroying) settle into a steady or terminal one.
export type SandboxStatus =
| "creating"
| "running"
| "pausing"
| "paused"
| "resuming"
| "forking"
| "error"
| "destroying"
| "destroyed"
| "failed";SandboxView
Full server-side projection of a sandbox. Backs the Sandbox handle and is returned by get/list
endpoints. Optional fields are omitted by the server (omitempty) rather than sent as null.
Wire drift:
ingress_url_templateis present on the SDKSandboxViewbut absent from the published OpenAPISandboxViewschema. Verify against the live API.
| Field | Type | Notes |
|---|---|---|
id | string | |
status | SandboxStatus | |
ip | string? | Absent until the VM is assigned an address (omitted while creating) |
vcpu | number | |
mem_mib | number | Memory in MiB |
disk_mib | number | Overlay disk size in MiB |
created_at | string | RFC 3339 timestamp |
ingress_enabled | boolean | |
ingress_url_template | string? | Ingress URL template with <port> placeholder; set when ingress is on |
name | string? | |
running_at | string? | RFC 3339 timestamp of when the VM last reached running |
destroyed_at | string? | RFC 3339 timestamp of when the VM was destroyed |
spawn_ms | number? | Wall-clock boot time in ms |
shape | string? | |
rootfs | string? | |
region | string? | |
egress | string[]? | |
envs | string[]? | Names of env vars stored on the sandbox; values are never returned |
ssh_pubkeys | string[]? | |
created_by | string? | Identity that created the sandbox |
bandwidth_ingress_bytes | number? | Inbound bytes observed (never enforced) |
paused_at | string? | RFC 3339 timestamp of the last pause |
last_resumed_at | string? | RFC 3339 timestamp of the last resume |
forked_from | string? | Source sandbox id when created via fork |
auto_pause_after_seconds | number? | Idle auto-pause timeout in seconds; absent when auto-pause is disabled |
ListSandboxesOptions
Extends RequestOptions.
| Field | Type | Notes |
|---|---|---|
limit | number? | Cap the number of handles returned; omit to fetch every page |
status | "running" | "creating" | "destroyed" | "failed"? | SDK filter type. The REST API accepts all sandbox lifecycle states; SDK versions with this narrower union require an update or a direct REST call to filter by paused, for example. |
ForkSandboxRequest
Optional overrides applied to a fork. Omitted fields inherit from the source.
| Field | Type | Notes |
|---|---|---|
start_paused | boolean? | Keep the fork in paused instead of auto-resuming |
ssh_pubkeys | string[]? | |
egress | string[]? | |
ingress_enabled | boolean? | |
envs | Record<string, string>? | |
bandwidth_quota_bytes | number? | Present in some SDK versions but rejected by the server. Omit it; recharge after the fork is running. |
PatchSandboxRequest
Body of the sandbox PATCH endpoint, used by Sandbox.setIngress and Sandbox.setAutoPause.
Omitted fields are left unchanged.
| Field | Type | Notes |
|---|---|---|
ingress_enabled | boolean? | |
auto_pause_after_seconds | number? | Idle auto-pause timeout in seconds (60-86400) |
disable_auto_pause | boolean? | When true, clears the auto-pause timeout; required because omitting auto_pause_after_seconds means "leave unchanged" |
AddSSHPubkeysRequest
Body of Sandbox.addSSHPubkeys: keys to add to a live sandbox.
export interface AddSSHPubkeysRequest {
/** OpenSSH-formatted public keys. Keys already present are de-duplicated. */
keys: string[];
}AddSSHPubkeysResponse
export interface AddSSHPubkeysResponse {
/** Total `ssh_pubkeys` on the sandbox after the add. */
count: number;
}DestroyedResponse
export interface DestroyedResponse {
/** Id of the sandbox accepted for destruction. */
id: string;
/** Status reached by the destroy call. `destroying` for an async reclaim;
* `destroyed` when the call was a no-op on an already terminal row or
* could be reclaimed inline (paused/error). */
status: Extract<SandboxStatus, "destroying" | "destroyed">;
}SetEgressRequest
export interface SetEgressRequest {
/** `host:port` allow rules. `null`, omitted, or `[]` means allow all. */
egress?: string[] | null;
}EgressView
The sandbox's current egress allowlist. Returned by getEgress / setEgress.
export interface EgressView {
id: string;
/** Active `host:port` allow rules. Empty = allow all. */
egress: string[];
}BandwidthView
Bandwidth quota and usage counters. Returned by getBandwidth / rechargeBandwidth.
| Field | Type | Notes |
|---|---|---|
id | string | |
quota_bytes | number | Total transferable byte quota; -1 = unmetered |
used_bytes | number | Egress bytes, billed against the quota |
ingress_bytes | number | Inbound bytes, observed, never enforced |
remaining_bytes | number | Bytes left before the VM is network-capped |
capped | boolean | true once the quota is exhausted and egress is blocked |
RechargeBandwidthRequest
export interface RechargeBandwidthRequest {
/** Bytes to add to the quota. */
add_bytes: number;
}ResizeSandboxRequest
export interface ResizeSandboxRequest {
/** New overlay disk size in MiB. Must be larger than the current size. */
disk_mib: number;
}ResizeSandboxResponse
export interface ResizeSandboxResponse {
/** Disk size in MiB after the grow. */
disk_mib: number;
}Command execution
ExecRequest
Wire request body for POST /v1/sandboxes/:id/exec.
| Field | Type | Notes |
|---|---|---|
cmd | string | Executable to run inside the guest. Not run through a shell; wrap in ["bash", "-c", "…"] for pipes, globbing, or redirection |
args | string[]? | Arguments passed to cmd |
stream | boolean? | Stream output as NDJSON frames instead of buffering. Set by streamCommand |
ExecResult
Buffered output of a completed command. Returned by runCommand.
| Field | Type | Notes |
|---|---|---|
stdout | string | Captured standard output |
stderr | string | Captured standard error |
exit_code | number | Process exit code; 0 = success |
error | string? | Agent-level failure (the command could not be started) |
ExecResponse
Result of runCommand: the buffered output plus timing.
export interface ExecResponse {
result: ExecResult;
/** Wall-clock time the command ran, in milliseconds. */
exec_ms: number;
}ExecStreamEvent
Discriminated union yielded by Sandbox.streamCommand. Switch on type to handle each kind of
event; TypeScript narrows the payload.
export type ExecStreamEvent =
| { type: "stdout"; data: string }
| { type: "stderr"; data: string }
| { type: "exit"; exitCode: number }
| { type: "error"; message: string }
| { type: "heartbeat" };ExecStreamFrame
Raw NDJSON frame as emitted by the server. Exposed for advanced users who want to bypass the
ExecStreamEvent projection (e.g. log forwarders that need the snake_case shape).
| Field | Type | Notes |
|---|---|---|
stdout | string? | |
stderr | string? | |
exit_code | number? | |
error | string? | |
hb | boolean? | Heartbeat marker emitted every 5 s |
Handle option types
CreateSandboxOptions
Extends RequestOptions. Passed to CreateosSandboxClient.createSandbox.
| Field | Type | Notes |
|---|---|---|
wait | boolean? | Wait until the sandbox reaches running before resolving; default true |
waitTimeoutMs | number? | Budget for the wait in ms; default 120000 |
WaitOptions
Options for the Sandbox.waitUntil* pollers.
| Field | Type | Notes |
|---|---|---|
timeoutMs | number? | Wait budget in ms; default 120000 |
signal | AbortSignal? | Cancel the wait |
request | RequestOptions? | Per-request options applied to each poll refresh; timeoutMs here is the per-request transport timeout, not the overall wait budget |
Templates
TemplateStatus
export type TemplateStatus = "pending" | "building" | "ready" | "failed";ready once the rootfs image is usable as a sandbox rootfs.
TemplateCreateRequest
Body of templates.create: a Dockerfile to build into a sandbox rootfs.
| Field | Type | Notes |
|---|---|---|
name | string | |
dockerfile | string | Dockerfile source built into the rootfs image |
base | string? | Base rootfs catalog name to build on top of; empty = host default |
TemplateView
A custom rootfs template. Returned by the templates endpoints.
| Field | Type | Notes |
|---|---|---|
id | string | |
name | string | |
base | string | Base rootfs the template was built on |
status | TemplateStatus | |
ext4_size_bytes | number | Size of the built ext4 rootfs image, in bytes |
created_at | string | RFC 3339 |
built_at | string? | RFC 3339; absent until ready |
dockerfile | string? | Present only on detail GET with include: "dockerfile" |
GetTemplateOptions
Extends RequestOptions.
export interface GetTemplateOptions extends RequestOptions {
/** Set to `"dockerfile"` to include the original build source in the response. */
include?: "dockerfile";
}TemplateLogEvent
One NDJSON line from a template build log stream.
| Field | Type | Notes |
|---|---|---|
ts | string? | Timestamp |
level | string? | Log level |
line | string? | Log line text |
attempt | number? | Build attempt number |
final | boolean? | Terminal frame when true |
status | string? | "ready" or "failed" in the terminal frame |
[key] | unknown | Additional arbitrary fields (index signature) |
TemplateLogsOptions
Options for templates.logs / templates.followLogs. Extends RequestOptions.
export interface TemplateLogsOptions extends RequestOptions {
/** Filter to one build attempt. Default = all attempts. */
attempt?: number;
}Disks
DiskKind
export type DiskKind = "s3";Storage backend for a registered disk. Only "s3" today (covers AWS S3, Cloudflare R2, MinIO,
and any S3-compatible endpoint).
DiskConfig
Non-secret S3 disk configuration. Persisted server-side as JSON.
| Field | Type | Notes |
|---|---|---|
bucket | string | |
endpoint | string | S3-compatible endpoint URL |
region | string? | |
use_path_style | boolean? | Force path-style addressing (endpoint/bucket/key) instead of virtual-hosted; needed for MinIO / R2 with custom domains |
DiskCredentials
Bucket credentials. Sent only on create; AES-GCM-encrypted at rest and never returned by any read endpoint.
export interface DiskCredentials {
access_key: string;
secret_key: string;
}DiskCreateRequest
Body of POST /v1/disks.
| Field | Type | Notes |
|---|---|---|
name | string | User-scoped name; must match ^[a-z0-9][a-z0-9-]{0,62}$ |
kind | DiskKind | |
config | DiskConfig | |
credentials | DiskCredentials |
DiskView
User-facing projection of a registered disk. Credentials never appear here.
| Field | Type | Notes |
|---|---|---|
id | string | |
name | string | |
kind | DiskKind | |
config | DiskConfig | Server returns this as a JSON blob; the SDK exposes it as parsed JSON |
created_at | string | RFC 3339 |
DiskMountStatus
The server returns pending, mounted, or failed. Some SDK versions still declare the following incompatible union:
export type DiskMountStatus = "pending" | "mounted" | "error" | "unmounting";Do not check only for error: a failed mount arrives as failed. Until your SDK type includes it, compare the value as a string, for example String(disk.mount_status) === "failed". The server does not emit unmounting as a mount status.
DiskAttachment
One element of CreateSandboxRequest.disks or the body of the attach endpoint.
| Field | Type | Notes |
|---|---|---|
disk_id | string | A disk_<ulid> id or the user-scoped disk name |
mount_path | string | Absolute path inside the guest, e.g. /mnt/data |
sub_path | string? | Bucket sub-folder to expose at mount_path |
SandboxDiskView
Per-attachment projection. Returned from GET /v1/sandboxes/:id/disks.
| Field | Type | Notes |
|---|---|---|
disk_id | string | The disk_<ulid> id of the registered disk |
name | string | |
kind | DiskKind | |
config | DiskConfig | |
mount_path | string | Absolute path inside the guest where the disk is mounted |
sub_path | string? | Bucket sub-folder exposed at mount_path, when set |
mount_status | DiskMountStatus | |
mount_error | string? | Failure detail when the server returns mount_status: "failed" |
AttachDiskOptions
Options for Sandbox.attachDisk.
| Field | Type | Notes |
|---|---|---|
diskId | string | A disk_<ulid> id or the user-scoped disk name |
mountPath | string | Absolute path inside the guest, e.g. /mnt/data |
subPath | string? | Optional bucket sub-folder to expose at mountPath |
Note:
detachDiskrequires thedisk_<ulid>id, not the name.
DetachDiskOptions
Options for Sandbox.detachDisk.
| Field | Type | Notes |
|---|---|---|
diskId | string | A disk_<ulid> id or the user-scoped disk name |
mountPath | string | Absolute path inside the guest where the disk is currently mounted; required because the same disk may be attached at multiple paths and the composite key is (sandbox, disk, mountPath) |
DiskDeletedResponse
export interface DiskDeletedResponse {
deleted: boolean;
}DiskDetachedResponse
export interface DiskDetachedResponse {
detached: boolean;
}Networks
NetworkCreateRequest
export interface NetworkCreateRequest {
name: string;
}NetworkMember
A sandbox attached to an overlay network, with its address on that network.
| Field | Type | Notes |
|---|---|---|
sandbox_id | string | |
status | string | |
ip | string? | The member's IP on this overlay network; absent until the membership is programmed |
name | string? | The member sandbox's user-facing name, when set |
Network
An overlay network. Returned by the networks endpoints.
| Field | Type | Notes |
|---|---|---|
id | string | |
name | string | |
created_at | string | RFC 3339 |
member_count | number? | Number of attached sandboxes; present on list responses |
members | NetworkMember[]? | Attached sandboxes with per-network addresses; present on detail GET |
Misc / probe responses
OKResponse
Generic acknowledgement returned by endpoints with no richer payload.
export interface OKResponse {
ok: boolean;
}HealthzResponse
Liveness probe result. Returned by healthz().
export interface HealthzResponse {
/** True once the control plane process is up. */
up: boolean;
}ReadyzResponse
Readiness probe result. Returned by readyz().
export interface ReadyzResponse {
/** True once the control plane is ready to serve traffic. */
ready: boolean;
/** Why the control plane is not ready, when `ready` is false. */
reason?: string;
/** Milliseconds since the scheduler last completed a healthy pass. */
scheduler_last_ok_ms_ago?: number;
}