Sessions
Create and drive Tenki Sandbox sessions covering lifecycle, command execution, file I/O, port exposure, and SSH access.
A session is one running sandbox VM. This page covers the full session-level surface: lifecycle, command execution, file I/O, ports, and SSH.
Create a session
CLI
tenki sandbox create \
--name my-session \
--cpu 4 \
--memory-mb 8192 \
--allow-inbound \
--allow-outbound \
--env APP_ENV=dev \
--metadata owner=alice \
--metadata purpose=reviewSupported flags on tenki sandbox create:
--name--cpu,--memory-mb--allow-inbound,--allow-outbound--max-duration--snapshot,--template(mutually exclusive)--metadata key=value(repeatable)--env key=value(repeatable)--authorized-key,--authorized-keys-file(repeatable)--volume <volume-id>:<mount-path>[:ro](repeatable)--no-wait,--wait-timeout
By default, the CLI waits for the session to become READY before returning. Pass --no-wait to return immediately.
Go SDK
session, err := client.Create(
ctx,
tenkisandbox.WithName("demo"),
tenkisandbox.WithCPUCores(4),
tenkisandbox.WithMemoryMB(8192),
tenkisandbox.WithAllowInbound(true),
tenkisandbox.WithAllowOutbound(true),
tenkisandbox.WithEnvs(map[string]string{"APP_ENV": "dev"}),
tenkisandbox.WithMetadata(map[string]string{"owner": "alice"}),
)
// Or create-and-wait in one call:
session, err := client.CreateAndWait(ctx, 3*time.Minute, tenkisandbox.WithName("demo"))Useful create options:
WithName,WithCPUCores,WithMemoryMB,WithMaxDurationWithAllowInbound,WithAllowOutboundWithMetadata,WithEnvsWithSSHKeysWithVolume(volumeID, mountPath, ...VolumeOption)WithSnapshot,WithTemplateWithOpenCode,WithOpenCodeProviderWithCloneRepo,WithGitHubToken
Defaults applied by Create: inbound=false, outbound=true, cpu=2, memory=4096 MB.
TypeScript SDK
const session = await sandbox.createAndWait({
name: "demo",
cpuCores: 4,
memoryMb: 8192,
allowInbound: true,
allowOutbound: true,
env: { APP_ENV: "dev" },
metadata: { owner: "alice" },
});Lifecycle
err = session.WaitReady(ctx, 3*time.Minute)
err = session.Refresh(ctx)
err = session.Extend(ctx, 30*time.Minute)
err = session.Close(ctx)
err = session.CloseIfOpen(ctx)In TypeScript:
await session.extend(600_000); // +10 minutes
await session.pause();
await session.resume();
await session.close(); // or use Symbol.asyncDisposeList and inspect
tenki sandbox list
tenki sandbox list --json
tenki sandbox get --session <session-id>sessions, err := client.List(ctx)
session, err := client.Get(ctx, sessionID)Template inheritance
If you create a session from a template:
- the template's CPU and memory defaults are inherited unless you override them
- explicit
--cpu/--memory-mbalways win
Command execution
CLI
tenki sandbox exec --session <session-id> bash -lc 'go test ./...'
tenki sandbox exec --session <session-id> --timeout 2m bash -lc 'npm ci && npm test'CLI output includes:
- streamed stdout and stderr
- final status, exit code, and duration
- the execution ID
Go SDK
result, err := session.Exec(
ctx,
"bash",
tenkisandbox.WithArgs("-lc", "echo $APP_ENV && make test"),
tenkisandbox.WithEnv("APP_ENV", "ci"),
tenkisandbox.WithTimeout(2*time.Minute),
)
if err != nil {
log.Fatal(err)
}
if !result.Status.IsSuccess() {
log.Fatalf("failed: exit=%d stderr=%s", result.ExitCode, result.StderrString())
}Exec options: WithArgs, WithTimeout, WithEnv, WithEnvs.
Result helpers: result.StdoutString(), result.StderrString(), result.Status.IsSuccess(), IsFailed(), IsTimedOut().
TypeScript SDK
// One-shot
const result = await session.exec("npm", {
args: ["test"],
timeoutMs: 60_000,
onOutput: (chunk) => process.stdout.write(chunk.data),
});
// Or stream explicitly
const stream = await session.stream("npm", { args: ["test"] });
for (;;) {
const chunk = await stream.next();
if (!chunk) break;
process.stdout.write(chunk.data);
}
await stream.wait();File operations
CLI
# inline
tenki sandbox write --session <session-id> --path /workspace/app.env --data 'PORT=3000'
# from a local file
tenki sandbox write --session <session-id> --path /workspace/config.json --data-file ./config.json
# from stdin
cat ./local-file.txt | tenki sandbox write --session <session-id> --path /workspace/input.txt
# read to stdout
tenki sandbox read --session <session-id> --path /workspace/config.json
# read into a local file
tenki sandbox read --session <session-id> --path /workspace/build.log --out ./build.logSDK
err = session.WriteFile(ctx, "/workspace/hello.txt", []byte("hello"))
data, err := session.ReadFile(ctx, "/workspace/hello.txt")await session.writeFile("/tmp/config.json", '{"key": "value"}');
const data = await session.readFile("/tmp/config.json");Port exposure and networking
Each session has independent inbound and outbound settings:
allow_outbound=truelets the guest make outbound network callsallow_inbound=trueenables inbound exposure workflows
CLI
tenki sandbox expose --session <session-id> --port 3000
tenki sandbox ports --session <session-id>
tenki sandbox unexpose --session <session-id> --port 3000SDK
port, err := session.ExposePort(ctx, 3000)
ports, err := session.ListExposedPorts(ctx)
err = session.UnexposePort(ctx, 3000)const port = await session.exposePort(3000, { ttlMs: 3600_000 });
console.log(port.previewUrl);Port numbers must be between 1 and 65535.
SSH access
Direct CLI SSH
tenki sandbox ssh --session <session-id>Useful flags: --user (default root), --identity-file, --batch-mode, --connect-timeout, --strict-host-key-checking.
You can pass standard SSH arguments after the session ID:
tenki sandbox ssh <session-id> -L 8080:127.0.0.1:8080Managed SSH config
Install a managed entry into your local SSH config so you can use friendly aliases:
tenki sandbox ssh config install
tenki sandbox ssh config status
tenki sandbox ssh config uninstallAfter install:
ssh sbx-<session-uuid>Authorized keys
Add keys at create time:
tenki sandbox create \
--authorized-key 'ssh-ed25519 AAAA...' \
--authorized-keys-file ~/.ssh/authorized_keysReplace the set later:
tenki sandbox ssh-keys set --session <session-id> --keys-file ~/.ssh/authorized_keyssession, err := client.Create(
ctx,
tenkisandbox.WithSSHKeys([]string{"ssh-ed25519 AAAA..."}),
)
err = session.UpdateSSHAuthorizedKeys(ctx, []string{"ssh-ed25519 AAAA..."})Raw SSH transport
The Go SDK exposes the underlying tunnel for integration with your own SSH tooling:
conn, err := session.SSH(ctx)
defer conn.Close()const conn = await session.ssh();
conn.write("ls -la\n");
conn.onData((data) => process.stdout.write(data));
conn.close();Session metadata
Use --metadata key=value (CLI) or WithMetadata (SDK) to tag sessions with arbitrary string pairs, useful for filtering in dashboards, billing attribution, or tying a session to an upstream job ID.
tenki sandbox create --metadata owner=alice --metadata job=ci-1234Metadata is opaque to the service; it is for your bookkeeping.