@unikernelai/sandbox
v1.4.0
Published
TypeScript SDK for the Unikernel AI sandbox — isolated code execution with sub-second cold starts
Maintainers
Readme
@unikernelai/sandbox
TypeScript SDK for the Unikernel AI sandbox API. Run Python, Node.js, and shell code in isolated unikernel VMs with sub-second cold starts.
Install
npm install @unikernelai/sandboxQuick Start
Fastest path: Create sandbox + run code in one call (recommended)
The combined create+execute API gets you a sandbox and the first execution result in a single network round trip — 56% faster than the traditional two-step approach.
import { UnikernelsClient } from "@unikernelai/sandbox";
const client = new UnikernelsClient({
apiKey: "your-api-key",
});
// Create sandbox AND run initial code in 1 request (~350ms vs ~750ms)
const { sandbox, initialResult } = await client.createSandbox({
initialCommand: "echo hello && node -v",
});
console.log(initialResult?.stdout); // "hello\nv20.20.2\n"
// Subsequent commands reuse the same sandbox
const result = await sandbox.execute({
language: "nodejs",
code: `console.log(JSON.stringify({ ts: Date.now() }))`,
});
await sandbox.delete();One-shot execution (stateless, no sandbox)
For simple, single executions where you don't need persistent state:
import { UnikernelsClient } from "@unikernelai/sandbox";
const client = new UnikernelsClient({ apiKey: "your-api-key" });
// Run Node.js
const result = await client.execute({
language: "nodejs",
code: `console.log("Hello from a unikernel!")`,
timeout_ms: 5000,
});
console.log(result.stdout); // "Hello from a unikernel!\n"
// Run Python
const py = await client.execute({
language: "python",
code: `print(sum(range(100)))`,
});
console.log(py.stdout); // "4950\n"
// Run shell commands
const sh = await client.command({
command: `echo "OS: $(uname -s)" && date`,
});
console.log(sh.stdout);Sandbox sessions (stateful)
Sandboxes maintain state across multiple executions — files, dependencies, and git repos persist for the session lifetime (default 15 minutes).
const { sandbox } = await client.createSandbox();
// Execute code in the sandbox
const result = await sandbox.execute({
language: "python",
code: `x = 42; print(x)`,
});
// Upload a file
await sandbox.uploadFile("data.txt", btoa("hello world"));
// List files
const files = await sandbox.listFiles();
// Install dependencies
await sandbox.installDependencies("python", [
{ name: "requests", version: "2.31.0" },
]);
// Clean up when done
await sandbox.delete();Configuration
const client = new UnikernelsClient({
// Default: https://sandbox.unikernel.ai (production)
baseUrl: "https://sandbox.unikernel.ai",
// API key authentication (recommended for execution)
apiKey: "uk_live_...",
// Or Bearer JWT auth (required for API key management)
authToken: "your-jwt-token",
// Default timeout for all execute/command calls (ms)
// Per-call timeout_ms overrides this value
defaultTimeoutMs: 5000,
// Custom fetch implementation (useful for testing or Node.js <18)
fetchImpl: customFetch,
});Timeout Configuration
// Set a global default (applies to execute, command, executeInSandbox)
const client = new UnikernelsClient({
apiKey: "uk_live_...",
defaultTimeoutMs: 5000, // 5 seconds for all calls
});
// Override per-call when needed
const result = await client.execute({
language: "python",
code: "import time; time.sleep(7); print('done')",
timeout_ms: 8000, // this call gets 8s, others still get 5s
});API Reference
UnikernelsClient
| Method | Description |
|--------|-------------|
| execute(params) | One-shot stateless code execution |
| command(params) | Run a shell command (stateless) |
| getCapabilities() | Get supported runtimes and limits |
| createSandbox(options?) | Create a sandbox session (optionally with initial command) |
| getSandboxHandle(id) | Get a Sandbox handle by ID without an API call |
| getSandbox(id) | Fetch sandbox details from server |
| deleteSandbox(id) | Delete a sandbox |
| createApiKey(input?) | Create an API key (requires Bearer auth) |
| listApiKeys() | List your API keys (requires Bearer auth) |
| revokeApiKey(id) | Revoke an API key (requires Bearer auth) |
createSandbox(options?)
The recommended way to create sandboxes. Pass an initial command to eliminate one network round trip:
// Option A: initial shell command (simplest)
const { sandbox, initialResult } = await client.createSandbox({
initialCommand: "node -v && npm -v",
});
// Option B: initial code in a specific language
const { sandbox, initialResult } = await client.createSandbox({
initialCode: "console.log(JSON.stringify({ ready: true }))",
initialLanguage: "nodejs",
initialTimeoutMs: 5000,
});
// Option C: plain create (no initial execution)
const { sandbox } = await client.createSandbox();Returns: CreateSandboxResult
type CreateSandboxResult = {
sandbox: Sandbox;
initialResult?: ExecutionResult; // Present when initial command/code was provided
};Sandbox
| Method | Description |
|--------|-------------|
| execute(params) | Execute code in this sandbox |
| refresh() | Refresh sandbox metadata from server |
| delete() | Delete the sandbox and release resources |
| uploadFile(path, contentBase64) | Upload a file |
| downloadFile(path) | Download a file |
| listFiles(path?) | List files in directory |
| deleteFile(path) | Delete a file |
| installDependencies(language, deps) | Install pip/npm packages |
| listDependencies(language?) | List installed packages |
| gitStatus() | Get git working tree status |
| gitCommit(input) | Create a git commit |
| gitBranches() | List branches |
| gitCreateBranch(branch, from?) | Create a branch |
| gitCheckout(branch) | Switch branches |
| applySnapshot(snapshotId) | Apply a snapshot template |
The Sandbox object also exposes:
sandbox.id— the sandbox session IDsandbox.initialResult— the result of the initial command (if provided at creation)
Types
type SupportedLanguage = "python" | "nodejs" | "shell";
type ExecutionResult = {
status: "success" | "error" | "timeout";
stdout: string;
stderr: string;
execution_time_ms: number;
};
type CreateSandboxOptions = {
initialCommand?: string; // Shell command to run immediately
initialCode?: string; // Code to execute (requires initialLanguage)
initialLanguage?: SupportedLanguage;
initialTimeoutMs?: number; // Timeout for initial execution
};
type CreateSandboxResult = {
sandbox: Sandbox;
initialResult?: ExecutionResult;
};
type SandboxSession = {
id: string;
state: "active" | "deleted";
createdAt: string;
updatedAt: string;
expiresAt: string;
executionCount: number;
};Supported Languages
| Language | Value | Runtime |
|----------|-------|---------|
| Python | "python" | Python 3.12 |
| Node.js | "nodejs" | Node.js 20 |
| Shell | "shell" | Bash |
Authentication
There are three authentication modes:
- Self-service provision — no auth needed, get a free-tier API key instantly
- API Key (
x-api-keyheader) — for executing code and sandbox operations - Bearer JWT (
Authorization: Bearer <token>) — for API key management
// Easiest: provision a key with zero auth (rate-limited by IP)
const client = new UnikernelsClient();
const key = await client.provision("[email protected]");
// key.key = "uk_live_..." — use this for all future calls
// For execution (most users)
const authed = new UnikernelsClient({ apiKey: key.key });
// For API key management (requires JWT)
const admin = new UnikernelsClient({ authToken: "your-jwt-token" });
const newKey = await admin.createApiKey({ label: "my-agent" });Agent Integration
Zero-to-executing in 3 lines
import { UnikernelsClient } from "@unikernelai/sandbox";
const client = new UnikernelsClient();
const key = await client.provision();
const authed = new UnikernelsClient({ apiKey: key.key });
const result = await authed.execute({
language: "nodejs",
code: `console.log("Agent is live!")`,
});As an MCP Server (Claude, Cursor, etc.)
{
"mcpServers": {
"unikernel-sandbox": {
"command": "npx",
"args": ["-y", "@unikernelai/sandbox-cli", "mcp"]
}
}
}CLI one-liner for agents
npm i -g @unikernelai/sandbox-cli && unik auth login --headless && unik sandbox run python "print('ready')"Error Handling
import { UnikernelsApiError } from "@unikernelai/sandbox";
try {
await client.execute({ language: "python", code: "print(1)" });
} catch (err) {
if (err instanceof UnikernelsApiError) {
console.error(err.statusCode, err.code, err.message);
// 429 "rate_limited" "Rate limit exceeded for this identity."
}
}Common Error Codes
| Code | Status | Meaning |
|------|--------|---------|
| invalid_payload | 400 | Malformed request body |
| unsupported_language | 422 | Language not supported |
| disallowed_capability | 422 | Code uses blocked APIs (fs, net, etc.) |
| code_too_large | 413 | Code exceeds 64KB limit |
| timeout_exceeds_limit | 422 | Timeout > 8000ms |
| sandbox_not_found | 404 | Sandbox doesn't exist |
| sandbox_deleted | 409 | Sandbox already deleted or expired |
| unauthorized | 401 | Missing/invalid credentials |
| rate_limited | 429 | Per-minute rate limit hit |
| daily_quota_exceeded | 429 | Daily quota exhausted |
Performance
Requests are automatically geo-routed to the nearest region via Cloudflare:
| Region | Sequential exec p50 | Parallel burst p50 | Sandbox TTI | |--------|---------------------|-------------------|-------------| | Frankfurt (FRA) | 35 ms | 85 ms | 50 ms | | San Francisco (SFO) | 56 ms | 74 ms | 46 ms | | Singapore (SIN) | 33 ms | 85 ms | 46 ms |
Measured from colocated Fly.io machines in each region (Jun 2025).
Geo-routing: The default endpoint https://sandbox.unikernel.ai routes to the nearest datacenter automatically:
- Americas → SFO
- Asia-Pacific → SIN
- Europe / Africa / Middle East → FRA
You can override region selection with ?region=fra|sfo|sin or the X-Region header.
Tip: Always use
createSandbox({ initialCommand: "..." })instead of creating a sandbox and then running a command separately. It saves one full network round trip.
License
MIT
