Tenki Sandboxes

Run full microVM sandboxes from Convex actions with live state sync, giving each user or AI agent isolated shell, file, and network access.

Installation

npm install @tenkicloud/convex

About Tenki Sandboxes

Give every user or AI agent in your Convex app its own full Linux VM that boots in about a second. This component creates and drives those VMs from your Convex actions and mirrors each sandbox's state into a component table, so your UI subscribes with a plain Convex query and updates live as a sandbox goes from `provisioning` to `ready` to `paused`, with no polling. Sandboxes are keyed by `(ownerId, key)`, so one call gets a user their machine: run shell commands and background processes, read and write files, expose ports as public preview URLs, pause and resume with memory intact, and snapshot or fork environments. `create` is idempotent and crash-safe, `maxActiveSandboxes` caps spend, and your Tenki API key stays on the server as a Convex environment variable.

Benefits

Use cases

how to give each user their own cloud VM in a Convex app

Tenki Sandboxes provides a Convex component that provisions a microVM per user, keyed by ownerId and a string key. Calling tenki.create() from a Convex action creates the sandbox if it does not exist and resumes it if paused, with idempotent semantics safe to retry. The sandbox phase is stored in a Convex table, so your frontend can subscribe to live status updates using useQuery.

run shell commands from Convex actions for AI agents

Tenki Sandboxes exposes tenki.exec() for running a command and waiting for output, and tenki.spawn() for starting a background process that persists across action boundaries and across pause and resume cycles. Both run inside an isolated microVM, so each agent gets its own execution environment. The API key stays in a Convex environment variable and never reaches the client.

how to snapshot and fork a sandbox in Convex for AI agent branching

Tenki Sandboxes provides tenki.snapshot() to capture a sandbox's disk state and tenki.fork() to create a new sandbox from a snapshot of an existing one. After a fork, the two sandboxes run independently. This lets AI agent workflows branch from a known checkpoint without duplicating setup work.

real-time sandbox status updates in Convex UI

The Tenki component writes sandbox lifecycle state to a Convex table, so phases like provisioning, ready, pausing, paused, resuming, and terminated are immediately queryable. A Convex query using components.tenki.sandboxes.get can be subscribed to with useQuery, giving the UI live updates without polling.

Frequently asked questions

How does Tenki Sandboxes handle concurrent create calls for the same user?

Tenki Sandboxes makes create idempotent: calling tenki.create() concurrently for the same ownerId and key always results in a single sandbox. If a concurrent create fails mid-flight, the next call adopts the tagged session rather than creating a duplicate. This means it is safe to retry creates from Convex actions without risk of provisioning multiple sandboxes for the same identity.

How does multi-tenant isolation work in Tenki Sandboxes?

Each sandbox is keyed by a combination of ownerId and a string key. The README explicitly states that ownerId is the only thing separating tenants and must always be derived from ctx.auth, never from client input. Typically you pass the authenticated user ID returned by getAuthUserId as the ownerId.

How do I limit how many sandboxes can run simultaneously?

Tenki Sandboxes accepts a maxActiveSandboxes option when constructing the Tenki instance: new Tenki(components.tenki, { maxActiveSandboxes: 20 }). Once the limit is reached, any attempt to start or resume a sandbox throws a capacity_exceeded error. The check runs inside the same Convex transaction that reserves the sandbox row, so concurrent creates and forks cannot exceed the limit.

What happens when a Tenki sandbox reaches its time limit?

Tenki pauses the sandbox at its maxDurationMs deadline and retains it for the configured pause retention period, defaulting to 7 days. The Convex row keeps the last known phase until a refresh, reconcile, or the next create call syncs it. Calling tenki.create() on a paused sandbox resumes it and starts a new duration window of the same length, so maxDurationMs bounds each run rather than the sandbox's total lifetime.

Does Tenki Sandboxes require a specific Convex runtime for actions?

Yes. The Tenki SDK depends on Node.js, so the action file that calls Tenki methods must include the 'use node' directive at the top. Queries that read sandbox state from the Convex table do not require Node.js and run in the default Convex runtime, which means UI subscriptions work without any special configuration.

Links