Sandbox

SDK Reference

Programmatic reference for Tenki Sandbox covering installation, authentication, client options, identity discovery, OpenCode integration, and error types.

Tenki Sandbox provides two official SDKs:

  • Go SDK: github.com/TenkiCloud/tenki-sdk-go/sandbox
  • TypeScript SDK: published as @tenki/sandbox

Both SDKs wrap the same public service contract (tenki.sandbox.v1), so you get identical functionality regardless of which language you choose.

Install

Go

go get github.com/TenkiCloud/tenki-sdk-go/sandbox

TypeScript

npm install @tenki/sandbox

Authenticate

Token forms

The service accepts three token forms:

  • API key: sent as Authorization: Bearer <token> when the token starts with tk_
  • Ory session token: sent as X-Session-Token when the token starts with ory_st_
  • browser session token: sent as Cookie: ory_kratos_session=<token> otherwise

For most external integrations, use an API key.

Resolution order

Go SDK: WithAuthToken() > TENKI_API_KEY env var > error.

TypeScript SDK: options.authToken > TENKI_AUTH_TOKEN env var > TENKI_API_KEY env var.

Base URL: WithBaseURL() / options.baseUrl > TENKI_API_URL env var > https://api.tenki.cloud.

Create a client

Go

import tenkisandbox "github.com/TenkiCloud/tenki-sdk-go/sandbox"

// Zero-config: reads TENKI_API_KEY and TENKI_API_URL.
client, err := tenkisandbox.New()

// Explicit:
client, err := tenkisandbox.New(
  tenkisandbox.WithAuthToken("tk_your_api_key"),
  tenkisandbox.WithBaseURL("https://api.example.com"),
)
defer client.Close()

Useful client options:

OptionDescriptionDefault
WithAuthToken(token)API authentication tokenTENKI_API_KEY env
WithBaseURL(url)Sandbox service endpointhttps://api.tenki.cloud
WithHTTPTimeout(d)HTTP timeout30s
WithHTTPClient(c)Custom *http.Clientauto-created
WithConnectClientOptions(...)Additional Connect client optionsnone

TypeScript

import { TenkiSandbox } from "@tenki/sandbox";

const sandbox = new TenkiSandbox(); // env-driven

// Or explicit:
const sandbox = new TenkiSandbox({
  authToken: "tk_...",
  baseUrl: "https://api.tenki.cloud",
});

Defaults

Defaults applied by Create when you do not override them:

  • inbound: false
  • outbound: true
  • cpu: 2
  • memory: 4096 MB

Validation: cpu_cores 1..16, memory_mb 512..65536, volume size 1 MiB to 100 GiB.

Identity and workspaces

Use WhoAmI to discover the authenticated owner and visible workspaces. Many APIs (volumes, templates) require a workspace ID.

identity, err := client.WhoAmI(ctx)
fmt.Printf("owner: %s/%s\n", identity.OwnerType, identity.OwnerID)
for _, ws := range identity.Workspaces {
  fmt.Printf("workspace: %s (%s)\n", ws.Name, ws.ID)
}

OpenCode integration

Sessions can run OpenCode inside the VM for AI-driven workflows. Enable it with WithOpenCode(true) at create time, or pass WithOpenCodeProvider(...) to wire in your provider keys.

Single run

result, err := session.OpenCode.Run(
  ctx,
  "Review this repository and summarize risks.",
  tenkisandbox.WithModel("anthropic/claude-sonnet-4-5"),
  tenkisandbox.WithTimeout(5*time.Minute),
  tenkisandbox.WithOnEvent(func(ev tenkisandbox.Event) {
    log.Printf("[%s] %s", ev.Type, ev.Data)
  }),
)
const run = await session.openCode.run("Fix the failing tests", {
  model: "claude-sonnet-4-20250514",
  onEvent: (e) => console.log(`[${e.type}] ${e.data}`),
});
console.log(`Cost: $${run.totalCost}`);

Long-lived instance

Start an OpenCode instance once and run many prompts against it:

instance, err := session.OpenCode.StartInstance(
  ctx,
  tenkisandbox.WithInstanceWorkDir("/workspace/repo"),
  tenkisandbox.WithInstanceModel("anthropic/claude-sonnet-4-5"),
)

