Running Code in Sandboxes

Run code in isolated containers with configurable runtimes, resources, and file access

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/telemetry
import { 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 caseExample
code execution agentsrun user-provided Python or JavaScript without sharing your host filesystem
code validationcheck whether generated code compiles, tests, or produces expected output
build jobsinstall dependencies and run builds in a disposable environment
AI coding assistantsgive an assistant a workspace where it can edit and execute files
data processingrun short-lived scripts with explicit CPU, memory, and timeout limits

Access Patterns

PatternUse 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.sandboxYou are inside a Hono app using @agentuity/hono middleware
agentuity cloud sandboxYou want to inspect, debug, or script sandboxes from your terminal
Web AppYou want to browse runtimes, snapshots, and service state visually

Choose the Execution Surface

NeedStart WithWhy
one command with input files and captured outputSandboxClient.run()the sandbox is created, executed, and destroyed for you
multiple commands, writable files, snapshots, jobs, or exposed portsSandboxClient.create()you keep the sandbox state long enough to coordinate each step
repo-aware coding work with session history, skills, review, and reconnectsCoderthe unit of work is a managed session, not only a process
direct control over a coding-agent CLI or server imageCoding agents in sandboxesyou choose the runtime and manage provider config, events, and cleanup

Key Concepts

ConceptDescription
RuntimeA base environment such as bun:1, node:lts, or python:3.14
SandboxA running container created from a runtime or snapshot
SnapshotA reusable filesystem state for starting future sandboxes
CheckpointA 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 list

Runtime 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