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, TypeScript SDK, or Python 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
- 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 @tenkicloud/sandboxgo get github.com/TenkiCloud/tenki-sdk-go/sandboxRequires Python 3.10+.
pip install tenki-sandbox2. Authenticate
There are two ways to authenticate the CLI.
Option 1: tenki login (recommended for local dev)
Interactive browser sign-in that stores the session and auto-selects a workspace and project:
# Opens your browser to sign in
tenki login
# Verify you are signed in
tenki status
# Headless: print the URL instead of opening a browser
tenki login --no-browserOption 2: API key (recommended for CI/automation, and used by the SDKs)
Generate a key from your workspace settings, then either sign the CLI in with it or export it:
# Sign the CLI in with a key (no browser)
tenki login --api-key tk_your_api_key
# Or set it in the environment — the CLI and all SDKs read it automatically
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.cloudTokens that start with tk_ are sent as Authorization: Bearer <token>. The SDKs also accept an explicit token through WithAuthToken() (Go), the authToken option (TypeScript), or auth_token= (Python).
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 "@tenkicloud/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.
from tenki_sandbox import Sandbox
# Reads TENKI_API_KEY from env. Waits for RUNNING by default.
sb = Sandbox.create(name="demo", cpu_cores=2, memory_mb=4096)Sandbox.create is also a context manager — with Sandbox.create(...) as sb: closes the session automatically when the block exits. The remaining steps assume sb is in scope.
4. Run a command
tenki sandbox exec -c 'uname -a && whoami'-c (short for --shell) runs the line through a shell, so pipes, redirects, globs, $VAR, && and & work. Without it, exec runs the program directly with no shell. You will see streamed stdout and stderr, then the final status, exit code, and duration.
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())result = sb.exec("bash", "-lc", "uname -a && whoami", timeout=30)
print(f"exit={result.exit_code} stdout={result.stdout_text}")5. Move files in and out
File operations are rooted in the session's working directory, /home/tenki. Relative paths resolve from there; absolute paths outside it are rejected with a permission error. (/workspace/... paths only exist when you mount a volume there.)
echo 'hello sandbox' | tenki sandbox write --path hello.txt
tenki sandbox read --path hello.txtYou can also write a local file directly:
tenki sandbox write --path config.json --data-file ./config.jsonawait session.writeFile("/home/tenki/hello.txt", "hello sandbox");
const data = await session.readFile("/home/tenki/hello.txt");if err := session.WriteFile(ctx, "/home/tenki/hello.txt", []byte("hello sandbox")); err != nil {
log.Fatal(err)
}
data, err := session.ReadFile(ctx, "/home/tenki/hello.txt")
if err != nil {
log.Fatal(err)
}
log.Printf("read: %s", data)sb.fs.write_text("/home/tenki/hello.txt", "hello sandbox")
data = sb.fs.read_text("/home/tenki/hello.txt")6. Expose a port
Start a server inside the sandbox, then publish its port at a public preview URL.
When you background a long-running server, redirect its output and detach stdin (>/tmp/server.log 2>&1 </dev/null &). exec streams the command's output until it closes; a backgrounded server that still holds the output stream keeps exec waiting forever.
tenki sandbox exec -c 'python3 -m http.server 3000 >/tmp/server.log 2>&1 </dev/null &'
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 >/tmp/server.log 2>&1 </dev/null &"] });
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 >/tmp/server.log 2>&1 </dev/null &")); err != nil {
log.Fatal(err)
}
port, err := session.ExposePort(ctx, 3000)
if err != nil {
log.Fatal(err)
}
log.Printf("preview URL: %s", port.PreviewURL)sb.shell("python3 -m http.server 3000 >/tmp/server.log 2>&1 &")
preview = sb.expose_port(3000, ttl=3600)
print(preview.url)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.close();The defer session.Close(ctx) in step 3 tears the session down when main returns. To terminate explicitly:
if err := session.Close(ctx); err != nil {
log.Fatal(err)
}If you used with Sandbox.create(...) as sb:, the session is terminated when the block exits. To tear down explicitly:
sb.terminate()What's next
- Read the Concepts page to learn how sessions, volumes, and snapshots fit together.
- See Sessions for command execution, file I/O, port exposure, and SSH.
- See Volumes and Snapshots for durable state.
- The full SDK reference covers every option for the Go, TypeScript, and Python SDKs.
Need help? Email us at hello@tenki.cloud and we'll be happy to onboard you.