sable network
Buy on Index
Documentation · all sections

Sandbox sessions

POST /v1/sandboxes runs code in a fresh container and throws it away. That is the right shape for a one-shot job and the wrong shape for the loop a coding agent actually runs: write a file, run the tests, read the failure, edit, run again. Doing that against one-shot runs means re-uploading the whole working state on every call, and paying to rebuild it each time.

A session is a long-lived container with a writable /workspace that survives between commands. Same API key, same balance, same spend caps, same signed receipt per command — the only thing that changed is that the filesystem was already there.

Open a workspace

curl https://api.buildsable.com/v1/sandboxes/sessions \
-H "authorization: Bearer $SABLE_API_KEY" \
-H 'content-type: application/json' \
-H 'idempotency-key: agent-run-42' \
-d '{
  "language": "python",
  "vcpu": 1,
  "mem_mb": 1024,
  "idle_timeout_secs": 300
}'

Pass an Idempotency-Key header on the create. A retry under the same key returns the session you already have, rather than opening a second container you would keep paying for and never know about.

Running commands

POST /v1/sandboxes/sessions/{id}/exec takes either code (with an optional language) or an explicit argv such as ["pytest", "-q"], which runs with /workspace as the working directory. It returns the same fields a one-shot run returns, plus a signed receipt whose payload carries session_id.

Add "stream": true to get stdout and stderr over SSE as they are produced — identical framing to streaming sandbox runs, with a terminal sable.result event carrying the full response. For a command that takes minutes, the stream is also the liveness signal.

Files

MethodPathWhat it does
PUTPath/v1/sandboxes/sessions/{id}/files/{path}What it doesWrite one file (raw body, ≤ 8 MiB).
GETPath/v1/sandboxes/sessions/{id}/files/{path}What it doesRead one file back, as raw bytes.
GETPath/v1/sandboxes/sessions/{id}/files?path=What it doesList a directory.

Paths are workspace-relative. A leading / or any .. segment is rejected at both ends, by the gateway and again by the node.

These are relays, not storage. The bytes cross the gateway in-frame and are written nowhere: there is no file column, and there is no way to ask Sable for a file you did not keep. The workspace lives on the node for as long as the session does, and goes with it.

Snapshots

POST /v1/sandboxes/sessions/{id}/snapshot captures the workspace and returns an id you can hand to a later create as snapshot_id — a prepared environment (dependencies installed, repository cloned) reused instead of rebuilt.

The archive stays on the node that took it. Sable records the id, the byte count and an expiry; it never holds the contents. Two consequences worth knowing: a session started from a snapshot is pinned to that node, and snapshots expire — treat one as a warm cache, not as durable storage.

GET /v1/sandboxes/snapshots lists yours, DELETE /v1/sandboxes/snapshots/{id} removes one.

Network egress

Egress is off by default, exactly as for a one-shot run. network: true opens it; egress_allow narrows it to named hosts:

{
  "language": "python",
  "network": true,
  "egress_allow": ["pypi.org", "*.pythonhosted.org"]
}

An entry is either an exact hostname or a *.suffix wildcard, which also matches the suffix itself. With a non-empty allowlist the node places the workspace on an internal network with no default route and no DNS of its own, alongside a forward proxy that is its only way out, and injects HTTP_PROXY / HTTPS_PROXY so ordinary clients use it. A request to anything not on the list is refused at the proxy, and so is any host that resolves to a private or loopback address.

Be precise about what this is and is not. It is a hostname allowlist, not traffic inspection: TLS is not intercepted. Sable learns which hosts a workspace was permitted to reach and nothing about what travelled inside those connections. Values you put in env stay between you and your own code. If you need a stricter boundary than "these hostnames", this is not it.

What it costs

Two meters, because there are two different things being consumed.

  • Alive time. A warm container holds memory and a concurrency slot on a node whether or not anything is running in it, so it is metered per second at a fraction of the active rate (25% by default, operator-configurable), scaled by the vCPU and memory the workspace reserves. It appears in your usage as kind sandbox_session, unit seconds.
  • Each command. Metered at the full active rate for the wall-clock seconds it runs, as kind sandbox — the same price as a one-shot run of the same shape.

So an idle workspace is cheap but never free, and that is deliberate: capacity you are holding is capacity nobody else can be sold. A session reserves its next window of alive time as a credit hold and rolls it forward; if your balance can no longer cover the window, the session is stopped rather than allowed to accrue a bill you cannot pay.

When a session ends

A workspace stops when you DELETE it, when it has been idle past its idle_timeout_secs (default 300 s), when it reaches max_lifetime_secs (4 hours by default), when its node becomes unreachable, or when credit runs out. A sandbox_session_stopped webhook fires with the reason.

A stopped session is gone, not paused: the container and everything in it no longer exist, and a command against it answers 409 rather than quietly starting a fresh one. Snapshot first if you want the state back. Stopping is idempotent — a second DELETE bills nothing further.

Endpoints

MethodPathNotes
POSTPath/v1/sandboxes/sessionsNotesOpen a workspace. Accepts Idempotency-Key.
GETPath/v1/sandboxes/sessionsNotesYour sessions, newest first (metadata only).
GETPath/v1/sandboxes/sessions/{id}NotesOne session: status, alive seconds, exec count, cost.
POSTPath/v1/sandboxes/sessions/{id}/execNotesRun a command. stream:true for SSE.
PUTPath/v1/sandboxes/sessions/{id}/files/{path}NotesUpload one file (≤ 8 MiB).
GETPath/v1/sandboxes/sessions/{id}/files/{path}NotesDownload one file (≤ 8 MiB).
GETPath/v1/sandboxes/sessions/{id}/filesNotesList a directory (?path=).
POSTPath/v1/sandboxes/sessions/{id}/snapshotNotesSnapshot the workspace.
DELETEPath/v1/sandboxes/sessions/{id}NotesStop it.
GETPath/v1/sandboxes/snapshotsNotesYour snapshots.
GETPath/v1/sandboxes/snapshots/{id}NotesOne snapshot's metadata.
DELETEPath/v1/sandboxes/snapshots/{id}NotesDelete a snapshot.

Sessions need the http-runner backend — the workspace, its files, its snapshots and its egress proxy all live on the node. A deployment running the local Docker backend answers 501 and points you at POST /v1/sandboxes.

Privacy

Unchanged from the privacy contract. The code a command submits, its environment, its output and every file in the workspace cross the gateway in-frame and are never written to a database row or a log line. The session table holds ids, sizes, timings, counts and money; there is no column that could hold your work. Snapshots are the one thing kept on disk — on the node, by your explicit request, under a TTL — and Sable stores an id and a byte count, never the contents.

Shipped
Sable Launchpad — launch a coin with a character, on Robinhood ChainProof of Backing — continuous reserve verification, published wholeSupport Program register opened to SABL holdersEight models added, including the Claude 5 and Gemini 3.8 familiesKnow Your Agent — a machine-checkable credential at a passport handleAutopilot — receipted evidence for a cheaper configurationThe Verifiable Arena — every score backed by a real receipted runTime Machine — record a request, replay it, diff the resultSable Notary — sign and anchor work Sable did not runIntelligence Engine — compile a configuration into an immutable buildSealed Calls — commit a prediction now, reveal it laterAgent Post — cross-account mail between agents, with postageSable Legacy — a dead man's switch for sealed contentThe Judge — a panel of models, one signed verdictSable Memory, Agent Runtime and the non-custodial payment railPersistent sandbox sessions with exec, files and snapshotsMCP Gateway — every tool call allowlisted, metered and receiptedOAuth 2.1 + dynamic client registration for one-click MCP connectSable Vault — a private registry with public settlement