Skip to documentation
API + guides

01Documentation

Observability

Sandbox0 provides runtime metrics, historical logs, and a signed audit ledger for each sandbox. These APIs read historical data without waking a paused sandbox.

DataEndpointUse it for
Runtime metricsGET /api/v1/sandboxes/{id}/metricsChart-ready CPU, memory, network, process, and writable-rootfs series
Metric catalogGET /api/v1/sandboxes/{id}/metrics/catalogDiscover supported metric names, kinds, units, and dimensions
Historical logsGET /api/v1/sandboxes/{id}/observability/logsQuery or watch retained stdout, stderr, and PTY output
Audit eventsGET /api/v1/sandboxes/{id}/observability/eventsQuery or watch canonical signed audit facts

Audit ingestion and the audit event API require the enterprise sandbox_audit feature and the sandboxaudit:read permission. Historical logs, runtime metrics, and the metric catalog do not require that enterprise feature.

Per-sandbox historical data is separate from platform telemetry and the metering usage ledger.

Runtime Metrics#

GET

/api/v1/sandboxes/{id}/metrics

The metrics endpoint returns bounded, server-downsampled series ready for charts. The default query covers the last hour and requests CPU utilization, memory utilization, and network I/O. A query cannot span more than 30 days.

Query Parameters#

ParameterTypeDescription
start_timeRFC3339 timestampStart of the window; defaults to one hour before end_time
end_timeRFC3339 timestampEnd of the window; defaults to the current time
metricscomma-separated stringCanonical metric names from the catalog
step_secondsintegerRequested bucket width; the server uses at least 15 seconds and may increase it to honor max_points
statisticstringauto, average, minimum, maximum, last, or rate; auto averages gauges and rates counters
max_pointsintegerMaximum points per series (default: 240, max: 1000)

Metric Catalog#

GET

/api/v1/sandboxes/{id}/metrics/catalog

MetricKindUnitMeaning
sandbox.cpu.utilizationgaugeratioCPU usage divided by the configured CPU limit
sandbox.cpu.usagegaugecoresCPU cores currently used
sandbox.cpu.timecountersecondsCumulative CPU time; auto returns its rate in cores
sandbox.cpu.limitgaugecoresConfigured CPU limit
sandbox.memory.usagegaugebytesCgroup memory usage
sandbox.memory.working_setgaugebytesMemory working set
sandbox.memory.availablegaugebytesMemory available within the sandbox limit
sandbox.memory.limitgaugebytesConfigured memory limit
sandbox.memory.utilizationgaugeratioWorking set divided by the configured memory limit
sandbox.network.iocounterbytesReceive or transmit bytes, selected by the direction dimension; auto returns bytes per second
sandbox.network.errorscountercountReceive or transmit errors, selected by the direction dimension; auto returns errors per second
sandbox.process.countgaugecountCurrent sandbox process count
sandbox.rootfs.writable.usagegaugebytesMain runtime container writable-layer usage when the CRI reports it
sandbox.rootfs.writable.inodesgaugecountMain runtime container writable-layer inodes when the CRI reports them

Each series contains zero or more continuous segments:

  • runtime resets and collection gaps start a new segment; clients must not connect lines across segments
  • typed gaps explain missing intervals
  • the response reports freshness, the effective step_seconds, and whether the result is partial
  • missing or unsupported observations are omitted and never synthesized as zero
typescript
import { Client, SandboxRuntimeMetricName } from "sandbox0"; const client = new Client({ token: process.env.SANDBOX0_TOKEN! }); const metrics = await client.sandbox("sb_abc123").getMetrics({ startTime: new Date(Date.now() - 60 * 60 * 1000), metrics: [ SandboxRuntimeMetricName.SandboxCpuUtilization, SandboxRuntimeMetricName.SandboxMemoryWorkingSet, SandboxRuntimeMetricName.SandboxNetworkIo, ], maxPoints: 120, });

Historical Logs#

GET

/api/v1/sandboxes/{id}/observability/logs

Use this endpoint for retained logs. It is separate from the live output streams exposed by process and session APIs.

ParameterTypeDescription
start_timeRFC3339 timestampInclude entries that occurred at or after this time
end_timeRFC3339 timestampInclude entries that occurred at or before this time
limitintegerMaximum entries to return (default: 100, max: 1000)
cursorstringOpaque cursor from a previous response; in watch mode, use a watermark cursor
watchbooleanStream matching records as NDJSON until the client disconnects
context_idstringRestrict results to one process context
streamstringstdout, stderr, or pty

Audit Events#

GET

/api/v1/sandboxes/{id}/observability/events

The audit endpoint reads canonical signed facts from ClickHouse. It returns 403 feature_not_licensed when audit is not enabled or the cluster-gateway license does not contain sandbox_audit.

Query Parameters#

ParameterTypeDescription
start_timeRFC3339 timestampInclude events that occurred at or after this time
end_timeRFC3339 timestampInclude events that occurred at or before this time
limitintegerMaximum events to return (default: 100, max: 1000); exact event_id lookup ignores it and returns at most two payload variants
cursorstringOpaque cursor from a previous response; in watch mode, use a watermark cursor
watchbooleanStream matching records as NDJSON until the client disconnects
sourcestringTrusted producer, such as cluster_gateway or netd; netd identifies ctld network-runtime events
event_typestringCategory such as api_access or network_audit
outcomestringcompleted, denied, error, succeeded, failed, accepted, or unknown
actor_kindstringTrusted actor kind, such as human, api_key, service, or sandbox_workload
actor_idstringTrusted user, API key, service, or workload identifier
actionstringStable action such as sandbox.pause, process.exec, file.write, or network.connect
resource_typestringResource category such as sandbox or sandbox_network
operation_idstringCorrelates the attempt and result of one operation
event_idUUIDExact lookup for one stable audit ID; cannot be combined with time, cursor, watch, or other event filters

