Sandboxes
Run isolated commands, keep a session for multiple steps, and access its files and HTTP services.
Run a command in a temporary Sandbox. Volcano terminates the session after the command completes.
import { VolcanoClient } from '@volcano.dev/sdk';
const client = new VolcanoClient({
apiUrl: process.env.VOLCANO_API_URL ?? 'https://api.volcano.dev',
anonKey: process.env.VOLCANO_ANON_KEY,
accessToken: process.env.VOLCANO_SERVICE_KEY,
});
const projectId = process.env.VOLCANO_PROJECT_ID;
const { data, error } = await client.sandboxes.exec(projectId, 'python -c "print(42)"', {
preset: 'python3.12',
region: 'aws-us-east-1',
});
if (error) throw error;
console.log(data.stdout, data.exitCode);Keep service keys on your backend. Creating and managing sessions requires a platform user or service key. An authenticated project user can access only sessions explicitly granted to that user. Anonymous keys can list the public preset catalog with sandboxes.presets(), but cannot create or access sessions. Availability depends on your platform's Sandbox rollout.
A nonzero command exit is returned as data.exitCode; it is not a platform error. All methods return { data, error }. Check error before using data.
Reuse a session
Creation returns the current state, which may be starting. Poll refresh() until it becomes running; fail on unknown or terminated and keep polling bounded. Commands require a running session.
const created = await client.sandboxes.create(projectId, {
preset: 'python3.12',
region: 'aws-us-east-1',
maxDurationSeconds: 300,
});
if (created.error) throw created.error;
const session = created.data;
try {
const deadline = Date.now() + 120_000;
while (session.state !== 'running') {
if (Date.now() >= deadline || ['unknown', 'terminated'].includes(session.state)) {
throw new Error(`Session unavailable: ${session.state}`);
}
await new Promise((resolve) => setTimeout(resolve, 1000));
const refreshed = await session.refresh();
if (refreshed.error) throw refreshed.error;
}
const written = await session.files.write('/workspace/input.txt', new TextEncoder().encode('hello'));
if (written.error) throw written.error;
const output = await session.exec('cat /workspace/input.txt');
if (output.error) throw output.error;
console.log(output.data.stdout);
} finally {
const stopped = await session.terminate();
if (stopped.error) throw stopped.error;
}In a runtime with explicit resource management, use await using session = created.data after checking the creation result. Leaving the scope requests termination through Symbol.asyncDispose. Disposal skips sessions already terminating or terminated and accepts a 404 for a session already removed. Termination is durable and asynchronous; poll refresh() if you need confirmation that the session has stopped.
session.suspend() and session.resume() request state transitions. Poll for suspended or running before the next operation. Local suspension preserves processes and files, but the container continues to reserve memory.
Read files and access HTTP
session.files.read(path) returns Uint8Array. Writes accept Uint8Array; paths stay within the session workspace. Files are limited to 8 MiB.
Start a background HTTP service inside the session, then obtain a short-lived credential for its port:
const started = await session.exec('python -m http.server 8080 >/workspace/http.log 2>&1 &');
if (started.error) throw started.error;
const access = await session.access(8080);
if (access.error) throw access.error;
const response = await fetch(access.data.url, {
headers: { 'X-Volcano-Sandbox-Token': access.data.token },
});
console.log(await response.text());Read the scoped credential through access.data.token; it is omitted from JSON serialization and routine object logging. The token is bound to this session and port. Do not append it to URLs. See Sandbox HTTP access for browser redemption and authorization.
Retry safely
Pass a UUID requestId to sandboxes.create(), sandboxes.deploy(), sandboxes.exec(), or session.exec(). Other operations accept cancellation through signal but do not accept requestId. Retry the same intent with the same ID after a network failure. Do not reuse the ID for a different command. The SDK does not automatically replay failed commands.
Pass signal to cancel waiting. Cancellation does not prove the remote command stopped. The HTTP timeout budget is the command timeout plus 120 seconds for provisioning, or the configured client timeout if longer. A one-shot command timeout returns HTTP 504; retrying the same request ID returns HTTP 409 and does not rerun it. A session command timeout returns command data with timedOut: true. One-shot commands allow up to 60 seconds; session commands allow up to 3600 seconds.
Use either preset or a saved template's sandboxId, never both. sandboxes.presets() lists available preset IDs, memory sizes, and regions. sandboxes.get(sessionId) reconnects to an existing session. Backend code can call grant(sessionId, authUserId, expiresAt) and revoke(sessionId, authUserId) to control user access.
Deploy custom templates
Build a tar.gz archive containing a root Dockerfile fragment and its files.
Volcano supplies the base image and entrypoint; use RUN, COPY, and CMD
instead of FROM, USER, or ENTRYPOINT. Use the backend client initialized
above with a service key granting sandboxes.deployments.write to deploy and
sandboxes.deployments.read to read history, status, source, and logs.
const sandboxId = crypto.randomUUID();
const requestId = crypto.randomUUID();
const { readFile } = await import('node:fs/promises');
const source = new Uint8Array(await readFile('./sandbox.tar.gz'));
const { data: deployment, error } = await client.sandboxes.deploy(projectId, sandboxId, source, {
name: 'my-image',
memoryMB: 1024,
ports: [8080],
requestId,
});
if (error) throw error;
const state = await client.sandboxes.deployment(projectId, sandboxId, deployment.id);ports accepts at most 16 unique integers from 1 through 65532.
Keep the same template ID, request ID, source bytes, and options when retrying an
uncertain request. To update an existing template, deploy with its ID and a new
request ID. Wait for status: 'active' before creating a session from that
sandboxId. Existing sessions retain their original image.
deployments(projectId, sandboxId, { cursor, limit: 25 }) returns paginated history.
limit accepts 1–100 and defaults to 10; use the returned nextCursor for the next page.
source(projectId, sandboxId, deploymentId) returns the original archive as
Uint8Array. Sources may be at most 32 MiB compressed and expanded.
Read regional build output with
logs(projectId, sandboxId, deploymentId, { region: 'aws-us-east-1', limit: 100 }).
The response contains data events and an optional nextCursor; pass it as
cursor to read the next page. Failed builds remain visible in deployment history.
Delete a template
deleteTemplate(projectId, sandboxId) retires the template and requests termination
of all its sessions, including running work. The template becomes unavailable for
new sessions. Use this only when you intend to remove the template; to stop one
session while keeping the template, call session.terminate() instead.
Call it from backend code with a platform user token or a service key granting
sandboxes.terminate in the target project. An anonymous key or an authenticated
project-user session does not authorize template deletion.
const deleted = await client.sandboxes.deleteTemplate(projectId, sandboxId);
if (deleted.error) throw deleted.error;A successful response acknowledges the deletion request. Session termination and
resource cleanup finish asynchronously; wait for sessions to reach terminated
if your workflow requires confirmation that execution has stopped.
Runtime billing
Confirmed cloud running time is measured using baseline memory times observed running duration and consumes the account's Sandbox allowance. Paid overage is purchased in 5 GBh blocks at 25 credits per block; unused purchased capacity carries forward. Usage is not rounded up to a whole hour per session. Suspended and uncertain time are uncharged, snapshot costs are included, and burst resources are not billed separately. Local sessions consume no cloud allowance or credits.
Plan and allowance refusals use sandbox_plan_limit_exceeded and
sandbox_allowance_exhausted. Retrying these errors with a fresh request ID
does not bypass the plan or account limit.
Realtime
Volcano Realtime enables live data synchronization using WebSockets. Subscribe to database changes, track user presence, and broadcast messages between clients.
Storage
Volcano Storage provides secure file storage with access control. Upload user avatars, documents, media files, and more with built-in security policies.