isorun

July 31, 2026 · View on GitHub

npm types node provenance license docs

Official TypeScript SDK for Isorun. Create an isolated Linux VM from any container image, run commands in it, read and write files, and tear it down — billed by the second.

Install

npm install isorun

Requires Node.js 22.19 or newer.

Quickstart

import { Isorun } from 'isorun'

const isorun = new Isorun() // reads ISORUN_API_KEY from env

const sandbox = await isorun.create({ image: 'node:22' })

const { stdout } = await sandbox.exec('node -v')
console.log(stdout) // v22.x.x

await sandbox.destroy()

Get an API key at app.isorun.ai.

Examples

Run untrusted code

const sandbox = await isorun.create({ image: 'python:3.12' })
try {
  await sandbox.writeFile('/tmp/code.py', sourceFromLLM)
  const { stdout } = await sandbox.exec('python /tmp/code.py', 30)
  return stdout
} finally {
  await sandbox.destroy()
}

Fork a running sandbox

const parent = await isorun.create({ image: 'node:22' })
await parent.writeFile('/app/worker.js', workerCode)

const workers = await parent.fork(10) // 10 independent clones
const results = await Promise.all(
  workers.map((w) => w.exec('node /app/worker.js')),
)

Snapshot and restore

const base = await isorun.create({ image: 'python:3.12' })
await base.exec('pip install pandas numpy')
const { id } = await base.snapshot()
await base.destroy()

// Later, restore a fresh sandbox from the snapshot
const restored = await isorun.restore(id)
await restored.exec('python -c "import pandas; print(pandas.__version__)"')

Read and write files

await sandbox.writeFile('/app/config.json', JSON.stringify({ env: 'prod' }))
const contents = await sandbox.readFile('/app/config.json')
const entries = await sandbox.readdir('/app')

Expose a guest port over HTTPS

const url = sandbox.url(3000) // URL for the guest's port 3000
// Send requests with an `Authorization: Bearer ${apiKey}` header.

Agent frameworks

Use a sandbox as the code-execution tool in an agent stack. Each helper returns ready-to-use tools plus a close() to tear the sandbox down. The relevant peer dependency is installed only for the entry point you use.

OpenAI Agents

import { isorunTools } from 'isorun/openai-agents'

const { tools, close } = await isorunTools()
// add `tools` to your Agent, then call close() when the run is done

LangChain

import { isorunTools } from 'isorun/langchain'

const { tools, close } = await isorunTools()
// pass `tools` to a LangChain / LangGraph agent; close() when done

MCP server

Expose the Isorun tools over stdio to any MCP client:

ISORUN_API_KEY=isorun_live_... npx isorun-mcp

Claude Managed Agents

import { Isorun } from 'isorun'
import { runOrchestrator } from 'isorun/claude-agents/orchestrator'

await runOrchestrator({
  isorun: new Isorun(),
  environmentId: 'env_...',           // from the Anthropic Console
  environmentKey: 'sk-ant-oat01-...', // self-hosted environment key
  image: 'docker.io/isorun/claude-agents:0.4.3',
  vcpus: 2,
  memMiB: 4096,
})

See the Claude Managed Agents guide for setup.

API reference

new Isorun(options?)

const isorun = new Isorun({ apiKey: 'isorun_live_...' })
OptionTypeDefaultDescription
apiKeystringISORUN_API_KEY envYour API key.
apiUrlstringderived from key regionOverride the runner endpoint.

The runner URL is derived from the region encoded in the API key. ISORUN_API_URL also overrides it.

Client methods

await isorun.create(options?)      // → Sandbox
await isorun.get(id)               // → Sandbox | null
await isorun.list()                // → Sandbox[]
await isorun.restore(snapshotId)   // → Sandbox
await isorun.listSnapshots()       // → Snapshot[]
await isorun.deleteSnapshot(id)    // → void
await isorun.networkProfiles()     // → NetworkProfile[]
await isorun.usage()               // → UsageSummary
await isorun.history()             // → SandboxHistoryEntry[]
await isorun.connect()             // optionally pre-open connections before issuing requests

Sandbox methods

// Execute
await sandbox.exec(command, timeoutSec?)        // → { exitCode, stdout, stderr }

// Files
await sandbox.writeFile(path, content)          // string | Uint8Array
await sandbox.readFile(path)                     // → string
await sandbox.readdir(path)                      // → FileEntry[]

// URL builder for guest services
sandbox.url(port, path?)                         // → string

// Lifecycle
await sandbox.info()                             // → SandboxInfo
await sandbox.snapshot()                         // → Snapshot
await sandbox.fork(count?)                        // → Sandbox[]
await sandbox.hibernate()                        // pause + snapshot to disk
await sandbox.resume()                           // resume a hibernated sandbox
await sandbox.setTimeout(seconds)                // keep-alive; 0 disables auto-destroy
await sandbox.auditLog()                         // → AuditEntry[]
await sandbox.destroy()                          // → DestroyResult

All methods are fully typed; see the bundled .d.ts or docs.isorun.ai.

CreateOptions

FieldTypeDefaultNotes
imagestringnode:22Container image.
vcpusnumber1Virtual CPUs.
memMiBnumber1024Memory in MiB.
diskMiBnumber4096Scratch disk; wiped on destroy.
timeoutSecnumber300Auto-destroy timer; reset via setTimeout.
network{ allow?: string[]; deny?: string[] }Egress allow/deny lists (CIDRs, hostnames, wildcards).
networkProfilestringNamed egress profile (see networkProfiles()).
credentialsRecord<string, string>Credentials injected into the sandbox.

vcpus and memMiB together select a sandbox size.

Environment variables

VariablePurpose
ISORUN_API_KEYAPI key, used when not passed to the constructor.
ISORUN_API_URLOverride the runner endpoint.

Errors

Every method throws an IsorunError on a non-2xx response. It carries the HTTP status and a truncated body, so you can branch by code:

import { Isorun, IsorunError } from 'isorun'

try {
  await sandbox.exec('...')
} catch (e) {
  if (e instanceof IsorunError && e.status === 429) backoff()
  else throw e
}

Docs

Full guides and the complete API reference are at docs.isorun.ai.

License

MIT