Event Model And Integrity#

FieldMeaning
event_idStable UUID for the audit fact
phaseattempt, result, or effect
actor, action, resourceWho acted, what they did, and the affected resource
producerTrusted service and producer sequence
operation_idCorrelation ID shared by facts from the same operation
requestBounded request metadata
integrity.payload_hashCanonical SHA-256 payload digest
integrity.signatureEd25519 signature
integrity.signing_key_idSigning key identifier
integrity.signature_statusQuery-time result: verified, invalid, or unavailable

List and watch responses recompute each returned row's digest and verify its signature. An exact event_id lookup also detects reused IDs:

  • one canonical payload returns one row
  • conflicting payload hashes return at most two variants and set event_id_conflict=true
  • event_id_conflict does not replace signature_status, so callers can distinguish an ID collision from a signature failure

Pagination cursors and watch watermarks are response metadata. They are not repeated inside each signed event.

Audit Delivery#

Gateway operations produce an attempt before authorization or execution and a result after the downstream response or a failed admission decision.

Operationdurable_asynccanonical_sync
Non-mutating Sandbox API and public exposureMay proceed after local durable enqueue; ClickHouse visibility follows asynchronouslyWaits for ClickHouse to acknowledge the attempt before proceeding
New allowed network flowOpens after the ctld node-local spool fsyncs the attemptWaits for ClickHouse acknowledgement before opening, including server-first SSH probes
State-changing Sandbox APIAlways waits for ClickHouse acknowledgement before execution and before returning the signed resultSame strict behavior

The default durable_async path normally becomes query-visible after immediate delivery wakeups and the one-second flush/replay cycle. It is not a read-after-write guarantee.

Failure behavior is fail-closed at the applicable admission boundary:

FailureCaller-visible behavior
Local buffer cannot accept an eventSandbox0 tries synchronous canonical delivery; the operation is denied if that also fails
Canonical admission fails for a mutationThe downstream operation is not executed; the caller receives 503
A mutation executes but its result is only buffered for retryThe caller receives 503, not a success response
A downstream 5xx or panic has an uncertain execution resultThe audit result uses outcome=unknown
ClickHouse is unavailable in durable_asyncEligible traffic can continue while the durable buffer remains writable, but audit queries lag until replay succeeds
ClickHouse is unavailable in canonical_syncNew audited operations and flows fail closed

A failed canonical mutation admission records a correlated result when result custody succeeds:

FieldValue
outcomefailed
HTTP status503
execution_startedfalse
operation_executedfalse
failure_stagecanonical_audit_admission
failure_codecanonical_ack_unavailable

If terminal-result custody also fails, the response reports audit_result=unrecorded. Investigate the surviving attempt as an audit-completeness incident, not evidence that the operation ran.

Network Flow Events#

Network audit records the connection lifecycle:

  • an allowed flow has one attempt and one result when the flow closes
  • a long-lived keep-alive connection therefore has one allow attempt and one eventual flow result
  • parsed HTTP or MCP operations may appear in bounded protocol details rather than as separate connections
  • raw proxy and credential-resolution errors stay in local diagnostics because they may contain upstream or credential details

Watch Logs Or Events#

Set watch=true on the logs or events endpoint to receive application/x-ndjson in ingestion order.

Line typeMeaning
event or logOne matching record
watermarkResume cursor for a later watch request
heartbeatKeeps an idle stream active
errorBackend failure after streaming started; the stream then ends

Watch behavior:

  • without cursor or start_time, the stream starts at request time and does not replay history
  • end_time is not supported with watch=true
  • runtime metrics use bounded time-series queries and do not expose a watch mode

Storage And Coverage#

ClickHouse is the only audit read source. Fsync-backed buffers retain pending events for retry, but buffered events are invisible to list and watch queries until ClickHouse acknowledges them. Logs and runtime samples use separate historical projections with independent retention periods.

Current producers are:

  • cluster-gateway for authenticated Sandbox API access, including regional detail requests
  • the ctld network runtime for network attempts and results
  • manager for historical logs
  • node-local ctld collectors for sandbox-wide CRI runtime samples, collected every 15 seconds with jitter

Historical queries return 503 when their backend is disabled or unavailable. The static metric catalog remains available so clients can build selectors without probing storage.

Coverage Limits#

  • Metering windows remain separate usage-ledger records for quota, showback, and billing export.
  • process.* and file.* facts prove authenticated gateway requests and their HTTP results. They do not observe every process effect, SSH/SFTP command, POSIX write, direct SandboxVolume operation, or manager-initiated reconciliation.
  • Public exposure streams and WebSockets record a durable open attempt, but abrupt node or process loss can prevent the eventual close result.
  • Cluster-gateway pending events require reattachable durable storage. Node-local network-flow buffers survive same-node Pod restarts, not permanent node loss.
  • Per-row signatures detect modified returned facts. They do not prove that a privileged ClickHouse administrator never deleted a row.

Deployments that require completeness across node loss should preserve audit signing public keys and export signed facts or checkpoints to independently controlled immutable storage. See Self-hosted Observability for ClickHouse, retention, delivery persistence, and signing-key configuration.