Use sandboxes when your app needs to execute code without running it on your own host. SandboxClient runs commands in isolated Linux containers with explicit runtime, filesystem, resource, network, and timeout controls.
Install the standalone client with:
npm install @agentuity/sandbox @agentuity/telemetryimport { SandboxClient } from '@agentuity/sandbox';
import { logger } from '@agentuity/telemetry';
const client = new SandboxClient();
const result = await client.run({
runtime: 'python:3.14',
command: { exec: ['python3', '-c', 'print("hello from a sandbox")'] },
});
logger.info('sandbox output', {
exitCode: result.exitCode,
stdout: result.stdout,
});SandboxClient reads AGENTUITY_SDK_KEY, then AGENTUITY_CLI_KEY, from the environment. Pass apiKey, url, orgId, or logger only when your script needs to override the project defaults.
Use SandboxClient directly from server-side code, or c.var.sandbox when you are already using the Hono middleware.
When to use sandboxes
| Use case | Example |
|---|---|
| code execution agents | run user-provided Python or JavaScript without sharing your host filesystem |
| code validation | check whether generated code compiles, tests, or produces expected output |
| build jobs | install dependencies and run builds in a disposable environment |
| AI coding assistants | give an assistant a workspace where it can edit and execute files |
| data processing | run short-lived scripts with explicit CPU, memory, and timeout limits |
Access Patterns
| Pattern | Use It When |
|---|---|
SandboxClient.run() | You need one disposable command with captured output |
SandboxClient.create() | You need files, multiple commands, checkpoints, jobs, or snapshots |
c.var.sandbox | You are inside a Hono app using @agentuity/hono middleware |
agentuity cloud sandbox | You want to inspect, debug, or script sandboxes from your terminal |
| Web App | You want to browse runtimes, snapshots, and service state visually |
Choose the Execution Surface
| Need | Start With | Why |
|---|---|---|
| one command with input files and captured output | SandboxClient.run() | the sandbox is created, executed, and destroyed for you |
| multiple commands, writable files, snapshots, jobs, or exposed ports | SandboxClient.create() | you keep the sandbox state long enough to coordinate each step |
| repo-aware coding work with session history, skills, review, and reconnects | Coder | the unit of work is a managed session, not only a process |
| direct control over a coding-agent CLI or server image | Coding agents in sandboxes | you choose the runtime and manage provider config, events, and cleanup |
Key Concepts
| Concept | Description |
|---|---|
| Runtime | A base environment such as bun:1, node:lts, or python:3.14 |
| Sandbox | A running container created from a runtime or snapshot |
| Snapshot | A reusable filesystem state for starting future sandboxes |
| Checkpoint | A filesystem state for one sandbox, used by pause, resume, restore, and delete workflows |
Runtimes, sandboxes, and snapshots build on each other: runtime, sandbox, snapshot. Checkpoints stay scoped to one sandbox.
Execution Modes
Use client.run() for one command. It creates a one-shot sandbox, runs the command, captures output, and the sandbox is auto-destroyed after the command exits.
import { SandboxClient } from '@agentuity/sandbox';
import { logger } from '@agentuity/telemetry';
const client = new SandboxClient();
const result = await client.run({
runtime: 'bun:1',
command: {
exec: ['bun', '-e', 'process.stdout.write(JSON.stringify({ ok: true }))'],
},
timeout: { execution: '15s' },
});
logger.info('sandbox execution finished', {
exitCode: result.exitCode,
stdout: result.stdout,
});Use client.create() when the workflow needs multiple commands or persistent files. Destroy interactive sandboxes when you are done with them.
import { Buffer } from 'node:buffer';
import { SandboxClient } from '@agentuity/sandbox';
import { logger } from '@agentuity/telemetry';
const client = new SandboxClient();
const sandbox = await client.create({
runtime: 'bun:1',
resources: { memory: '1Gi', cpu: '1000m' },
network: { enabled: true },
timeout: { idle: '10m', execution: '2m' },
});
try {
await sandbox.writeFiles([
{
path: 'index.ts',
content: Buffer.from('process.stdout.write("ready")'),
},
]);
const execution = await sandbox.execute({
command: ['bun', 'run', 'index.ts'],
});
logger.info('sandbox execution finished', { exitCode: execution.exitCode });
} finally {
await sandbox.destroy();
}See Using the Sandbox API for file I/O, background jobs, checkpoints, snapshots, and method reference tables.
Runtimes
Runtimes are preconfigured base environments. List the runtimes available to your org from code or from the CLI:
import { SandboxClient } from '@agentuity/sandbox';
import { logger } from '@agentuity/telemetry';
const client = new SandboxClient();
const { runtimes } = await client.listRuntimes({ limit: 20 });
for (const runtime of runtimes) {
logger.info('sandbox runtime', {
name: runtime.name,
description: runtime.description ?? '',
});
}agentuity cloud sandbox runtime listRuntime responses include metadata such as description, iconUrl, brandColor, url, tags, and requirements. Names can change as new images are added, so list runtimes before hard-coding one in a long-lived workflow.
Coding-agent runtimes may appear in the same catalog as language runtimes. The runtime catalog is the source of truth for your org. See Coding agents in sandboxes before building around an agent image; model access and tool permissions are separate from runtime availability.
Snapshots
A snapshot captures a sandbox filesystem so new sandboxes can start with dependencies and files already present.
import { SandboxClient } from '@agentuity/sandbox';
import { logger } from '@agentuity/telemetry';
const client = new SandboxClient();
const sandbox = await client.create({
runtime: 'bun:1',
network: { enabled: true },
});
try {
await sandbox.execute({ command: ['bun', 'init', '-y'] });
await sandbox.execute({ command: ['bun', 'add', 'zod'] });
const snapshot = await client.createSnapshot(sandbox.id, {
name: 'bun-zod',
tag: 'bun-zod',
description: 'Bun project with Zod installed',
});
logger.info('snapshot created', { snapshotId: snapshot.snapshotId });
} finally {
await sandbox.destroy();
}Create future sandboxes from the snapshot ID or tag:
const sandbox = await client.create({
snapshot: 'bun-zod',
resources: { memory: '512Mi' },
});See Creating and Using Snapshots for declarative snapshot files, CLI commands, and snapshot management APIs.
Background Jobs
Use jobs for commands that should keep running while your process does other work. Jobs are managed from SandboxClient with a sandbox ID.
import { SandboxClient } from '@agentuity/sandbox';
import { logger } from '@agentuity/telemetry';
const client = new SandboxClient();
const sandbox = await client.create({ runtime: 'bun:1' });
try {
const job = await client.createJob(sandbox.id, {
command: ['sh', '-c', 'sleep 30 && echo done'],
});
const current = await job.get();
logger.info('sandbox job status', { jobId: job.id, status: current.status });
await job.stop();
} finally {
await sandbox.destroy();
}Hono
@agentuity/hono initializes SandboxClient once and exposes it on c.var.sandbox.
import { agentuity } from '@agentuity/hono';
import type { Services } from '@agentuity/hono';
import { Hono } from 'hono';
type Variables = Pick<Services, 'sandbox'>;
const app = new Hono<{ Variables: Variables }>();
app.use('*', agentuity());
app.post('/run', async (c) => {
const result = await c.var.sandbox.run({
runtime: 'bun:1',
command: { exec: ['bun', '-e', 'process.stdout.write("ok")'] },
});
return c.json({ stdout: result.stdout, exitCode: result.exitCode });
});
export default app;Events and Lifecycle
Every sandbox records lifecycle events. Use client.listEvents() when you need to inspect what happened to a sandbox.
import { SandboxClient } from '@agentuity/sandbox';
import { logger } from '@agentuity/telemetry';
const client = new SandboxClient();
const { events } = await client.listEvents('sbx_abc123', {
limit: 50,
direction: 'asc',
});
for (const event of events) {
logger.info('sandbox event', { type: event.type, createdAt: event.createdAt });
}Use sandbox.pause() and sandbox.resume() when you want to checkpoint and later restart an interactive sandbox. sandbox.execute() can also auto-resume a suspended sandbox before running a command and may return autoResumed: true.
import { SandboxClient } from '@agentuity/sandbox';
import { logger } from '@agentuity/telemetry';
const client = new SandboxClient();
const sandbox = await client.connect('sbx_abc123');
await sandbox.resume();
const execution = await sandbox.execute({
command: ['bun', 'run', 'test'],
});
logger.info('sandbox execution finished', {
executionId: execution.executionId,
autoResumed: execution.autoResumed,
});Security Defaults
Sandboxes give each run an isolated workspace and explicit resource limits. Outbound network access is disabled unless you set network.enabled: true. Expose inbound access separately with network.port.
Set timeouts for untrusted commands, keep resource limits tight, and use snapshots for shared dependencies instead of reinstalling packages on every run.
Next Steps
- Using the Sandbox API: method examples for
SandboxClient - Coding agents in sandboxes: run and verify coding-agent runtimes without assuming provider access
- Creating and Using Snapshots: declarative and manual snapshot workflows
- CLI Sandbox Commands: inspect and manage sandboxes from the terminal
- Sandboxes API Reference: inspect REST fields and lower-level method details