Quickstart
Spin up your first Tenki Sandbox session in under a minute. Install the CLI or SDK, authenticate with an API key, and run commands inside an isolated Linux microVM.
Tenki Sandbox provides disposable Linux microVMs that you can control through the CLI, Go SDK, or TypeScript SDK. It's designed for running AI agents, executing untrusted code, automating jobs, spinning up reproducible development environments, and handling one-off compute tasks.
A sandbox session lets you:
- run shell commands and stream output
- read and write files
- expose ports through preview URLs
- accept SSH connections
- mount workspace-scoped persistent volumes
- snapshot and restore VM state
- spin up new sessions from a reusable template
- run OpenCode inside the VM for AI-driven workflows
Get started
The steps below cover the full flow: install, authenticate, create a session, run a command, move files, expose a port, and tear down. Pick your tool in any code block — the selection syncs across the rest of the page.
1. Install
Install the latest stable tenki CLI:
curl -fsSL https://tenki.cloud/install.sh | bashPin a version when you need reproducibility:
curl -fsSL https://tenki.cloud/install.sh | bash -s -- --version 0.1.0The binary installs to ~/.local/bin/tenki by default. Override the location with TENKI_INSTALL_DIR.
Verify the install:
tenki --version
tenki sandbox --helpThe CLI command group is tenki sandbox, with tenki sbx available as a shorter alias.
npm install @tenki/sandboxgo get github.com/TenkiCloud/tenki-sdk-go/sandbox2. Authenticate
Generate an API key from your workspace settings, then export it:
export TENKI_API_KEY=tk_your_api_key
# Optional: override the default API endpoint (defaults to https://api.tenki.cloud)
export TENKI_API_URL=https://api.tenki.cloudThe CLI and both SDKs read TENKI_API_KEY automatically. Tokens that start with tk_ are sent as Authorization: Bearer <token>. The SDKs also accept an explicit token through WithAuthToken() (Go) or the authToken option (TypeScript).
3. Create your first session
tenki sandbox create --name demo --cpu 2 --memory-mb 4096By default the CLI waits for the session to reach READY and prints the session ID. Persist it as the current session so you can omit --session from later commands:
tenki sandbox set <session-id>The remaining steps assume the current session is set. Pass --session <session-id> explicitly on any command if you skipped set or want to target a different session.
import { TenkiSandbox } from "@tenki/sandbox";
const sandbox = new TenkiSandbox(); // reads TENKI_API_KEY from env
await using session = await sandbox.createAndWait({
name: "demo",
cpuCores: 2,
memoryMb: 4096,
});await using ensures the session is closed automatically when it leaves scope. The remaining steps assume session is in scope.
package main
import (
"context"
"log"
"time"
tenkisandbox "github.com/TenkiCloud/tenki-sdk-go/sandbox"
)
func main() {
ctx := context.Background()
client, err := tenkisandbox.New() // reads TENKI_API_KEY from env
if err != nil {
log.Fatal(err)
}
defer client.Close()
session, err := client.CreateAndWait(
ctx,
3*time.Minute,
tenkisandbox.WithName("demo"),
tenkisandbox.WithCPUCores(2),
tenkisandbox.WithMemoryMB(4096),
)
if err != nil {
log.Fatal(err)
}
defer session.Close(ctx)
// Steps 4-6 go here.
}defer session.Close(ctx) tears the session down when main returns. The remaining steps assume session and ctx are in scope.
4. Run a command
tenki sandbox exec bash -lc 'uname -a && whoami'You will see streamed stdout and stderr, the final status, exit code, duration, and an execution ID.
const result = await session.exec("bash", {
args: ["-lc", "uname -a && whoami"],
timeoutMs: 30_000,
});
console.log(`exit=${result.exitCode} stdout=${result.stdout}`);result, err := session.Exec(
ctx,
"bash",
tenkisandbox.WithArgs("-lc", "uname -a && whoami"),
tenkisandbox.WithTimeout(30*time.Second),
)
if err != nil {
log.Fatal(err)
}
log.Printf("status=%s exit=%d stdout=%s", result.Status, result.ExitCode, result.StdoutString())5. Move files in and out
echo 'hello sandbox' | tenki sandbox write --path /workspace/hello.txt
tenki sandbox read --path /workspace/hello.txtYou can also write a local file directly:
tenki sandbox write --path /workspace/config.json --data-file ./config.jsonawait session.writeFile("/workspace/hello.txt", "hello sandbox");
const data = await session.readFile("/workspace/hello.txt");if err := session.WriteFile(ctx, "/workspace/hello.txt", []byte("hello sandbox")); err != nil {
log.Fatal(err)
}
data, err := session.ReadFile(ctx, "/workspace/hello.txt")
if err != nil {
log.Fatal(err)
}
log.Printf("read: %s", data)6. Expose a port
Start a server inside the sandbox, then publish its port at a public preview URL.
tenki sandbox exec bash -lc 'python3 -m http.server 3000 &'
tenki sandbox expose --port 3000The CLI prints a preview_url you can open in a browser. List exposed ports with tenki sandbox ports and remove one with tenki sandbox unexpose.
await session.exec("bash", { args: ["-lc", "python3 -m http.server 3000 &"] });
const port = await session.exposePort(3000, { ttlMs: 3600_000 });
console.log(port.previewUrl);if _, err := session.Exec(ctx, "bash", tenkisandbox.WithArgs("-lc", "python3 -m http.server 3000 &")); err != nil {
log.Fatal(err)
}
port, err := session.ExposePort(ctx, 3000)
if err != nil {
log.Fatal(err)
}
log.Printf("preview URL: %s", port.PreviewURL)7. Tear it down
tenki sandbox terminateThe await using declaration in step 3 closes the session automatically when it leaves scope. To tear down explicitly:
await session.terminate();The defer session.Close(ctx) in step 3 tears the session down when main returns. To terminate explicitly:
if err := session.Terminate(ctx); err != nil {
log.Fatal(err)
}What's next
- Read the Concepts page to learn how sessions, volumes, snapshots, and templates fit together.
- See Sessions for command execution, file I/O, port exposure, and SSH.
- See Volumes, Snapshots, and Templates for durable state.
- The full SDK reference covers every option for both Go and TypeScript.
Need help? Email us at [email protected] and we'll be happy to onboard you.