Skip to content
Sandbox SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

Tenki

Run coding agents on full Tenki Linux VMs. Memory pause, disk and memory snapshots, exact sizes, network allow lists, and a hard lifetime.

tenki() runs each sandbox as a full Linux VM on Tenki through the official @tenkicloud/sandbox SDK.

Install

npm install @opencoredev/sandbox-sdk @tenkicloud/sandbox
pnpm add @opencoredev/sandbox-sdk @tenkicloud/sandbox
yarn add @opencoredev/sandbox-sdk @tenkicloud/sandbox
bun add @opencoredev/sandbox-sdk @tenkicloud/sandbox

The adapter needs @tenkicloud/sandbox 1.4.1 or later. Set TENKI_API_KEY to a workspace API key on your server, and keep it out of browser bundles.

Quickstart

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { tenki } from "@opencoredev/sandbox-sdk/tenki";

await using box = await createSandbox(tenki());
const result = await box.run("uname -a");
console.log(result.stdout);

await using terminates the VM when the block ends.

What it supports

Feature Support Notes
Commands and streaming Yes Output streams in bounded pages
Files Yes
Background processes Yes
Preview URLs Yes Public https://, with an expiry when Tenki reports one
Pause and resume Yes Disk and memory survive
Snapshots Yes Disk and memory, with optional expiry
Machine size Yes Presets or exact CPU, memory, and disk
Network policy Yes "none" or a host allow list, *. wildcards allowed
Hard lifetime (timeoutMs) Yes
Idle timeout (idleTimeoutMs) No Tenki removed idle auto-pause
Keys Best-effort Stored in session metadata
Terminal No

The capability matrix compares these with every other provider.

Run commands

Await box.run for the result, or iterate it to stream output as it arrives.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { tenki } from "@opencoredev/sandbox-sdk/tenki";

await using box = await createSandbox(tenki());
const result = await box.run("ls -la", { cwd: "/workspace", timeoutMs: 30_000 });
console.log(result.exitCode, result.stdout);

for await (const chunk of box.run("for i in 1 2 3; do echo $i; sleep 1; done")) {
  process.stdout.write(chunk.text);
}

A timeout or abort kills the command’s whole process group.

Files

Relative paths resolve against /workspace.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { tenki } from "@opencoredev/sandbox-sdk/tenki";

await using box = await createSandbox(tenki());
await box.files.write("notes/todo.txt", "ship the docs\n");
console.log(await box.files.read("notes/todo.txt"));
console.log(await box.files.list("notes"));
await box.files.write("secrets/token", "abc123", { mode: 0o600 });

{ mode } is set when the file is written, by Tenki’s streaming file API under /home/tenki (where /workspace lives) and by the guest helper elsewhere, so the file never has a looser mode.

Background processes and previews

No ports to declare. The preview URL is public, so keep authentication in the app you expose.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { tenki } from "@opencoredev/sandbox-sdk/tenki";

await using box = await createSandbox(tenki());
await box.start("python3 -m http.server 3000", { name: "web", ready: { port: 3000 } });
const preview = await box.preview(3000);
console.log(preview.url, preview.expiresAt);

Outbound network rules don’t make a preview private.

Pause, resume, and snapshots

Pause and resume keep disk and memory. Snapshots capture both, but not attached native volumes.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { tenki } from "@opencoredev/sandbox-sdk/tenki";

await using box = await createSandbox(tenki(), { key: "user:123" });
await box.pause();
await box.resume();

const { id } = await box.snapshot();
await using copy = await createSandbox(tenki(), { from: id });

Pause closes active TCP, preview, and SSH connections; reconnect clients after resume. Snapshots stay in Tenki after the VM is gone, unless you give them an expiry:

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { tenki } from "@opencoredev/sandbox-sdk/tenki";

await using box = await createSandbox(tenki());
const { id } = await box.snapshot({ expiresInMs: 7 * 24 * 60 * 60_000 }); // Tenki deletes it in 7 days

expiresInMs: 0 or no option keeps the snapshot until you delete it. A paused sandbox is kept for Tenki’s pause retention; set pauseRetentionMs to change it.

Size, network, and lifetime

Preset CPUs Memory Disk
small 1 1024 MiB 10 GiB
medium 2 4096 MiB 20 GiB
large 4 8192 MiB 40 GiB
import { createSandbox } from "@opencoredev/sandbox-sdk";
import { tenki } from "@opencoredev/sandbox-sdk/tenki";

await using box = await createSandbox(tenki(), {
  size: { cpu: 4, memoryMb: 16_384, diskGb: 50 },
  network: { allow: ["registry.npmjs.org", "*.github.com"] },
  timeoutMs: 30 * 60_000,
});
  • Exact sizes take 1 to 128 CPUs, an even memoryMb from 128 to 524288, and 5 to 100 GiB of disk. Workspace quotas can be lower.
  • Without size, the base image gets 2 CPUs, 4096 MB, and 5 GiB.
  • An empty allow list blocks outbound traffic. The adapter checks the returned policy and fails if it doesn’t match.
  • timeoutMs sets Tenki’s maxDurationMs. idleTimeoutMs fails with unsupported.

Configuration

Option Type Default Env var
apiKey string TENKI_AUTH_TOKEN, then TENKI_API_KEY
baseUrl string https://api.tenki.cloud TENKI_API_ENDPOINT, then TENKI_API_URL
image string Tenki’s default image
requestTimeoutMs number 30_000
waitTimeoutMs number 180_000; snapshots 300_000
resumeTimeoutMs number 180_000, at most 900_000
pauseRetentionMs number Tenki’s retention
previewTtlMs number Tenki’s preview TTL
volumes { volumeId, mountPath, readOnly? }[]
workspaceId string Inferred from the API key

authToken is a deprecated alias for apiKey. volumes mounts Tenki volumes you created with the native SDK. workspaceId is only needed for service credentials that span workspaces; it scopes both creation and listing. A custom image needs Python 3, sh, Git, tar, setsid, and nohup, and must allow privileged execution. box.native is a typed Tenki Session; its close() terminates the VM, so use box.close() to detach.

Limitations

  • Keys are best-effort. Pass a locator for exact get-or-create.
  • No idle timeout or terminals.
  • Pause closes active connections.
  • Snapshots have their own lifecycle; delete them through the native client.

How it works

  • Tenki clears /tmp on pause. The adapter keeps SDK state under /home/tenki/.sandbox-sdk and relinks /tmp/.sandbox-sdk on resume and connect.
  • Sticky mode is off, so it can’t override timeoutMs.
  • Tenki 1.4.1 can’t cancel control-plane calls. A timed-out create may still finish; the adapter tries to close a late VM, but keep your process alive for that and check your sessions if it fails.
  • Command output spools to a file in the VM, which uses guest disk until the run finishes.

Next steps

Was this page helpful?