result, err := instance.Run(ctx, "Fix the failing tests")
const instance = await session.openCode.startInstance({
  workDir: "/workspace",
  model: "claude-sonnet-4-20250514",
});
const result = await instance.run("Add unit tests");

Abort the active OpenCode session:

err = session.OpenCode.Abort(ctx)

Git helpers

The SDK exposes structured Git operations on a session.

out, err := session.Git.Clone(ctx, "https://github.com/octocat/Hello-World.git", tenkisandbox.GitCloneParams{
  Directory: "/workspace/repo",
  Depth:     1,
})
out, err = session.Git.Checkout(ctx, "main", tenkisandbox.GitCheckoutParams{})
out, err = session.Git.FetchPR(ctx, 123, tenkisandbox.GitFetchPRParams{
  Directory: "/workspace/repo",
})
await session.git.clone("https://github.com/org/repo", { depth: 1 });
await session.git.checkout({ branch: "feature" });
const diff = await session.git.diff({});
const log = await session.git.log({ maxCount: 10 });
await session.git.fetchPR({ remote: "origin", prNumber: 42 });

You can also inject a GitHub token at session create time with WithGitHubToken(token) so private clones work without provisioning credentials inside the guest.

Timeout constants (Go)

  • DefaultSessionCreateTimeout
  • DefaultSnapshotCreateTimeout
  • DefaultRestoreTimeout
  • DefaultExecTimeout

Size constants (TypeScript)

import { GB, GiB, KB, KiB, MB, MiB, TB, TiB } from "@tenki/sandbox";

const tenGiB = 10 * GiB;

The Go SDK exposes the same scale factors (tenkisandbox.GB, MiB, etc.).

Errors

The Go SDK maps service errors to typed values. Common ones:

ErrorMeaning
ErrSessionNotFoundsession ID is unknown
ErrSessionExpiredsession passed its MaxDuration
ErrSessionTerminatedsession is TERMINATED
ErrInvalidStateoperation invalid for current state
ErrCommandTimeoutexec exceeded its timeout
ErrUnauthorizedbad/missing auth token
ErrPermissionDeniedauthorized but lacks permission
ErrQuotaExceededworkspace hit a resource quota
ErrPortLimitExceededtoo many exposed ports on this session
ErrInboundDisabledinbound network not enabled
ErrSSHUnavailableSSH endpoint not ready
ErrRateLimitedback off and retry
ErrVolumeNotFoundvolume ID is unknown
ErrVolumeInUsevolume already attached
ErrVolumeLimitExceededworkspace volume quota
ErrSnapshotNotFoundsnapshot ID is unknown
ErrSnapshotFailedsnapshot creation failed
ErrTemplateNotFoundtemplate ID is unknown
ErrTemplateExiststemplate name conflict
ErrTemplateBuildNotFoundbuild ID is unknown
ErrTemplateBuildFailedbuild failed; see error/log_tail
ErrTemplateBuildInProgressbuild still running
if errors.Is(err, tenkisandbox.ErrTemplateBuildInProgress) {
  // retry later
}

The TypeScript SDK exposes equivalent typed errors that all extend SandboxError:

import { CommandTimeoutError, QuotaExceededError, SessionNotFoundError } from "@tenki/sandbox";

try {
  await session.exec("sleep", { args: ["999"], timeoutMs: 1000 });
} catch (err) {
  if (err instanceof CommandTimeoutError) {
    console.log("Command timed out");
  }
}

Advanced API surface

The public service contract also exposes lower-level RPCs that the convenience SDKs don't wrap:

  • GetArtifactUploadUrl, GetArtifactDownloadUrl: used for full template build log artifacts
  • ListWorkspaceSandboxes
  • PauseSession, ResumeSession: present in the protobuf, not always wrapped, may not be available in every deployment

If you need them, integrate directly with the protocol:

  • proto/tenki/sandbox/v1/sandbox.proto

Stick to the SDKs unless you need a raw RPC

The Go and TypeScript SDKs are the officially supported surface for Tenki Sandbox. Only drop down to raw Connect/gRPC when an RPC isn't yet wrapped by an SDK, and keep in mind that a wrapper may be added in a future release.

LinkedInProduct Hunt