# Zeish > Clone AI agent environments like git. Hardware-isolated MicroVM sandboxes with instant boot from snapshots: snapshot, branch, and fork reproducible agent workloads. For focused agent retrieval, use https://zei.sh/llms.txt and its API, CLI, MCP, and ComputeSDK context indexes. ## Start here ### Install and authenticate Source: https://zei.sh/docs/install Set up the SDK, CLI, MCP, API key, and production host in one place. #### Install the ComputeSDK package Install the first-party TypeScript package for the standard provider and Zeish session client. The package is @zeish/computesdk-provider. It includes the portable ComputeSDK provider, a typed public API client, and the richer Zeish sandbox session client. Use Node.js with a server-side environment. Keep ZEISH_API_KEY out of browser bundles and client-side source. ``` npm install @zeish/computesdk-provider ``` #### Install zeishctl Install a published zeishctl release from the public download service. The CLI binary is named zeishctl and also accepts the aliases zctl and zc. Get the current version and platform download URLs from https://api.zei.sh/api/v1/cli/versions. The public service provides Linux x86_64, macOS arm64, macOS x86_64, and Windows x86_64 archives. ``` curl -fsSL https://api.zei.sh/api/v1/cli/versions zeishctl --help ``` #### Connect an MCP client Connect Claude, ChatGPT, or another MCP client over Streamable HTTP. MCP is hosted by Zeish. There is no local package to install for the hosted connection. Use the endpoint below for headless API-key access. Interactive clients can use OAuth Authorization Code with PKCE and dynamic client registration. ``` { "mcpServers": { "zeish": { "url": "https://api.zei.sh/api/v1/mcp", "headers": { "Authorization": "Bearer zeish_live_..." } } } } ``` #### Create an API key Create a server credential for the REST API, SDK, CLI, or headless MCP. Create a standalone API key from the dashboard Limits page. The full value is shown once, stored as a hash, and starts with zeish_live_. Choose only the permissions the integration needs. Public API permissions include MACHINE_READ, MACHINE_CREATE, MACHINE_UPDATE, MACHINE_DELETE, and ORG_MANAGE. API keys are bearer credentials. Store them in a secret manager or environment variable. Never put one in a browser bundle, source control, logs, or a command copied into a shared shell history. ``` export ZEISH_API_KEY="zeish_live_..." ``` #### Configure the production host Use the correct base URL in each client. The REST API base is https://api.zei.sh/api/v1. The CLI base host is https://api.zei.sh and it adds the versioned path for API calls. Set the SDK baseUrl explicitly. The package default is not the production Zeish host. ``` const baseUrl = "https://api.zei.sh/api/v1"; const cliHost = "https://api.zei.sh"; ``` ### Getting started Source: https://zei.sh/docs/getting-started Understand Zeish, run a sandbox, authenticate, and choose your interface. #### How Zeish works A simple control plane for isolated, disposable MicroVM sandboxes. Zeish has two planes. The control plane owns organizations, API keys, templates, sandboxes, resources, permissions, and lifecycle state. The data plane is the sandbox runtime, reached through short-lived credentials issued by the control plane. A sandbox runs in a hardware-isolated MicroVM with its own Linux kernel. Firecracker is the default driver. Cloud Hypervisor is available when a node advertises it. The guest sandboxd service handles commands, files, terminal sessions, screenshots, and desktop actions without exposing the host control process. Creates and lifecycle actions are asynchronous. Create a sandbox, wait for running, request data-plane access, then run work. Snapshots and clones let you fan out from a prepared environment instead of repeating setup. #### Run your first sandbox Install the TypeScript package, create a sandbox, run a command, and clean up. Create a standalone API key in the dashboard, choose a template, and set ZEISH_API_KEY. The public API currently provisions in the bremen region. A templateId is required unless you configure defaultTemplateId in the SDK. The session client waits for the sandbox data plane and refreshes its short-lived credential as needed. Use it for agent workflows that need commands, files, desktop actions, and lifecycle operations from one object. The SDK default host is not the production Zeish host. Set baseUrl explicitly so the client calls https://api.zei.sh/api/v1. ``` npm install @zeish/computesdk-provider import { createZeishSandboxClient } from "@zeish/computesdk-provider"; const client = createZeishSandboxClient({ apiKey: process.env.ZEISH_API_KEY!, defaultTemplateId: process.env.ZEISH_TEMPLATE_ID!, baseUrl: "https://api.zei.sh/api/v1", }); const sandbox = await client.create({ name: "first-run" }); await sandbox.waitForAccess(); const result = await sandbox.run("uname -a"); console.log(result.stdout); await sandbox.destroy(); ``` #### Authentication Use API keys for automation or OAuth for interactive clients. Create API keys from the dashboard's Limits page. A new key is shown once, stored as a hash, and starts with zeish_live_. Send it as X-API-Key for REST. Authorization: Bearer also works for public API requests. New keys are standalone and use the organization and user that created them. API keys are server credentials. Keep them out of browser bundles, logs, shell history, and source control. Use the smallest permission set that fits the integration: MACHINE_READ, MACHINE_CREATE, MACHINE_UPDATE, MACHINE_DELETE, and ORG_MANAGE. ``` export ZEISH_API_KEY="zeish_live_..." curl https://api.zei.sh/api/v1/public/sandboxes -H "X-API-Key: $ZEISH_API_KEY" ``` #### Use the CLI Manage sandboxes and use the data plane from zeishctl, zctl, or zc. Install a published release from https://api.zei.sh/api/v1/cli/versions. The binary also supports the aliases zctl and zc. Use auth login for browser OAuth with PKCE, or set ZEISH_API_KEY for automation. The default API URL is https://api.zei.sh. OAuth state is stored under the zeish config directory with mode 0600. Commands return JSON. The api escape hatch accepts any authenticated method and path. Data-plane commands obtain a scoped access token automatically, then use sandboxd gRPC or HTTP. Secret values come from stdin by default or from --value-file. ``` zeishctl auth login zeishctl sandboxes create agent-run --template-id TEMPLATE_ID zeishctl sandboxes exec SANDBOX_ID -- echo hello zeishctl sandboxes files SANDBOX_ID read /workspace/output.json zeishctl sandboxes shell SANDBOX_ID zeishctl sandboxes ssh SANDBOX_ID zeishctl sandboxes snapshots SANDBOX_ID create before-deploy zeishctl secrets create API_TOKEN --provider-reference secret/data/api-token < token.txt zeishctl completion zsh > ~/.zsh/completions/_zeishctl zeishctl upgrade ``` ## Build ### Sandboxes Source: https://zei.sh/docs/sandboxes Create, operate, snapshot, and clone isolated MicroVM environments. #### Create and configure sandboxes Choose a template, resources, storage, network, ingress, and secret injection policy. POST /public/sandboxes requires name and either templateId or template. CPU is measured in cores and memory in MB. Omitted values use the selected template. The region is bremen today. Drivers are firecracker and cloud-hypervisor, with firecracker as the default. Attach existing volumes with volumeIds or create and attach them in one request with createVolumes. Attach an organization network with networkId. Labels are useful for run correlation; metadata is accepted as create-time input and merged into labels. A create request can declare raw_l4 ingress for TCP or UDP and can configure secretInjection. The create operation is idempotent when you reuse the same Idempotency-Key for the same logical request. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes -H "X-API-Key: $ZEISH_API_KEY" -H "Idempotency-Key: run-123" -H "Content-Type: application/json" -d '{ "name": "agent-workspace", "templateId": "TEMPLATE_ID", "cpu": 4, "memory": 4096, "volumeIds": ["VOLUME_ID"], "ingress": [ {"mode":"raw_l4","protocol":"tcp","internalPort":3000} ] }' ``` #### Sandbox lifecycle Create, inspect, start, pause, resume, stop, kill, clone, and delete sandboxes. Sandbox statuses include initialized, pending, running, pausing, paused, resuming, stopping, stopped, suspending, cloning, destroying, failed, and destroyed. Actions are asynchronous. Refresh the detail response before using a runtime-dependent feature. Creating a sandbox normally starts its runtime. createAndStartSandbox handles the common agent path: create, wait for running, destroy a failed attempt, and retry. Its defaults are three attempts, a 90 second ready timeout, and a two second poll interval. Use pause and resume when you want to keep state. Stop releases the running runtime while preserving the sandbox record. kill force-stops a stuck runtime. Clone creates a new sandbox from an existing one. Delete is permanent. ``` import { createZeishApi, createAndStartSandbox, isTerminalSandboxStatus, } from "@zeish/computesdk-provider"; const api = createZeishApi({ apiKey: process.env.ZEISH_API_KEY!, baseUrl: "https://api.zei.sh/api/v1", }); const sandbox = await createAndStartSandbox(api, { name: "agent-run", templateId: process.env.ZEISH_TEMPLATE_ID!, }); const current = await api.getSandbox(sandbox.id); if (isTerminalSandboxStatus(current.status)) { throw new Error("sandbox is no longer usable"); } await api.pauseSandbox(sandbox.id); await api.resumeSandbox(sandbox.id); ``` #### Snapshots and clones Save a ready environment and branch new sandboxes from it. A snapshot captures the sandbox runtime state for reuse. Create it only after the sandbox has a live runtime. A new snapshot is initially pending and becomes ready before it can be used as a source. The public REST API and first-party session client support create, list, and delete for sandbox-scoped snapshots. The ComputeSDK standard interface supports snapshot creation, but its list and delete methods are not sandbox-scoped, so use the Zeish API for those operations. Snapshot and sandbox deletion are irreversible. Use descriptive names such as before-deploy or dependencies-v4, and keep a stable source sandbox when you need repeatable fan-out. ``` const snapshot = await sandbox.createSnapshot("dependencies-v4"); const snapshots = await sandbox.listSnapshots(); await sandbox.deleteSnapshot(snapshot.id); // REST routes // POST /public/sandboxes/:sandboxId/snapshots // GET /public/sandboxes/:sandboxId/snapshots // DELETE /public/sandboxes/:sandboxId/snapshots/:snapshotId ``` ### Agent tools Source: https://zei.sh/docs/agent-tools Run commands, manage files, control desktops, and connect MCP clients. #### Get data-plane access Mint a short-lived credential for commands, files, and desktop actions. GET /public/sandboxes/:sandboxId/exec-access returns sandboxUrl, sandboxRpcUrl, token, and expiresAt. The token is scoped to one sandbox and lasts about ten minutes. It requires MACHINE_UPDATE because it grants command execution and filesystem mutation. Use sandboxUrl for sandboxd HTTP operations and sandboxRpcUrl for gRPC ExecStream and terminal operations. Send Authorization: Bearer . Never persist or log this token. Request a new one when it expires. Use GET /terminal-url or the CLI shell command for a browser terminal. The first-party session client calls getAccess and refreshes the credential. The ComputeSDK provider does the same for runCommand and filesystem methods. ``` const access = await api.getExecAccess(sandboxId); const response = await fetch( access.sandboxUrl + "/files/stat?path=/workspace", { headers: { Authorization: "Bearer " + access.token } }, ); console.log(await response.json()); ``` #### Run commands Execute one-shot or background commands in the sandbox. Commands run through sandboxd's authenticated gRPC service. The stream emits stdout, stderr, and a final exit event. Set a timeout, working directory, and environment explicitly in agent code, and treat command output as untrusted data. The session client exposes run(command). The ComputeSDK provider exposes compute.sandbox.runCommand(sandbox, command, options). A background command returns after the execution is accepted instead of waiting for an exit event. The public control API does not proxy command bytes. It only mints the scoped data-plane credential, which keeps high-volume runtime traffic away from the control plane. ``` const result = await sandbox.run("python agent.py", { workingDirectory: "/workspace", timeoutMs: 120000, environment: { RUN_ID: runId }, onStdout: chunk => process.stdout.write(chunk), onStderr: chunk => process.stderr.write(chunk), }); if (result.exitCode !== 0) { throw new Error("agent failed with exit code " + result.exitCode); } ``` #### Read and write files Use the sandbox data plane for safe, scoped filesystem operations. sandboxd exposes read, write, directory listing, mkdir, stat, exists, and remove operations below the sandbox file root. Paths are checked for traversal and symlink escapes. Removing a missing path is idempotent, while removing the file root is rejected. The first-party session client exposes readText, writeText, makeDirectory, listDirectory, stat, exists, and remove. The ComputeSDK provider maps readFile, writeFile, mkdir, readdir, exists, and remove to the same data plane. The CLI adds rename plus streaming upload and download. Use a volume or snapshot when data must outlive the sandbox runtime. ``` await sandbox.files.makeDirectory("/workspace/results"); await sandbox.files.writeText( "/workspace/results/status.json", JSON.stringify({ ok: true }), ); const file = await sandbox.files.readText("/workspace/results/status.json"); const entries = await sandbox.files.listDirectory("/workspace/results"); console.log(file, entries); ``` #### Control a desktop Take screenshots and send native Wayland input to a desktop sandbox. The session client exposes screenshot, move, click, scroll, type, and key actions. These calls use the same short-lived sandbox token as commands and files. Use a template that includes the desktop agent when GUI automation is needed. Desktop actions use native Wayland endpoints through sandboxd. X11 and Xwayland are not required. Screenshots are returned as a Buffer in Node.js. Desktop automation is available on the first-party session client. For portable ComputeSDK code, combine the standard provider with the session client when the workflow also needs display actions. ``` const png = await sandbox.desktop.screenshot(); await sandbox.desktop.move(340, 210); await sandbox.desktop.click({ x: 340, y: 210 }); await sandbox.desktop.type("hello"); await sandbox.desktop.key("ENTER"); ``` #### Use Zeish MCP Connect Claude, ChatGPT, or any MCP client to Zeish over Streamable HTTP. The MCP endpoint is https://api.zei.sh/api/v1/mcp. It uses Streamable HTTP. MCP clients send Authorization: Bearer with either a zeish_live_ API key or an OAuth access token. Headless clients can use an API key directly. Interactive connectors use OAuth Authorization Code with PKCE and dynamic client registration. OAuth metadata is available at /api/v1/.well-known/oauth-authorization-server. The flow supports S256 and refresh tokens. The organization tool set covers sandbox create, list, get, data-plane access, terminal URLs, SSH-key sync, previews, logs, events, lifecycle, deletion, snapshots, templates, volumes, networks, secrets, SSH keys, and whoami. Superadmins also get user, organization, node, sandbox, usage, billing, suspension, and credit tools. Call tools/list to inspect the live schema. ``` { "mcpServers": { "zeish": { "url": "https://api.zei.sh/api/v1/mcp", "headers": { "Authorization": "Bearer zeish_live_..." } } } } ``` ## Resources ### Templates, volumes, networks, and SSH keys Source: https://zei.sh/docs/resources Manage the resources sandboxes depend on. Templates are read-only through the public API. List or fetch a template to discover its image, CPU, memory, machine kind, and declared ingress. Global templates are visible to all organizations. Organization templates are visible only to their organization. Volumes are persistent organization resources with a name, region, and size in GB. Networks are organization-scoped logical networks that can be attached to sandboxes with networkId. Both resources use cursor pagination and require ORG_MANAGE. SSH keys are user resources. Register a public key before creating a sandbox, then use sandbox SSH-key sync after changing the account key set. The sandbox detail includes the SSH service once its runtime is ready. ``` GET /public/templates GET /public/templates/:templateId POST /public/volumes GET /public/volumes?limit=20&cursor=... GET /public/volumes/:volumeId DELETE /public/volumes/:volumeId POST /public/networks GET /public/networks?limit=20&cursor=... GET /public/networks/:networkId DELETE /public/networks/:networkId GET /public/ssh-keys POST /public/ssh-keys DELETE /public/ssh-keys/:keyId ``` ## Reference ### Public API Source: https://zei.sh/docs/api The complete versioned REST API, organized by resource and operation. #### REST API reference The versioned public control-plane API at https://api.zei.sh/api/v1. The machine-readable contract is available at /api/docs-json and the interactive Swagger explorer is at /api/docs. Public routes use /public and authenticate with X-API-Key or Bearer. The API returns structured errors under an error object with code, message, requestId, and optional details. List endpoints return data and nextCursor. Use limit and cursor for sandboxes, templates, volumes, and networks. Mutation requests should send Idempotency-Key and reuse it when retrying the same logical request. The complete public route map is below. The clone, secret grant, and secret lease routes are included because they are part of the live controller surface even when an older contract snapshot does not list every advanced route. ``` # Resources GET /public/templates GET /public/templates/:templateId GET /public/ssh-keys POST /public/ssh-keys DELETE /public/ssh-keys/:keyId POST /public/volumes GET /public/volumes GET /public/volumes/:volumeId DELETE /public/volumes/:volumeId POST /public/networks GET /public/networks GET /public/networks/:networkId DELETE /public/networks/:networkId # Sandboxes POST /public/sandboxes GET /public/sandboxes GET /public/sandboxes/:sandboxId PATCH /public/sandboxes/:sandboxId DELETE /public/sandboxes/:sandboxId POST /public/sandboxes/:sandboxId/clone GET /public/sandboxes/:sandboxId/exec-access GET /public/sandboxes/:sandboxId/terminal-url POST /public/sandboxes/:sandboxId/preview-codes POST /public/sandboxes/:sandboxId/tunnel-access POST /public/sandboxes/:sandboxId/ports PUT /public/sandboxes/:sandboxId/ports/:port/share POST /public/sandboxes/:sandboxId/ssh-keys/sync GET /public/sandboxes/:sandboxId/logs GET /public/sandboxes/:sandboxId/events POST /public/sandboxes/:sandboxId/{start|pause|resume|stop|kill} POST /public/sandboxes/:sandboxId/snapshots GET /public/sandboxes/:sandboxId/snapshots DELETE /public/sandboxes/:sandboxId/snapshots/:snapshotId # Secrets GET /public/secrets POST /public/secrets GET /public/secrets/:secretId PATCH /public/secrets/:secretId DELETE /public/secrets/:secretId POST /public/secrets/:secretId/grants POST /public/secrets/:sandboxId/leases ``` #### List templates List templates visible to the authenticated organization. GET /public/templates?limit=20&cursor=... Permission: MACHINE_READ. Use this to discover template IDs and their default runtime resources before creating a sandbox. Response: A paginated data array and nextCursor. The default limit is 20 and the maximum is 100. ``` curl -X GET https://api.zei.sh/api/v1/public/templates?limit=20&cursor=... \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Get a template Fetch one visible sandbox template by ID. GET /public/templates/:templateId Permission: MACHINE_READ. The response includes the image, CPU, memory, machine kind, and declared ingress settings. ``` curl -X GET https://api.zei.sh/api/v1/public/templates/:templateId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### List SSH keys List public SSH keys registered by the authenticated user. GET /public/ssh-keys Permission: MACHINE_READ. Keys are used for SSH access to sandboxes created for the user. Response: An array of key metadata and public key material. Private keys are never stored. ``` curl -X GET https://api.zei.sh/api/v1/public/ssh-keys \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Register an SSH key Register a public key for future sandbox access. POST /public/ssh-keys Permission: MACHINE_UPDATE. Register the public half of a key before creating a sandbox or sync it into an existing runtime. Request body: {"name":"workstation","publicKey":"ssh-ed25519 AAAA..."} Response: The created key with id, name, publicKey, isManaged, and createdAt. ``` curl -X POST https://api.zei.sh/api/v1/public/ssh-keys \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"name":"workstation","publicKey":"ssh-ed25519 AAAA..."}' ``` #### Delete an SSH key Remove a registered public SSH key. DELETE /public/ssh-keys/:id Permission: MACHINE_UPDATE. Deletion affects future provisioning. Call the sandbox SSH key sync operation to update an existing runtime. Response: {"ok":true} ``` curl -X DELETE https://api.zei.sh/api/v1/public/ssh-keys/:id \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Create a volume Create persistent organization storage. POST /public/volumes Permission: ORG_MANAGE. Volumes are region-scoped and survive sandbox stop and runtime replacement. Request body: {"name":"agent-data","slug":"agent-data","region":"bremen","sizeGb":20} Response: The created volume with id, organizationId, name, slug, region, sizeGb, and timestamps. ``` curl -X POST https://api.zei.sh/api/v1/public/volumes \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"name":"agent-data","slug":"agent-data","region":"bremen","sizeGb":20}' ``` #### List volumes List persistent volumes in the organization. GET /public/volumes?limit=20&cursor=... Permission: ORG_MANAGE. Use limit and the returned nextCursor to walk the collection. Response: A paginated data array and nextCursor. Limits range from 1 through 100. ``` curl -X GET https://api.zei.sh/api/v1/public/volumes?limit=20&cursor=... \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Get a volume Fetch one organization volume by ID. GET /public/volumes/:volumeId Permission: ORG_MANAGE. Use the volume ID in sandbox create or update requests. Response: The volume resource, including its region and sizeGb. ``` curl -X GET https://api.zei.sh/api/v1/public/volumes/:volumeId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Delete a volume Delete one organization volume. DELETE /public/volumes/:volumeId Permission: ORG_MANAGE. Confirm that no sandbox still depends on the volume before deleting it. Response: The deleted volume resource. ``` curl -X DELETE https://api.zei.sh/api/v1/public/volumes/:volumeId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Create a network Create an organization-scoped logical network. POST /public/networks Permission: ORG_MANAGE. Attach the network to sandboxes with networkId. Request body: {"name":"agent-network","slug":"agent-network","region":"bremen"} Response: The created network with id, organizationId, name, slug, region, and createdAt. ``` curl -X POST https://api.zei.sh/api/v1/public/networks \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"name":"agent-network","slug":"agent-network","region":"bremen"}' ``` #### List networks List logical networks in the organization. GET /public/networks?limit=20&cursor=... Permission: ORG_MANAGE. Use cursor pagination for stable iteration over organization networks. Response: A paginated data array and nextCursor. ``` curl -X GET https://api.zei.sh/api/v1/public/networks?limit=20&cursor=... \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Get a network Fetch one organization network by ID. GET /public/networks/:networkId Permission: ORG_MANAGE. Use the network ID in sandbox create or update requests. Response: The network resource. ``` curl -X GET https://api.zei.sh/api/v1/public/networks/:networkId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Delete a network Delete an organization network. DELETE /public/networks/:networkId Permission: ORG_MANAGE. Remove the network from dependent sandboxes before deleting it. Response: The deleted network resource. ``` curl -X DELETE https://api.zei.sh/api/v1/public/networks/:networkId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Create a sandbox Provision a MicroVM sandbox with optional resources, ingress, and secret injection. POST /public/sandboxes Permission: MACHINE_CREATE. The request needs name and templateId or template. CPU is cores, memory is MB, and omitted resources use the template defaults. Request body: {"name":"agent-run","templateId":"TEMPLATE_ID","cpu":4,"memory":4096,"region":"bremen","networkId":"NETWORK_ID","volumeIds":["VOLUME_ID"]} Response: The sandbox record. Provisioning is asynchronous and normally requests a running runtime. Reuse the same Idempotency-Key when retrying one logical create request. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"name":"agent-run","templateId":"TEMPLATE_ID","cpu":4,"memory":4096,"region":"bremen","networkId":"NETWORK_ID","volumeIds":["VOLUME_ID"]}' ``` #### List sandboxes List compact sandbox records in the organization. GET /public/sandboxes?limit=20&cursor=... Permission: MACHINE_READ. Use this for dashboards and polling. Fetch a detail record when you need enriched access information. Response: A paginated data array and nextCursor. ``` curl -X GET https://api.zei.sh/api/v1/public/sandboxes?limit=20&cursor=... \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Get sandbox details Fetch one sandbox with runtime and access URL enrichment. GET /public/sandboxes/:sandboxId Permission: MACHINE_READ. Use the detail response to decide whether a runtime-dependent operation is ready. Response: The sandbox record with current status and available runtime metadata. ``` curl -X GET https://api.zei.sh/api/v1/public/sandboxes/:sandboxId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Update a sandbox Change mutable sandbox configuration. PATCH /public/sandboxes/:sandboxId Permission: MACHINE_UPDATE. Update name, templateId, region, CPU, memory, networkId, volumeIds, or labels. Request body: {"name":"agent-run-final","cpu":6,"memory":8192,"labels":{"run":"2026-08-30"}} Response: The updated sandbox record. Set networkId to null to detach the current network. ``` curl -X PATCH https://api.zei.sh/api/v1/public/sandboxes/:sandboxId \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"name":"agent-run-final","cpu":6,"memory":8192,"labels":{"run":"2026-08-30"}}' ``` #### Delete a sandbox Permanently delete a sandbox. DELETE /public/sandboxes/:sandboxId Permission: MACHINE_DELETE. Deletion is a lifecycle action and is irreversible. Response: The lifecycle result for the delete request. ``` curl -X DELETE https://api.zei.sh/api/v1/public/sandboxes/:sandboxId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Clone a sandbox Create a new sandbox from an existing sandbox state. POST /public/sandboxes/:sandboxId/clone Permission: MACHINE_CREATE. Use clones for parallel agent branches and repeatable evaluation runs. Request body: {"name":"agent-run-copy"} Response: The new sandbox record. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/clone \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"name":"agent-run-copy"}' ``` #### Mint data-plane access Mint short-lived credentials for commands, files, and terminal sessions. GET /public/sandboxes/:sandboxId/exec-access Permission: MACHINE_UPDATE. The token is scoped to one sandbox and is valid for about ten minutes. Response: sandboxUrl, sandboxRpcUrl, token, and expiresAt. Send the token as Authorization: Bearer . Never log or persist it. ``` curl -X GET https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/exec-access \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Get a terminal URL Create a browser terminal URL for a sandbox. GET /public/sandboxes/:sandboxId/terminal-url Permission: MACHINE_READ. Open the returned URL in a browser to use the sandbox terminal. Response: A terminal URL and its expiration metadata. ``` curl -X GET https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/terminal-url \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Sync SSH keys Apply the user's current SSH keys to an existing sandbox runtime. POST /public/sandboxes/:sandboxId/ssh-keys/sync Permission: MACHINE_UPDATE. Call this after registering or removing a user SSH key. Response: The updated sandbox result. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/ssh-keys/sync \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Create a preview code Mint temporary HTTP browser or agent access to an exposed port. POST /public/sandboxes/:sandboxId/preview-codes Permission: MACHINE_READ. Use the browser handoff URL for a real browser and base_url with the bearer code for fetch, WebSocket, Playwright, or CDP. Request body: {"port":3000,"ttl_seconds":300,"path":"/health"} Response: url, handoff_url, base_url, code, and expires_at. TTL is 1 through 3600 seconds. Do not append paths to the browser handoff URL. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/preview-codes \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"port":3000,"ttl_seconds":300,"path":"/health"}' ``` #### Mint tunnel access Mint a short-lived WebSocket tunnel for exposed TCP ports. POST /public/sandboxes/:sandboxId/tunnel-access Permission: MACHINE_UPDATE. Use this for CDP, databases, and other protocols that do not work through an HTTP Host header. Request body: {"ttl_seconds":60} Response: ws_url, token, and expires_at. TTL is 1 through 3600 seconds. The default is 60 seconds. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/tunnel-access \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"ttl_seconds":60}' ``` #### Add a sandbox port Expose a TCP or UDP port from a sandbox. POST /public/sandboxes/:sandboxId/ports Permission: MACHINE_UPDATE. Declare raw L4 ingress with an internal port and optional external port and access policy. Request body: {"internalPort":3000,"externalPort":3000,"protocol":"tcp","accessPolicy":"org"} Response: The created port exposure. Ports must be between 1 and 65535. Access policy is org or public. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/ports \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"internalPort":3000,"externalPort":3000,"protocol":"tcp","accessPolicy":"org"}' ``` #### Change port access Change the access policy for an exposed port. PUT /public/sandboxes/:sandboxId/ports/:port/share Permission: MACHINE_UPDATE. Use org for organization-scoped credentials or public for unauthenticated access. Request body: {"policy":"public"} Response: The updated port exposure. ``` curl -X PUT https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/ports/:port/share \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"policy":"public"}' ``` #### List sandbox logs Read bounded captured output from a sandbox. GET /public/sandboxes/:sandboxId/logs?limit=100&source=app&service=... Permission: MACHINE_READ. Filter by source or service when diagnosing boot, memory, or application output. Response: A bounded log history. ``` curl -X GET https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/logs?limit=100&source=app&service=... \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### List sandbox events Read lifecycle events for a sandbox. GET /public/sandboxes/:sandboxId/events?limit=100 Permission: MACHINE_READ. Poll events to correlate asynchronous lifecycle transitions with an agent run. Response: Events with status, source, timestamp, and optional message. ``` curl -X GET https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/events?limit=100 \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Start a sandbox Request a sandbox runtime to start. POST /public/sandboxes/:sandboxId/start Permission: MACHINE_UPDATE. Use start after a sandbox is stopped and poll its detail or events until it is running. Response: The lifecycle result. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/start \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Pause a sandbox Pause a sandbox while preserving its state. POST /public/sandboxes/:sandboxId/pause Permission: MACHINE_UPDATE. Pause is useful when you want to preserve state without keeping the runtime active. Response: The lifecycle result. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/pause \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Resume a sandbox Resume a paused sandbox. POST /public/sandboxes/:sandboxId/resume Permission: MACHINE_UPDATE. Poll the sandbox detail until the runtime is running before requesting data-plane access. Response: The lifecycle result. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/resume \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Stop a sandbox Stop a sandbox runtime while retaining the sandbox record. POST /public/sandboxes/:sandboxId/stop Permission: MACHINE_UPDATE. Stop releases the active runtime and preserves the sandbox configuration. Response: The lifecycle result. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/stop \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Kill a sandbox Force-stop a stuck sandbox runtime. POST /public/sandboxes/:sandboxId/kill Permission: MACHINE_UPDATE. Use kill for a runtime that does not respond to a normal stop request. Response: The lifecycle result. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/kill \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Create a snapshot Capture a sandbox runtime state for reuse. POST /public/sandboxes/:sandboxId/snapshots Permission: MACHINE_UPDATE. Create snapshots from a live runtime and wait for the snapshot to become ready before using it. Request body: {"displayName":"dependencies-v4"} Response: The new snapshot with its status and ID. ``` curl -X POST https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/snapshots \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"displayName":"dependencies-v4"}' ``` #### List snapshots List snapshots belonging to one sandbox. GET /public/sandboxes/:sandboxId/snapshots Permission: MACHINE_READ. Use sandbox-scoped snapshot IDs for cleanup and repeatable fan-out. Response: An array of sandbox snapshots. ``` curl -X GET https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/snapshots \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Delete a snapshot Delete one sandbox snapshot. DELETE /public/sandboxes/:sandboxId/snapshots/:snapshotId Permission: MACHINE_UPDATE. Snapshot deletion is irreversible. Response: {"ok":true} ``` curl -X DELETE https://api.zei.sh/api/v1/public/sandboxes/:sandboxId/snapshots/:snapshotId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### List secret metadata List organization secrets without returning their values. GET /public/secrets Permission: ORG_MANAGE. Use this to discover secret IDs and metadata. Values are excluded from list responses. Response: Secret metadata only. ``` curl -X GET https://api.zei.sh/api/v1/public/secrets \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Create a secret Store a provider-backed plaintext or JSON secret. POST /public/secrets Permission: ORG_MANAGE. The current deployment uses Vault. Other provider values require that provider to be configured. Request body: {"name":"github-token","provider":"vault","providerReference":"secret/data/ci/github","providerKey":"token","format":"plaintext","value":"replace-me"} Response: The secret metadata. Values are audited when viewed or edited. Keep provider credentials out of sandbox records. ``` curl -X POST https://api.zei.sh/api/v1/public/secrets \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"name":"github-token","provider":"vault","providerReference":"secret/data/ci/github","providerKey":"token","format":"plaintext","value":"replace-me"}' ``` #### Get a secret Read one secret value and its metadata. GET /public/secrets/:secretId Permission: ORG_MANAGE. This operation is audited and returns no-store response headers. Response: The secret metadata and resolved value. Treat the response as sensitive data and do not log it. ``` curl -X GET https://api.zei.sh/api/v1/public/secrets/:secretId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Update a secret Update secret metadata or material. PATCH /public/secrets/:secretId Permission: ORG_MANAGE. Send only the fields that should change. Request body: {"name":"github-token-rotated","value":"replace-me","version":"v2","format":"plaintext"} Response: The updated secret metadata. ``` curl -X PATCH https://api.zei.sh/api/v1/public/secrets/:secretId \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"name":"github-token-rotated","value":"replace-me","version":"v2","format":"plaintext"}' ``` #### Delete a secret Remove a secret from its configured provider. DELETE /public/secrets/:secretId Permission: ORG_MANAGE. Deletion archives the secret metadata after provider removal. Response: The deletion result. ``` curl -X DELETE https://api.zei.sh/api/v1/public/secrets/:secretId \ -H "X-API-Key: $ZEISH_API_KEY" ``` #### Grant a secret to a sandbox Authorize a secret for environment or file delivery. POST /public/secrets/:secretId/grants Permission: ORG_MANAGE. Grant startup or command delivery to a specific sandbox and target. Request body: {"sandboxId":"SANDBOX_ID","target":"env","targetName":"GITHUB_TOKEN","mode":"command"} Response: The grant result. Environment names must match shell variable syntax. File targets must be below /run/secrets. ``` curl -X POST https://api.zei.sh/api/v1/public/secrets/:secretId/grants \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"sandboxId":"SANDBOX_ID","target":"env","targetName":"GITHUB_TOKEN","mode":"command"}' ``` #### Mint a secret lease Mint a short-lived lease for secret resolution. POST /public/secrets/:sandboxId/leases Permission: ORG_MANAGE. Leases are scoped to organization, sandbox, operation, secret IDs, expiry, and nonce. Request body: {"operation":"command"} Response: The short-lived lease claims. Operation is startup, command, or file. Do not persist lease tokens. ``` curl -X POST https://api.zei.sh/api/v1/public/secrets/:sandboxId/leases \ -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" \ -d '{"operation":"command"}' ``` ### CLI reference Source: https://zei.sh/docs/cli The complete zeishctl command tree, grouped by workflow. #### Log in with OAuth Authenticate zeishctl with browser OAuth and PKCE. Command: zeishctl auth login The CLI opens the authorization URL and stores the resulting tokens in its config directory with restrictive permissions. ``` zeishctl auth login zeishctl auth status ``` #### Use an API key Authenticate the CLI with an environment variable or stored key. Command: zeishctl auth api-key TOKEN ZEISH_API_KEY is preferred for automation. Use --store only when the local config should retain the key. An environment key is not written to disk. ``` export ZEISH_API_KEY="zeish_live_..." zeishctl sandboxes list zeishctl auth api-key zeish_live_... --store ``` #### Inspect or clear CLI auth Check the active auth method or remove stored OAuth tokens. Command: zeishctl auth status | zeishctl auth logout status reports whether the CLI uses an API key or OAuth. logout clears stored access and refresh tokens. ``` zeishctl auth status zeishctl auth logout ``` #### List templates, volumes, networks, and organizations Discover the resources available to the current identity. Command: zeishctl templates | volumes | networks | orgs These commands return the authenticated API response as formatted JSON. Volumes and networks are organization resources and require the matching permission. ``` zeishctl templates zeishctl volumes zeishctl networks zeishctl orgs ``` #### List or read secrets Inspect secret metadata or read one audited value. Command: zeishctl secrets list | get SECRET_ID list never includes values. get returns the value and is audited. Treat the output as sensitive. ``` zeishctl secrets list zeishctl secrets get SECRET_ID ``` #### Create, update, or delete secrets Manage Vault-backed secrets without putting material in shell history. Command: zeishctl secrets create | update | delete Create and update read secret material from stdin by default. Use --value-file for a file. --value-unsafe exposes the value in argv and shell history and should be avoided. ``` printf '%s' "$GITHUB_TOKEN" | zeishctl secrets create github-token \ --provider-reference secret/data/ci/github zeishctl secrets update SECRET_ID --name github-token-v2 < token.txt zeishctl secrets delete SECRET_ID ``` #### Manage SSH keys Register, list, and revoke public keys used by sandboxes. Command: zeishctl ssh-keys list | add | remove add reads ~/.ssh/id_ed25519.pub or ~/.ssh/id_rsa.pub unless --path is supplied. Only public key material is uploaded. ``` zeishctl ssh-keys list zeishctl ssh-keys add workstation --path ~/.ssh/id_ed25519.pub zeishctl ssh-keys remove KEY_ID ``` #### List and create sandboxes Provision a sandbox with a template and optional resource overrides. Command: zeishctl sandboxes list | create create requires a name and --template-id. CPU is measured in cores, memory in MB, and region defaults to bremen. ``` zeishctl sandboxes list zeishctl sandboxes create agent-run \ --template-id TEMPLATE_ID \ --cpu 4 \ --memory 4096 ``` #### Inspect or clone a sandbox Fetch sandbox detail or create a branch from an existing sandbox. Command: zeishctl sandboxes get | clone clone accepts an optional --name and returns the new sandbox record. ``` zeishctl sandboxes get SANDBOX_ID zeishctl sandboxes clone SANDBOX_ID --name agent-branch ``` #### Control sandbox lifecycle Start, pause, resume, stop, or delete a sandbox. Command: zeishctl sandboxes start | pause | resume | stop | delete Lifecycle operations are asynchronous. Poll get or events before using a runtime-dependent operation. delete is permanent. ``` zeishctl sandboxes start SANDBOX_ID zeishctl sandboxes pause SANDBOX_ID zeishctl sandboxes resume SANDBOX_ID zeishctl sandboxes stop SANDBOX_ID zeishctl sandboxes delete SANDBOX_ID ``` #### Open sandbox access Get data-plane credentials, a browser terminal, SSH access, preview codes, or tunnel access. Command: zeishctl sandboxes exec-access | shell | ssh | preview-code | tunnel-access exec-access prints a scoped credential. shell opens the terminal URL. ssh uses local OpenSSH. preview-code and tunnel-access accept short-lived TTL options. ``` zeishctl sandboxes exec-access SANDBOX_ID zeishctl sandboxes shell SANDBOX_ID zeishctl sandboxes ssh SANDBOX_ID -- -L LOCAL_PORT:REMOTE_HOST:REMOTE_PORT zeishctl sandboxes preview-code SANDBOX_ID 3000 --ttl-seconds 600 zeishctl sandboxes tunnel-access SANDBOX_ID --ttl-seconds 60 ``` #### Sync sandbox SSH keys Apply the current user SSH key set to a running sandbox. Command: zeishctl sandboxes sync-ssh-key SANDBOX_ID Run this after adding or removing a user key. The sandbox must have a runtime that can receive the update. ``` zeishctl sandboxes sync-ssh-key SANDBOX_ID ``` #### Read sandbox logs and events Inspect bounded workload output and lifecycle history. Command: zeishctl sandboxes logs | events SANDBOX_ID Logs and events are control-plane reads. Use them to diagnose provisioning and correlate state transitions with a run. ``` zeishctl sandboxes logs SANDBOX_ID zeishctl sandboxes events SANDBOX_ID ``` #### Manage snapshots Create, list, and delete snapshots for one sandbox. Command: zeishctl sandboxes snapshots SANDBOX_ID A snapshot is sandbox-scoped. Create it from a live runtime, wait until ready, then use the snapshot as a repeatable checkpoint. ``` zeishctl sandboxes snapshots SANDBOX_ID create before-deploy zeishctl sandboxes snapshots SANDBOX_ID zeishctl sandboxes snapshots SANDBOX_ID delete SNAPSHOT_ID ``` #### Run a command Execute a command through the sandboxd gRPC data plane. Command: zeishctl sandboxes exec SANDBOX_ID -- COMMAND The CLI streams stdout and stderr and prints the final exit code. It obtains scoped access automatically. ``` zeishctl sandboxes exec SANDBOX_ID -- python agent.py zeishctl sandboxes exec SANDBOX_ID -- env RUN_ID=123 ./worker ``` #### Read, write, and manage files Use sandboxd for scoped filesystem operations. Command: zeishctl sandboxes files SANDBOX_ID read | write | ls | mkdir | rm | mv Paths are checked inside the sandbox file root. mkdir supports --parents, rm supports --recursive, and mv supports --overwrite. ``` zeishctl sandboxes files SANDBOX_ID read /workspace/output.json zeishctl sandboxes files SANDBOX_ID write /workspace/input.txt hello zeishctl sandboxes files SANDBOX_ID ls /workspace --recursive zeishctl sandboxes files SANDBOX_ID mkdir /workspace/results --parents zeishctl sandboxes files SANDBOX_ID mv /workspace/a /workspace/b --overwrite zeishctl sandboxes files SANDBOX_ID rm /workspace/results --recursive ``` #### Upload and download files Stream files between the local machine and a sandbox. Command: zeishctl sandboxes files SANDBOX_ID upload | download Transfers use the sandboxd streaming file APIs. Use an explicit local path for output and keep large artifacts on a volume or snapshot when they must persist. ``` zeishctl sandboxes files SANDBOX_ID upload ./input.json /workspace/input.json zeishctl sandboxes files SANDBOX_ID download /workspace/output.json ./output.json ``` #### Use the API escape hatch Call any authenticated HTTP method and path supported by the server. Command: zeishctl api METHOD PATH [--body JSON] The path is appended to ZEISH_API_URL. Use this for a route that does not yet have a dedicated CLI command. Responses are printed as formatted JSON. ``` zeishctl api GET /api/v1/public/sandboxes zeishctl api POST /api/v1/public/sandboxes \ --body '{"name":"agent-run","templateId":"TEMPLATE_ID"}' ``` #### Generate completions or upgrade Integrate zeishctl into a shell and keep it current. Command: zeishctl completion SHELL | zeishctl upgrade completion writes a script to stdout for bash, zsh, fish, or another supported shell. upgrade installs the latest release or a requested --version. ``` zeishctl completion zsh > ~/.zsh/completions/_zeishctl zeishctl upgrade zeishctl upgrade --version 0.1.0 ``` #### Log in Authenticate the CLI with browser OAuth and PKCE. Command: zeishctl auth login The CLI opens the authorization URL and stores tokens in its config directory. ``` zeishctl auth login ``` #### Authenticate with an API key Authenticate the CLI with an API key. Command: zeishctl auth api-key TOKEN --store Use ZEISH_API_KEY for automation. Add --store only when the local config should retain the key. ``` zeishctl auth api-key TOKEN --store ``` #### Log out Clear stored OAuth credentials. Command: zeishctl auth logout This removes the local access and refresh tokens. ``` zeishctl auth logout ``` #### Check auth status See whether the CLI uses an API key or OAuth. Command: zeishctl auth status The output reports the active method without printing the credential. ``` zeishctl auth status ``` #### Generate shell completions Write completion scripts for the CLI. Command: zeishctl completion zsh The shell is a positional argument. Redirect the script into your shell completion directory. ``` zeishctl completion zsh ``` #### Upgrade zeishctl Install the latest or a selected CLI release. Command: zeishctl upgrade --version 0.1.0 Omit --version to use the latest published release. ``` zeishctl upgrade --version 0.1.0 ``` #### List organizations List organizations visible to the authenticated identity. Command: zeishctl orgs The command calls the versioned organization endpoint and prints JSON. ``` zeishctl orgs ``` #### List templates List visible sandbox templates. Command: zeishctl templates Use a returned template ID with sandboxes create. ``` zeishctl templates ``` #### List volumes List organization volumes. Command: zeishctl volumes The response includes cursor pagination when more results are available. ``` zeishctl volumes ``` #### List networks List organization networks. Command: zeishctl networks Use a returned network ID when creating or updating a sandbox. ``` zeishctl networks ``` #### List secrets List secret metadata. Command: zeishctl secrets list Values are excluded from the list response. ``` zeishctl secrets list ``` #### Read a secret Read one audited secret value. Command: zeishctl secrets get SECRET_ID Protect the terminal output. Secret reads are audited. ``` zeishctl secrets get SECRET_ID ``` #### Create a secret Create a Vault-backed secret from stdin or a file. Command: zeishctl secrets create NAME --provider-reference REF < value.txt Use --value-file for a file. Avoid --value-unsafe because it exposes material in argv. ``` zeishctl secrets create NAME --provider-reference REF < value.txt ``` #### Update a secret Update secret metadata or value. Command: zeishctl secrets update SECRET_ID --name NAME < value.txt Only supplied fields change. Value input follows the same safe stdin and file rules as create. ``` zeishctl secrets update SECRET_ID --name NAME < value.txt ``` #### Delete a secret Delete one secret. Command: zeishctl secrets delete SECRET_ID Deletion is audited and removes provider material. ``` zeishctl secrets delete SECRET_ID ``` #### List SSH keys List registered public keys. Command: zeishctl ssh-keys list Only public key data is returned. ``` zeishctl ssh-keys list ``` #### Add an SSH key Register a public SSH key. Command: zeishctl ssh-keys add NAME --path ~/.ssh/id_ed25519.pub If --path is omitted, the CLI checks common ed25519 and RSA public key paths. ``` zeishctl ssh-keys add NAME --path ~/.ssh/id_ed25519.pub ``` #### Remove an SSH key Revoke a registered public key. Command: zeishctl ssh-keys remove KEY_ID Sync the new key set into an existing sandbox separately. ``` zeishctl ssh-keys remove KEY_ID ``` #### List sandboxes List compact sandboxes in the organization. Command: zeishctl sandboxes list Use sandboxes get for detail and runtime metadata. ``` zeishctl sandboxes list ``` #### Create a sandbox Create a MicroVM sandbox. Command: zeishctl sandboxes create NAME --template-id TEMPLATE_ID --cpu 4 --memory 4096 CPU is cores, memory is MB, and region defaults to bremen. ``` zeishctl sandboxes create NAME --template-id TEMPLATE_ID --cpu 4 --memory 4096 ``` #### Get sandbox details Read one sandbox record. Command: zeishctl sandboxes get SANDBOX_ID Poll this command when waiting for an asynchronous lifecycle transition. ``` zeishctl sandboxes get SANDBOX_ID ``` #### Clone a sandbox Create a branch from an existing sandbox. Command: zeishctl sandboxes clone SANDBOX_ID --name agent-copy The name flag is optional. ``` zeishctl sandboxes clone SANDBOX_ID --name agent-copy ``` #### Start a sandbox Request a stopped sandbox runtime. Command: zeishctl sandboxes start SANDBOX_ID Wait for running before using commands, files, or desktop access. ``` zeishctl sandboxes start SANDBOX_ID ``` #### Pause a sandbox Pause a runtime while retaining state. Command: zeishctl sandboxes pause SANDBOX_ID Resume the sandbox before requesting data-plane work. ``` zeishctl sandboxes pause SANDBOX_ID ``` #### Resume a sandbox Resume a paused runtime. Command: zeishctl sandboxes resume SANDBOX_ID Poll get or events until the status is running. ``` zeishctl sandboxes resume SANDBOX_ID ``` #### Stop a sandbox Stop a runtime without deleting its record. Command: zeishctl sandboxes stop SANDBOX_ID Stopped sandboxes retain their configuration. ``` zeishctl sandboxes stop SANDBOX_ID ``` #### Delete a sandbox Permanently delete a sandbox. Command: zeishctl sandboxes delete SANDBOX_ID Deletion is irreversible. ``` zeishctl sandboxes delete SANDBOX_ID ``` #### Read sandbox logs Read captured sandbox output. Command: zeishctl sandboxes logs SANDBOX_ID The public API returns bounded history. ``` zeishctl sandboxes logs SANDBOX_ID ``` #### Read sandbox events Read lifecycle events. Command: zeishctl sandboxes events SANDBOX_ID Use events to correlate asynchronous state changes. ``` zeishctl sandboxes events SANDBOX_ID ``` #### Get exec access Mint data-plane credentials. Command: zeishctl sandboxes exec-access SANDBOX_ID The credential is short-lived and sandbox-scoped. ``` zeishctl sandboxes exec-access SANDBOX_ID ``` #### Sync sandbox SSH keys Apply the user's current keys to a runtime. Command: zeishctl sandboxes sync-ssh-key SANDBOX_ID Run this after adding or removing a user SSH key. ``` zeishctl sandboxes sync-ssh-key SANDBOX_ID ``` #### Create a preview code Mint temporary HTTP access to a port. Command: zeishctl sandboxes preview-code SANDBOX_ID 3000 --ttl-seconds 300 --path /health The port is positional. TTL and path are optional. ``` zeishctl sandboxes preview-code SANDBOX_ID 3000 --ttl-seconds 300 --path /health ``` #### Create tunnel access Mint WebSocket access to exposed TCP ports. Command: zeishctl sandboxes tunnel-access SANDBOX_ID --ttl-seconds 60 Use the SDK bridge or a compatible WebSocket client. ``` zeishctl sandboxes tunnel-access SANDBOX_ID --ttl-seconds 60 ``` #### List sandbox snapshots List snapshots for one sandbox. Command: zeishctl sandboxes snapshots SANDBOX_ID Snapshot IDs are sandbox-scoped. ``` zeishctl sandboxes snapshots SANDBOX_ID ``` #### Create a sandbox snapshot Capture a runtime checkpoint. Command: zeishctl sandboxes snapshots SANDBOX_ID create DISPLAY_NAME Create from a live runtime and wait for readiness. ``` zeishctl sandboxes snapshots SANDBOX_ID create DISPLAY_NAME ``` #### Delete a sandbox snapshot Delete one snapshot. Command: zeishctl sandboxes snapshots SANDBOX_ID delete SNAPSHOT_ID Snapshot deletion is irreversible. ``` zeishctl sandboxes snapshots SANDBOX_ID delete SNAPSHOT_ID ``` #### Execute a command Stream a command through sandboxd. Command: zeishctl sandboxes exec SANDBOX_ID -- COMMAND Stdout and stderr are streamed and the exit code is printed. ``` zeishctl sandboxes exec SANDBOX_ID -- COMMAND ``` #### Open a shell Open the browser terminal for a sandbox. Command: zeishctl sandboxes shell SANDBOX_ID The CLI prints the terminal URL and opens it when a browser is available. ``` zeishctl sandboxes shell SANDBOX_ID ``` #### Connect over SSH Connect with local OpenSSH. Command: zeishctl sandboxes ssh SANDBOX_ID Pass additional SSH arguments after -- and use --identity-file when needed. ``` zeishctl sandboxes ssh SANDBOX_ID ``` #### Read a file Read file contents from a sandbox. Command: zeishctl sandboxes files SANDBOX_ID read /workspace/output.json The path is resolved below the sandbox file root. ``` zeishctl sandboxes files SANDBOX_ID read /workspace/output.json ``` #### Write a file Write text to a sandbox file. Command: zeishctl sandboxes files SANDBOX_ID write /workspace/input.txt hello Parent directories are created by the CLI. ``` zeishctl sandboxes files SANDBOX_ID write /workspace/input.txt hello ``` #### List a directory List directory entries. Command: zeishctl sandboxes files SANDBOX_ID ls /workspace --recursive Use --recursive for nested entries. ``` zeishctl sandboxes files SANDBOX_ID ls /workspace --recursive ``` #### Create a directory Create a directory in the sandbox. Command: zeishctl sandboxes files SANDBOX_ID mkdir /workspace/results --parents Use --parents to create missing ancestors. ``` zeishctl sandboxes files SANDBOX_ID mkdir /workspace/results --parents ``` #### Remove a path Remove a file or directory. Command: zeishctl sandboxes files SANDBOX_ID rm /workspace/results --recursive Use --recursive for directories. Removing the file root is rejected. ``` zeishctl sandboxes files SANDBOX_ID rm /workspace/results --recursive ``` #### Rename a path Rename or move a path. Command: zeishctl sandboxes files SANDBOX_ID mv /workspace/a /workspace/b --overwrite Use --overwrite to replace an existing destination. ``` zeishctl sandboxes files SANDBOX_ID mv /workspace/a /workspace/b --overwrite ``` #### Download a file Stream a sandbox file to local storage. Command: zeishctl sandboxes files SANDBOX_ID download /workspace/output.json ./output.json The destination is a local path. ``` zeishctl sandboxes files SANDBOX_ID download /workspace/output.json ./output.json ``` #### Upload a file Stream a local file into the sandbox. Command: zeishctl sandboxes files SANDBOX_ID upload ./input.json /workspace/input.json Parent directories are created for the first upload chunk. ``` zeishctl sandboxes files SANDBOX_ID upload ./input.json /workspace/input.json ``` #### Call an arbitrary API route Use the CLI escape hatch for an HTTP operation without a named command. Command: zeishctl api GET /api/v1/public/sandboxes Pass JSON with --body. The request uses the configured API key or OAuth session. ``` zeishctl api GET /api/v1/public/sandboxes ``` ### MCP reference Source: https://zei.sh/docs/mcp Connect to Zeish and discover every organization and admin tool. #### Use the MCP server Connect an MCP client to Zeish over Streamable HTTP. Endpoint: https://api.zei.sh/api/v1/mcp. Send Authorization: Bearer with a zeish_live_ API key or an OAuth access token. The server exposes organization tools for sandboxes, templates, volumes, networks, secrets, SSH keys, and identity. Superadmins also receive platform operations. There are no direct MCP tools for raw TCP tunnel access, port sharing, or secret grants and leases. Use the public API or SDK for those operations. ``` {"mcpServers":{"zeish":{"url":"https://api.zei.sh/api/v1/mcp","headers":{"Authorization":"Bearer zeish_live_..."}}}} ``` #### Authenticate an MCP client Choose an API key for headless use or OAuth for interactive clients. Headless clients can send a standalone API key directly. Interactive connectors use OAuth Authorization Code with PKCE and dynamic client registration. OAuth metadata is at /api/v1/.well-known/oauth-authorization-server. Register at /api/v1/mcp-auth/register, authorize at /api/v1/mcp-auth/authorize, and exchange codes at /api/v1/mcp-auth/token. ``` Authorization: Bearer zeish_live_... ``` #### Discover live tool schemas Use tools/list instead of hard-coding an outdated tool catalog. Call tools/list after connecting. The response is the source of truth for names, descriptions, and JSON schemas exposed by the deployed server. Tool results are returned as text content. Validate arguments against the schema returned by the server. ``` {"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}} ``` #### Create a sandbox Create a MicroVM in the caller's organization. Tool: sandboxes_create Input: {"name":"agent-run","templateId":"TEMPLATE_ID","cpu":4,"memory":4096} Permission: MACHINE_CREATE. Optional storage, network, ingress, labels, and secretInjection fields match the public create schema. Response: The created sandbox record. ``` {"name":"sandboxes_create","arguments":{"name":"agent-run","templateId":"TEMPLATE_ID","cpu":4,"memory":4096}} ``` #### List sandboxes List compact sandboxes in the caller's organization. Tool: sandboxes_list Input: {"limit":20,"cursor":"..."} Permission: MACHINE_READ. Use the returned nextCursor to continue pagination. ``` {"name":"sandboxes_list","arguments":{"limit":20,"cursor":"..."}} ``` #### Get sandbox details Fetch one sandbox with access URL enrichment. Tool: sandboxes_get Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_READ. Inspect status and runtime metadata before data-plane work. ``` {"name":"sandboxes_get","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Mint sandbox data-plane access Mint credentials for commands and files. Tool: sandboxes_exec_access Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_UPDATE. The token is sandbox-scoped and short-lived. Never log it. Response: sandboxUrl, sandboxRpcUrl, token, and expiresAt. ``` {"name":"sandboxes_exec_access","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Get a terminal URL Create a browser terminal URL. Tool: sandboxes_terminal_url Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_READ. Open the returned URL in a browser. ``` {"name":"sandboxes_terminal_url","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Sync SSH keys Apply current user keys to an existing runtime. Tool: sandboxes_sync_ssh_keys Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_UPDATE. Run after adding or removing an SSH key. ``` {"name":"sandboxes_sync_ssh_keys","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Create a preview code Create temporary HTTP access to a sandbox port. Tool: sandboxes_create_preview_code Input: {"sandboxId":"SANDBOX_ID","port":3000,"ttl_seconds":300} Permission: MACHINE_READ. Use base_url and the code for agents, and the handoff URL for browsers. ``` {"name":"sandboxes_create_preview_code","arguments":{"sandboxId":"SANDBOX_ID","port":3000,"ttl_seconds":300}} ``` #### List sandbox logs Read bounded sandbox output. Tool: sandboxes_list_logs Input: {"sandboxId":"SANDBOX_ID","limit":"100","source":"app"} Permission: MACHINE_READ. Filter by service or source when diagnosing a run. ``` {"name":"sandboxes_list_logs","arguments":{"sandboxId":"SANDBOX_ID","limit":"100","source":"app"}} ``` #### List sandbox events Read sandbox lifecycle events. Tool: sandboxes_list_events Input: {"sandboxId":"SANDBOX_ID","limit":"100"} Permission: MACHINE_READ. Use events to poll asynchronous transitions. ``` {"name":"sandboxes_list_events","arguments":{"sandboxId":"SANDBOX_ID","limit":"100"}} ``` #### Start a sandbox Request a sandbox runtime to start. Tool: sandboxes_start Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_UPDATE. Poll sandboxes_get until the status is running. ``` {"name":"sandboxes_start","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Pause a sandbox Pause a sandbox while retaining its state. Tool: sandboxes_pause Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_UPDATE. Resume the sandbox before requesting fresh data-plane work. ``` {"name":"sandboxes_pause","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Resume a sandbox Resume a paused sandbox. Tool: sandboxes_resume Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_UPDATE. Wait for running before using commands or files. ``` {"name":"sandboxes_resume","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Stop a sandbox Stop a runtime and retain its record. Tool: sandboxes_stop Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_UPDATE. Stop releases the active runtime without deleting the sandbox. ``` {"name":"sandboxes_stop","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Kill a sandbox Force-stop a stuck runtime. Tool: sandboxes_kill Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_UPDATE. Use this when a normal stop does not converge. ``` {"name":"sandboxes_kill","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Delete a sandbox Permanently delete a sandbox. Tool: sandboxes_delete Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_DELETE. Deletion is irreversible. ``` {"name":"sandboxes_delete","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Create a snapshot Capture a sandbox runtime state. Tool: sandboxes_create_snapshot Input: {"sandboxId":"SANDBOX_ID","displayName":"dependencies-v4"} Permission: MACHINE_UPDATE. Create from a live runtime and wait until ready. ``` {"name":"sandboxes_create_snapshot","arguments":{"sandboxId":"SANDBOX_ID","displayName":"dependencies-v4"}} ``` #### List snapshots List snapshots owned by one sandbox. Tool: sandboxes_list_snapshots Input: {"sandboxId":"SANDBOX_ID"} Permission: MACHINE_READ. Snapshot IDs are scoped to their sandbox. ``` {"name":"sandboxes_list_snapshots","arguments":{"sandboxId":"SANDBOX_ID"}} ``` #### Delete a snapshot Delete one sandbox snapshot. Tool: sandboxes_delete_snapshot Input: {"sandboxId":"SANDBOX_ID","snapshotId":"SNAPSHOT_ID"} Permission: MACHINE_UPDATE. Snapshot deletion is irreversible. ``` {"name":"sandboxes_delete_snapshot","arguments":{"sandboxId":"SANDBOX_ID","snapshotId":"SNAPSHOT_ID"}} ``` #### List templates List templates visible to the organization. Tool: templates_list Input: {"limit":20,"cursor":"..."} Permission: MACHINE_READ. Use this to find a template ID for sandbox creation. ``` {"name":"templates_list","arguments":{"limit":20,"cursor":"..."}} ``` #### Get a template Fetch one template by ID. Tool: templates_get Input: {"templateId":"TEMPLATE_ID"} Permission: MACHINE_READ. The response includes image and default runtime settings. ``` {"name":"templates_get","arguments":{"templateId":"TEMPLATE_ID"}} ``` #### Create a volume Create persistent organization storage. Tool: volumes_create Input: {"name":"agent-data","region":"bremen","sizeGb":20} Permission: ORG_MANAGE. Attach the resulting volume ID to a sandbox. ``` {"name":"volumes_create","arguments":{"name":"agent-data","region":"bremen","sizeGb":20}} ``` #### List volumes List organization volumes. Tool: volumes_list Input: {"limit":20,"cursor":"..."} Permission: ORG_MANAGE. Use nextCursor for additional pages. ``` {"name":"volumes_list","arguments":{"limit":20,"cursor":"..."}} ``` #### Get a volume Fetch one volume by ID. Tool: volumes_get Input: {"volumeId":"VOLUME_ID"} Permission: ORG_MANAGE. Use the volume ID in sandbox configuration. ``` {"name":"volumes_get","arguments":{"volumeId":"VOLUME_ID"}} ``` #### Delete a volume Delete one organization volume. Tool: volumes_delete Input: {"volumeId":"VOLUME_ID"} Permission: ORG_MANAGE. Remove dependencies before deleting storage. ``` {"name":"volumes_delete","arguments":{"volumeId":"VOLUME_ID"}} ``` #### Create a network Create an organization network. Tool: networks_create Input: {"name":"agent-network","region":"bremen"} Permission: ORG_MANAGE. Attach the resulting network ID to a sandbox. ``` {"name":"networks_create","arguments":{"name":"agent-network","region":"bremen"}} ``` #### List networks List organization networks. Tool: networks_list Input: {"limit":20,"cursor":"..."} Permission: ORG_MANAGE. Use nextCursor for additional pages. ``` {"name":"networks_list","arguments":{"limit":20,"cursor":"..."}} ``` #### Get a network Fetch one network by ID. Tool: networks_get Input: {"networkId":"NETWORK_ID"} Permission: ORG_MANAGE. Use the network ID in sandbox configuration. ``` {"name":"networks_get","arguments":{"networkId":"NETWORK_ID"}} ``` #### Delete a network Delete one organization network. Tool: networks_delete Input: {"networkId":"NETWORK_ID"} Permission: ORG_MANAGE. Detach dependent sandboxes before deleting it. ``` {"name":"networks_delete","arguments":{"networkId":"NETWORK_ID"}} ``` #### List secret metadata List secrets without values. Tool: secrets_list Input: {} Permission: ORG_MANAGE. Use this to discover secret IDs. Values are never included. ``` {"name":"secrets_list","arguments":{}} ``` #### Get a secret Read one audited secret value. Tool: secrets_get Input: {"secretId":"SECRET_ID"} Permission: ORG_MANAGE. Treat the result as sensitive and do not log it. ``` {"name":"secrets_get","arguments":{"secretId":"SECRET_ID"}} ``` #### Create a secret Create a plaintext or JSON secret. Tool: secrets_create Input: {"name":"github-token","provider":"vault","providerReference":"secret/data/ci/github","value":"replace-me"} Permission: ORG_MANAGE. The configured deployment provider must be available. ``` {"name":"secrets_create","arguments":{"name":"github-token","provider":"vault","providerReference":"secret/data/ci/github","value":"replace-me"}} ``` #### Update a secret Edit secret metadata or material. Tool: secrets_update Input: {"secretId":"SECRET_ID","name":"github-token-v2"} Permission: ORG_MANAGE. Send only the fields that should change. ``` {"name":"secrets_update","arguments":{"secretId":"SECRET_ID","name":"github-token-v2"}} ``` #### Delete a secret Delete a secret. Tool: secrets_delete Input: {"secretId":"SECRET_ID"} Permission: ORG_MANAGE. Deletion is audited and removes provider material. ``` {"name":"secrets_delete","arguments":{"secretId":"SECRET_ID"}} ``` #### Get the caller identity Read the authenticated profile and memberships. Tool: users_whoami Input: {} Permission: Authenticated. Use this to confirm the current user and organization context. ``` {"name":"users_whoami","arguments":{}} ``` #### List SSH keys List keys authorized for sandbox provisioning. Tool: ssh_keys_list Input: {} Permission: Authenticated. Only public key material is returned. ``` {"name":"ssh_keys_list","arguments":{}} ``` #### Register an SSH key Register a public SSH key. Tool: ssh_keys_create Input: {"name":"workstation","publicKey":"ssh-ed25519 AAAA..."} Permission: Authenticated. Sync the key into an existing sandbox separately. ``` {"name":"ssh_keys_create","arguments":{"name":"workstation","publicKey":"ssh-ed25519 AAAA..."}} ``` #### Delete an SSH key Revoke a public SSH key. Tool: ssh_keys_delete Input: {"keyId":"KEY_ID"} Permission: Authenticated. The key no longer applies to future provisioning. ``` {"name":"ssh_keys_delete","arguments":{"keyId":"KEY_ID"}} ``` #### Use superadmin tools Inspect platform users, billing, organizations, and nodes. The admin tool family is available only to superadmins. tools/list exposes the exact schemas when the authenticated identity has access. Available tools: admin_list_users, admin_get_user_detail, admin_list_user_sandboxes, admin_get_user_usage, admin_list_sandboxes, admin_get_sandbox_billing, admin_get_sandbox_events, admin_get_billing_overview, admin_get_billing_periods, admin_suspend_user, admin_reinstate_user, admin_list_organizations, admin_get_organization_billing, admin_grant_organization_credit, and admin_list_node_machines. ``` {"name":"admin_list_users","arguments":{"page":1,"pageSize":20}} ``` ### ComputeSDK reference Source: https://zei.sh/docs/sdk Use the provider, typed API client, and session client from TypeScript. #### Use the ComputeSDK provider Run portable ComputeSDK code against Zeish. Call zeish with apiKey, baseUrl, and optional defaultTemplateId. The provider implements sandbox create, list, get, destroy, runCommand, getInfo, getUrl, filesystem operations, and snapshot creation. The standard provider uses cwd, timeout, env, and background in command options. It intentionally does not expose standard snapshot list and delete because Zeish snapshots are sandbox-scoped. ``` import { zeish } from "@zeish/computesdk-provider"; const compute = zeish({ apiKey: process.env.ZEISH_API_KEY!, baseUrl: "https://api.zei.sh/api/v1", defaultTemplateId: process.env.ZEISH_TEMPLATE_ID!, }); ``` #### Use the typed API client Call the public control plane from TypeScript. createZeishApi exposes typed methods for resources, sandboxes, lifecycle, access, ingress, logs, events, snapshots, and secrets. Pass the production baseUrl explicitly. The API client handles X-API-Key authentication and typed ZeishApiError responses. ``` import { createZeishApi } from "@zeish/computesdk-provider"; const api = createZeishApi({ apiKey: process.env.ZEISH_API_KEY!, baseUrl: "https://api.zei.sh/api/v1", }); const page = await api.listSandboxes({ limit: 20 }); ``` #### Use the Zeish session client Use one agent-friendly object for control and data-plane work. createZeishSandboxClient returns sessions with waitForAccess, credential refresh, commands, files, desktop actions, lifecycle, logs, events, previews, ports, terminal URLs, and sandbox-scoped snapshots. The session client uses workingDirectory, timeoutMs, and environment. It is the easiest path for a long-running agent workflow. ``` import { createZeishSandboxClient } from "@zeish/computesdk-provider"; const client = createZeishSandboxClient({ apiKey: process.env.ZEISH_API_KEY!, baseUrl: "https://api.zei.sh/api/v1", defaultTemplateId: process.env.ZEISH_TEMPLATE_ID!, }); const sandbox = await client.create({ name: "agent-run" }); await sandbox.waitForAccess(); ``` #### Create and wait for a sandbox Use the retrying lifecycle helper for agent startup. createAndStartSandbox creates a sandbox, waits for running, destroys a failed attempt, and retries. Its defaults are three attempts, a 90 second ready timeout, and a two second poll interval. A normal create request already asks for a running runtime. Use the helper when startup reliability matters. ``` import { createAndStartSandbox } from "@zeish/computesdk-provider"; const sandbox = await createAndStartSandbox(api, { name: "agent-run", templateId: process.env.ZEISH_TEMPLATE_ID!, maxAttempts: 3, readyTimeoutMs: 90_000, }); ``` #### Run commands with the SDK Stream command output through the sandbox data plane. The provider uses sandboxd gRPC ExecStream. Pass cwd, timeout, env, and background using standard ComputeSDK names. The session client maps these to workingDirectory, timeoutMs, and environment. Treat stdout, stderr, and exit codes as untrusted workload data. ``` const result = await compute.sandbox.runCommand(sandbox, "python agent.py", { cwd: "/workspace", timeout: 120_000, env: { RUN_ID: runId }, }); console.log(result.stdout, result.exitCode); ``` #### Use SDK filesystem operations Read and write files inside the sandbox root. The provider maps readFile, writeFile, mkdir, readdir, exists, and remove to sandboxd. The session client adds readText, writeText, makeDirectory, listDirectory, stat, exists, and remove. Paths are checked for traversal and symlink escapes. Use a volume or snapshot for data that must outlive the sandbox. ``` await sandbox.files.makeDirectory("/workspace/results"); await sandbox.files.writeText("/workspace/results/status.json", JSON.stringify({ ok: true })); const status = await sandbox.files.readText("/workspace/results/status.json"); ``` #### Create and resolve preview access Use temporary HTTP access for browser and agent clients. createPreviewCode returns browser and agent access forms. fetchPreviewJsonVersion uses baseUrl with the bearer code. resolveCdpEndpoint converts Chrome's loopback WebSocket URL into a preview endpoint. ``` const preview = await api.createPreviewCode(sandboxId, { port: 9222, ttl_seconds: 3600 }); const version = await fetchPreviewJsonVersion(preview); const endpoint = resolveCdpEndpoint({ preview, webSocketDebuggerUrl: String(version.webSocketDebuggerUrl) }); ``` #### Bridge a TCP tunnel Connect local clients to exposed sandbox TCP ports. createTunnelBridge and createCdpTunnelBridge bind to loopback only. Close the bridge when the operation ends and treat the local port as an active capability while it is open. ``` const access = await api.createTunnelAccess(sandboxId, { ttl_seconds: 60 }); const bridge = await createTunnelBridge(access, 5432); console.log(bridge.localPort); await bridge.close(); ``` #### Control a desktop session Use screenshots and native Wayland input for GUI agents. Desktop actions are available on the first-party session client. Use screenshot, move, click, scroll, type, and key with a template that includes a desktop agent. ``` const png = await sandbox.desktop.screenshot(); await sandbox.desktop.move(340, 210); await sandbox.desktop.click({ x: 340, y: 210 }); await sandbox.desktop.type("hello"); await sandbox.desktop.key("ENTER"); ``` ### Errors and limits Source: https://zei.sh/docs/reference/errors Handle authentication, validation, conflicts, throttling, and runtime failures. 401 means credentials are missing or invalid. 403 means the credential lacks the required permission. 404 also protects organization boundaries, so a resource in another organization looks absent. 409 indicates a conflict. 429 and 5xx responses may be retried with bounded backoff. Validation failures use 400 with code invalid_request. Common examples include a missing templateId, an invalid cursor, a port outside 1 through 65535, a preview or tunnel TTL outside 1 through 3600, an unavailable runtime, or a sandbox that has no live provider machine for data-plane access. ZeishApiError exposes status, code, details, method, and path. Reads can use the SDK's transient retry transport. Mutations should use a stable Idempotency-Key instead of blindly replaying a request. ``` import { ZeishApiError, createZeishApi } from "@zeish/computesdk-provider"; try { await createZeishApi({ apiKey: process.env.ZEISH_API_KEY!, baseUrl: "https://api.zei.sh/api/v1", }).getSandbox(sandboxId); } catch (error) { if (error instanceof ZeishApiError) { console.error(error.status, error.code, error.details); } throw error; } ``` ## Platform ### Platform capabilities Source: https://zei.sh/docs/platform Ingress, previews, tunnels, secrets, observability, and runtime behavior. #### Expose TCP and UDP ports Declare raw L4 services with explicit transport and access policy. Ingress is declared with mode raw_l4, protocol tcp or udp, internalPort, and an optional externalPort. externalPort defaults to internalPort. Ports must be between 1 and 65535, and each protocol/external-port pair can appear only once. There are two access policies. org is the default and limits access to matching organization credentials. public requires no authentication, so anyone with the endpoint can connect. A legacy private value is normalized to org. No cross-organization private tier exists. Declare ingress when creating a sandbox or add a port later with POST /ports. Change its tier with PUT /ports/:port/share. Public port exposure is separate from preview-code lifetime. ``` const sandbox = await api.createSandbox({ name: "web-and-db", templateId: process.env.ZEISH_TEMPLATE_ID!, ingress: [ { mode: "raw_l4", protocol: "tcp", internalPort: 3000 }, { mode: "raw_l4", protocol: "tcp", internalPort: 5432, accessPolicy: "org", }, { mode: "raw_l4", protocol: "udp", internalPort: 27015, externalPort: 27015, }, ], }); await api.addPort(sandbox.id, { internalPort: 8080, protocol: "tcp", accessPolicy: "public", }); ``` #### Preview URLs Give browsers and agents temporary access to an HTTP service. POST /public/sandboxes/:sandboxId/preview-codes mints a time-limited code for a declared port. ttl_seconds defaults to 300 and accepts values from 1 through 3600. The response includes url, handoff_url, base_url, code, and expires_at. Open url or handoff_url in a real browser. It is a single-use cookie handoff. For fetch, WebSocket, Playwright, or Chrome CDP, use base_url with Authorization: Bearer . Do not append paths to the browser handoff URL. The SDK normalizes the response to url, handoffUrl, baseUrl, token, headers, and expires_at. fetchPreviewJsonVersion and resolveCdpEndpoint handle Chrome's loopback WebSocket URL and preview authentication. ``` const preview = await api.createPreviewCode(sandboxId, { port: 9222, ttl_seconds: 3600, }); const version = await fetchPreviewJsonVersion(preview); const endpoint = resolveCdpEndpoint({ preview, webSocketDebuggerUrl: String(version.webSocketDebuggerUrl), }); // Playwright: chromium.connectOverCDP(endpoint.wsUrl, { // headers: endpoint.headers, // }); ``` #### Tunnel raw TCP services Connect databases, debuggers, and CDP without relying on an HTTP Host header. POST /public/sandboxes/:sandboxId/tunnel-access returns ws_url, token, and expires_at. The token defaults to 60 seconds and accepts values from 1 through 3600. It grants tunnel:connect access to the sandbox's exposed TCP ports. The tunnel WebSocket terminates at proxyd and dials the sandbox backend directly. This supports protocols such as Chrome CDP and database wire protocols that can reject a forwarded public Host header. The SDK includes createTunnelBridge and createCdpTunnelBridge. Both bind locally to loopback only. Treat the local bridge as an unauthenticated capability while it is running and close it as soon as the operation ends. ``` import { createTunnelBridge, TUNNEL_ACCESS_TTL_AGENT, } from "@zeish/computesdk-provider"; const access = await api.createTunnelAccess(sandboxId, { ttl_seconds: TUNNEL_ACCESS_TTL_AGENT, }); const bridge = await createTunnelBridge(access, 5432); console.log("database endpoint:", bridge.httpUrl); // Connect your TCP client to bridge.localPort. await bridge.close(); ``` #### Manage and inject secrets Store provider-backed values and deliver them only for an approved sandbox operation. Secrets are organization resources. Create, list, view, edit, and delete them with ORG_MANAGE. Values are never returned by list, while get returns the value and is audited with no-store response headers. The current configured provider is Vault. AWS Secrets Manager and GCP Secret Manager require a deployment with those providers configured. Create a grant for a sandbox with a target of env or file. Environment names must be valid shell variable names. File targets must be below /run/secrets. A grant can apply at startup or command time. A sandbox create request can include the same refs under secretInjection. Secret leases are short-lived and scoped to the organization, sandbox, exact secret IDs, target names, operation, expiry, and nonce. Secret values and provider credentials are not persisted in sandbox records, snapshots, or ordinary runtime responses. ``` curl -X POST https://api.zei.sh/api/v1/public/secrets -H "X-API-Key: $ZEISH_API_KEY" -H "Content-Type: application/json" -d '{ "name": "github-token", "provider": "vault", "providerReference": "secret/data/ci/github", "providerKey": "token", "format": "plaintext", "value": "replace-me" }' # Advanced routes # POST /public/secrets/:secretId/grants # POST /public/secrets/:sandboxId/leases ``` #### Logs and events Inspect workload output and lifecycle history with bounded reads. GET /public/sandboxes/:sandboxId/logs returns captured output. Filter by service or source and set a bounded limit. Sources are boot, memory, and app. GET /events returns lifecycle events with status, source, timestamp, and an optional message. Logs and events are read-only control-plane calls and require MACHINE_READ. They are useful for polling state transitions, diagnosing failed provisioning, and correlating workload output with a run label. The current public API returns bounded history, not an unbounded live stream. Persist only the data your retention policy allows. ``` const logs = await api.listLogs(sandboxId, { limit: 100, source: "app", }); const events = await api.listEvents(sandboxId, { limit: 100 }); console.log({ logs, events }); ``` #### Runtime drivers and recovery Understand runtime choices, supervision, and the limits of recovery. Firecracker is the default runtime and the broadest compatibility target. Cloud Hypervisor uses the same sandbox lifecycle surface and is enabled per node. Availability depends on the node and its advertised capabilities. The runtime is supervised independently from the node orchestrator process. A process crash can recover the same sandbox identity and durable state. This does not promise migration when an entire host goes offline. Persistent volumes and Windows guests are not available on Cloud Hypervisor today. Treat cross-node failover as unsupported and design agents to handle failed or terminal statuses.