Skip to content

Repository files navigation

Sando

Sando is an experimental convenience service wrapper around Microsandbox. It packages common single-machine sandbox lifecycle operations behind a Bearer-authenticated control API, with a TypeScript SDK and a separate host-routed preview proxy.

The supported environments are Apple Silicon macOS for development and Linux with KVM for deployment. Install Bun before continuing.

Repository layout

  • packages/server — Bun service (@deablabs/sando)
  • packages/sdk — dependency-free TypeScript client (@deablabs/sando-sdk)
  • examples — runnable workflows built against the workspace SDK
  • DESIGN.md — architecture, API contract, configuration, and security details

bun run build bundles both packages into their respective dist directories.

Run the service directly from npm and install the SDK:

API_KEYS=dev-key DEFAULT_IMAGE=node:22 bunx @deablabs/sando
bun add @deablabs/sando-sdk

Run locally

bun install
API_KEYS=dev-key DEFAULT_IMAGE=node:22 bun start

API_KEYS (a comma-separated list) and DEFAULT_IMAGE are required. The API and preview listeners default to ports 4000 and 4001 and bind all interfaces. Use http://127.0.0.1:4000 for local API access. The SDK reads SANDBOX_API_KEY and optionally SANDBOX_API_URL, which defaults to that local API URL.

Preview origins use PREVIEW_DOMAIN=preview.localhost and include port 4001 by default. Sandbox responses expose one stable preview origin per requested guest port. SDK reachability checks probe TCP connectivity; they do not determine application health.

See the configuration table in DESIGN.md for listener, storage, resource, network, exec, idempotency, and preview-auth settings. Common development commands are:

bun run build
bun run test
bun run check
bun run fix

See PUBLISHING.md for the manual npm release checklist.

OpenCode example

examples/opencode.ts starts a loopback-only Smart HTTP Git server, boots a Node microVM, installs OpenCode, asks it to edit and push a branch, and starts the resulting application as a durable exec. It then waits for TCP reachability and checks the application's /health endpoint.

Put ANTHROPIC_API_KEY (or FIREWORKS_API_KEY) in the root .env, then run the service and example in separate terminals:

HOST_CONNECTORS='{"local-git":{"port":7070,"protocol":"http","description":"OpenCode example Git fixture"}}' \
  API_KEYS=dev-key DEFAULT_IMAGE=node:22 bun start
SANDBOX_API_KEY=dev-key bun examples/opencode.ts

Pass a custom task as an argument and use OPENCODE_MODEL to override the provider-specific default model:

SANDBOX_API_KEY=dev-key bun examples/opencode.ts "Create a tiny Bun hello-world app"

The host Git server remains on 127.0.0.1:7070; the guest reaches only the registered connector at host.microsandbox.internal. Production clients would normally use a remote Git service and appropriate credentials.

Other examples include compare.ts (forks and Vite previews) and the phoenix-*.ts checkpoint, restore, and benchmark workflows. They require a running manager and SANDBOX_API_KEY.

Preview access

Trusted local development defaults to PREVIEW_AUTH=none. For protected previews, set PREVIEW_AUTH=grant and mint a short-lived browser bootstrap URL:

const preview = sandbox.previews.get(3000);
await preview.waitUntilReachable({ timeoutMs: 30_000 });
const grant = await preview.createBrowserGrant({
  expiresInSeconds: 300,
  singleUse: true,
});
console.log(grant.url);

Opening the URL exchanges the grant for a host-only HTTP-only cookie and redirects to the clean preview origin. Grants and cookies are scoped to one sandbox port. singleUse applies to grant redemption, not subsequent browser requests.

Reliability and lifecycle

Create, exec, fork, and checkpoint operations accept an optional idempotency key. Reusing a key with the same API credential and input returns the original response; reusing it with different input returns idempotency_key_reused. Interrupted operations fail closed with idempotency_operation_interrupted. See DESIGN.md for retention and retry semantics.

Exec sessions and output survive client disconnects, but not a manager restart. Call stream.detach() before dropping an attachment, reattach with the saved byte offset, and use kill() only when the remote process should terminate.

Checkpoint restore requires the API-key scope recorded on its source sandbox. Checkpoint names and checkpoint management are otherwise global to the manager. Checkpoints preserve disk and runtime configuration but not secret bindings.

Secrets and host services

Destination-scoped secret bindings pass a placeholder into the guest. Microsandbox substitutes it in outbound request headers to exact allowed DNS hostnames. Binding names must be valid, unique environment variable names and must not collide with ordinary env entries. Wildcards, URLs, localhost, IP addresses, and ports are not valid allowed hosts.

Secret bindings are not a credential vault or a general egress allowlist. Their plaintext exists transiently in client, service, and host runtime memory, and the service does not redact it from process output. Bound sandboxes cannot be forked, and restored checkpoints cannot receive bindings. Bindings also require reachable network policy: public egress or at least one host connector.

HOST_CONNECTORS registers fixed host-loopback TCP services. Clients request connector names; the service resolves them to host.microsandbox.internal and exposes the non-secret endpoint metadata in SANDBOX_HOST_CONNECTORS. Set publicEgress: false for connector-only networking. Connectors provide network authorization and discovery, not application credentials or TLS.

Deployment and compatibility

Expose the authenticated control API and wildcard preview listener separately, prefer different registrable domains, and terminate TLS at a reverse proxy. Set PREVIEW_SCHEME=https, set PREVIEW_DOMAIN to the preview suffix, and leave PREVIEW_PUBLIC_PORT empty when public TLS uses port 443. Firewall both listeners and the sandbox host-port range from direct public access.

This project is experimental and does not provide schema or API compatibility with older versions. Recreate the configured DATA_DIR before opening state written by an incompatible version. See DESIGN.md for the complete contract, configuration defaults, non-goals, and security model.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages