From d9b989774e463b26eeeb61a1f2e92e2e01063597 Mon Sep 17 00:00:00 2001 From: Christian Alfoni Date: Mon, 15 Jun 2026 09:03:55 +0200 Subject: [PATCH 1/6] docs(sdk): add migration guide to Together Sandbox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new SDK page walking through migrating from @codesandbox/sdk to together-sandbox: vocabulary mapping, auth/client changes, the create→start loop, per-namespace before/after examples (snapshots, lifecycle, execs, filesystem, ports, versioning), gaps without a direct equivalent, and a migration checklist. Co-Authored-By: Claude Opus 4.8 --- packages/projects-docs/pages/sdk/_meta.json | 1 + .../pages/sdk/migrate-to-together-sandbox.mdx | 332 ++++++++++++++++++ 2 files changed, 333 insertions(+) create mode 100644 packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx diff --git a/packages/projects-docs/pages/sdk/_meta.json b/packages/projects-docs/pages/sdk/_meta.json index 3bccad91..1a0db702 100644 --- a/packages/projects-docs/pages/sdk/_meta.json +++ b/packages/projects-docs/pages/sdk/_meta.json @@ -49,6 +49,7 @@ }, "template-library": "Template Library", "version-history": "Version History", + "migrate-to-together-sandbox": "Migrate to Together Sandbox", "contact": { "title": "Contact Us", "href": "https://codesandbox.io/support#form", diff --git a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx new file mode 100644 index 00000000..26033ea4 --- /dev/null +++ b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx @@ -0,0 +1,332 @@ +--- +title: Migrate to Together Sandbox +description: How to migrate from the CodeSandbox SDK to the Together Sandbox SDK. +--- + +import { Callout } from 'nextra-theme-docs' + +# Migrate from CodeSandbox SDK to Together Sandbox + +This guide walks you through moving from `@codesandbox/sdk` to `together-sandbox`. + +Together Sandbox is a hosted wrapper over the same underlying infrastructure that powers the CodeSandbox SDK — the in-VM agent, the Firecracker microVMs, and the memory-snapshot mechanism are the same engine. The difference is **how much of that engine each SDK surfaces**: CodeSandbox exposes a broad, ergonomic API; Together Sandbox exposes a deliberately minimal slice with a strong snapshot, streaming, and client-reliability story. + +This means the **core loop carries over cleanly** — build a snapshot, create a sandbox, run commands, read and write files, watch ports — but several developer-experience and platform features either need a different approach or aren't available. This guide covers both. + + +This guide focuses on the TypeScript SDK and CLI. If you're on the Python or Node-specific paths, the concepts map across but the exact APIs differ. + + +## Vocabulary mapping + +Before you start, here's how CodeSandbox concepts translate to Together Sandbox. + +| CodeSandbox SDK | Together Sandbox SDK | Difference | +| --- | --- | --- | +| Template | Snapshot | Same underlying memory-snapshot mechanism. | +| `CodeSandbox` client | `TogetherSandbox` client | Together adds `baseUrl` and `retry`; CodeSandbox adds an OpenTelemetry `tracer`. | +| `sandbox.connect()` → `client` | `sdk.sandboxes.start()` → `Sandbox` | Together separates create (record only) from start (boots the VM). | +| `client.commands` | `sandbox.execs` | Together exposes more primitives but no `runBackground` helper. | +| `client.fs` | `sandbox.files` / `sandbox.directories` | Split into two namespaces. | +| `client.ports` | `sandbox.ports` | Discovery only — no preview-URL builder or host tokens. | +| Sessions | _(none)_ | Together has no session / multi-user layer. | +| `client.tasks`, `client.setup` | _(none)_ | Setup moves into the Docker image. | + +## Installation & authentication + +Swap the package and the API key environment variable. Together Sandbox requires Node 18+. + +```bash +# Before +npm install @codesandbox/sdk # auth via CSB_API_KEY + +# After +npm install together-sandbox # auth via TOGETHER_API_KEY +``` + +The Together CLI ships as a prebuilt binary (darwin/linux/win) via a shell installer rather than as a bundled `csb` binary. + +## Client initialization + +```ts +// Before +import { CodeSandbox } from '@codesandbox/sdk'; + +const sdk = new CodeSandbox(); // reads CSB_API_KEY +``` + +```ts +// After +import { TogetherSandbox } from 'together-sandbox'; + +const sdk = new TogetherSandbox(); // reads TOGETHER_API_KEY + +// Optional configuration +const sdk = new TogetherSandbox({ + apiKey: process.env.TOGETHER_API_KEY, + baseUrl: 'https://...', // override the management API endpoint + retry: { // built-in configurable retry + maxAttempts: 3, + shouldRetry: (err) => err.status === 0 || err.status >= 500, + onRetry: (err, attempt) => console.warn(`retry ${attempt}`, err), + }, +}); +``` + + +The built-in `retry` config (backoff + jitter, `shouldRetry`, `onRetry`) and the typed `HttpError` (`{ message, status }`, where `status === 0` signals a transport error) are a genuine improvement over the CodeSandbox client. Make sure to **exclude `snapshots.create` from retries** — it is not idempotent. + + +## The core loop + +The biggest structural change is that **creating a sandbox and booting its VM are two steps**. In CodeSandbox, `create()` boots a VM and you `connect()` to it. In Together, `create()` makes a record (state `created`) and `start()` boots the VM and returns a connected `Sandbox`. + +```ts +// Before — CodeSandbox +const sdk = new CodeSandbox(); +const sandbox = await sdk.sandboxes.create({ id: 'your-template-id' }); +const client = await sandbox.connect(); + +const output = await client.commands.run("echo 'Hello World'"); +console.log(output); +``` + +```ts +// After — Together Sandbox +const sdk = new TogetherSandbox(); +const { id } = await sdk.sandboxes.create({ /* params */ }); +const sandbox = await sdk.sandboxes.start(id); + +const { exitCode, output } = await sandbox.execs.exec('echo', ["Hello World"]); +console.log(output); +``` + +## Templates → Snapshots + +This is Together's strongest area, and the snapshot API is arguably cleaner than CodeSandbox templates — structured progress callbacks, list/get/delete by id or alias, direct registration of any public image, and mutable aliases. + +```ts +// Register an existing public image (remote pull + nydus optimization) +const snapshot = await sdk.snapshots.create({ + image: 'node:20', + memorySnapshot: true, // warm-start hibernation snapshot + onProgress: (p) => console.log(p.stage), // prepare/build/auth/push/register/... +}); + +// Build from a local context + Dockerfile +const snapshot = await sdk.snapshots.create({ + context: './app', + dockerfile: 'Dockerfile', +}); + +// Manage snapshots +await sdk.snapshots.list(); +await sdk.snapshots.getByAlias('latest'); +await sdk.snapshots.alias(snapshot.id, 'latest'); // mutable "latest" pointer +await sdk.snapshots.deleteByAlias('latest'); +``` + + +Two things to plan for when migrating template builds: + +- **Context builds require local Docker** in the TS SDK (the Python/CLI paths can build remotely). +- There is **no `docker-compose` multi-service** story. If your CodeSandbox template ran Postgres alongside your app via `docker-compose.yml`, you'll need to bake services into a single image or run them as processes. + + +The CLI equivalent of `csb build` is the only command Together's CLI provides: + +```bash +together-sandbox snapshots create \ + --image node:20 \ + --alias latest \ + --ci +# also supports --context / --dockerfile +``` + +## Sandbox lifecycle + +Hibernate and shutdown carry over directly. Several operations are missing and need to be handled by your own code. + +| Operation | CodeSandbox | Together | Migration note | +| --- | --- | --- | --- | +| Create | `sdk.sandboxes.create()` (boots VM) | `sdk.sandboxes.create()` (record only) | Add a `start()` step. | +| Start / connect | `sandbox.connect()` | `sdk.sandboxes.start(id, opts)` | Returns a connected `Sandbox`. | +| Resume | `sdk.sandboxes.resume(id)` | _(implicit)_ | `start()` auto-resumes a hibernated sandbox. | +| Hibernate | `sandbox.hibernate()` | `sandbox.hibernate()` | Direct. | +| Shutdown | `sandbox.shutdown()` | `sandbox.shutdown()` | Direct. | +| Restart | `sdk.sandboxes.restart(id)` | _(none)_ | Shutdown + start. | +| Delete | `sdk.sandboxes.delete(id)` | _(ephemeral only)_ | Use `ephemeral: true` to auto-delete on stop. | +| Fork | `create({ id })` / Live Fork | _(indirect)_ | Create from a version's `snapshot_id`, or `start(id, { versionNumber })`. | +| List / get / listRunning | ✅ | _(none)_ | **Track your own inventory.** | +| Specs | `vmTier` (named tiers) | `millicpu` / `memoryBytes` / `diskBytes` | Finer-grained on Together. | +| Live tier resize | `sandbox.updateTier()` | _(none)_ | Choose the right size at create. | +| TTL / idle auto-hibernate | `hibernationTimeoutSeconds`, `automaticWakeupConfig` | _(none)_ | **No timeout knobs** — drive lifecycle from your own server. | + + +The most important lifecycle gaps to design around: + +- **No `list` / `get` / `listRunning`.** You must persist your own record of which sandboxes exist and which are running. +- **No TTL or idle auto-hibernate.** There is no `hibernationTimeoutSeconds` equivalent. You are responsible for hibernating idle sandboxes yourself (this was already recommended best practice on CodeSandbox). + + +## Commands + +Raw execution is roughly at parity, and Together actually exposes **more** exec primitives. The main change is the namespace (`commands` → `execs`) and that there's no single `runBackground` helper — you assemble background execution from `create({ autostart })` + `streamOutput`/`getOutput`. + +```ts +// Before — CodeSandbox +const output = await client.commands.run('npm test'); +const bg = await client.commands.runBackground('npm run dev', { name: 'dev' }); +bg.onOutput((data) => console.log(data)); +``` + +```ts +// After — Together Sandbox +const { exitCode, output } = await sandbox.execs.exec('npm', ['test'], { + cwd: '/project/workspace', + env: { NODE_ENV: 'test' }, + user: 'root', +}); + +// Background command — assembled from primitives +const exec = await sandbox.execs.create({ autostart: true /* cmd, args, ... */ }); +for await (const chunk of sandbox.execs.streamOutput(exec.id /* { lastSequence } */)) { + console.log(chunk); // resumable SSE +} +await sandbox.execs.sendStdin(exec.id, { input: 'y\n' }); + +// Manage execs +await sandbox.execs.list(); +await sandbox.execs.get(exec.id); +await sandbox.execs.delete(exec.id); +``` + + +There is **no JS/Python code interpreter** (`client.interpreters.*`). If you relied on it, run code via `execs` instead — e.g. `node -e '...'` or `python -c '...'`. + + +## Filesystem + +Everyday operations map cleanly. The single `fs` namespace splits into `files` and `directories`, and Together adds `stat` and recursive `mkdir -p`. + +```ts +// Before — CodeSandbox +await client.fs.writeTextFile('/app/index.js', 'console.log(1)'); +const text = await client.fs.readTextFile('/app/index.js'); +await client.fs.readdir('/app'); +await client.fs.rename('/app/a.js', '/app/b.js'); +await client.fs.copy('/app/b.js', '/app/c.js'); +await client.fs.remove('/app/c.js'); +const watcher = await client.fs.watch('/app', { recursive: true }); +``` + +```ts +// After — Together Sandbox +await sandbox.files.create('/app/index.js', 'console.log(1)'); // string or Blob/File +const text = await sandbox.files.read('/app/index.js'); // returns string +await sandbox.directories.list('/app'); +await sandbox.directories.create('/app/nested'); // mkdir -p +await sandbox.files.move('/app/a.js', '/app/b.js'); +await sandbox.files.copy('/app/b.js', '/app/c.js'); +await sandbox.files.stat('/app/b.js'); +await sandbox.files.delete('/app/c.js'); +const watcher = sandbox.files.watch('/app', { + recursive: true, + ignorePatterns: ['node_modules'], +}); // SSE +``` + + +Bulk and binary file movement is clunkier on Together: + +- **No binary read** — `files.read` returns a string only. +- **No `batchWrite`** (compressed multi-file write). +- **No zip `download`** or dedicated upload helper. + +For binary writes, `files.create` does accept a `Blob`/`File`. For bulk transfer, you'll need to move files individually or tar them inside the sandbox via `execs`. + + +## Terminals & PTY + +Together has no dedicated terminals namespace. You get an interactive PTY through `execs` with `pty: true`, but there is **no persistence (`getAll`) and no resize**. + +```ts +// After — interactive PTY via execs +const term = await sandbox.execs.create({ pty: true /* cmd, args, ... */ }); +await sandbox.execs.sendStdin(term.id, { input: 'ls\n' }); +for await (const chunk of sandbox.execs.streamOutput(term.id)) { + process.stdout.write(chunk); +} +``` + +## Ports & hosts + +Together exposes **port discovery only**. This is the largest gap for products that serve running sandbox apps to end-users behind authenticated or branded URLs. + +```ts +// After — discovery only +const ports = await sandbox.ports.list(); +for await (const update of sandbox.ports.streamList()) { + console.log(update); // SSE — poll this to "wait for a port" +} +``` + + +There is **no preview-URL builder, no host tokens, no privacy modes, and no custom-domain support** in Together Sandbox. If your product relies on `getPreviewUrl(port)`, `hosts.createToken`, `public`/`private`/`public-hosts` privacy, or custom domains, there is no direct equivalent — you'll need to front sandboxes with your own proxy layer. + + +## Persistence & version history + +Memory-snapshot hibernation and cross-run persistence work the same way. Together adds **explicit, numbered version history** — a clean way to branch or roll back. + +```ts +// Start a specific version of a hibernated sandbox +const sandbox = await sdk.sandboxes.start(id, { versionNumber: 3 }); +``` + + +Version *retrieval* is available at the REST level (`current_version_number`, `/versions/{n}`) but is not yet wrapped as `sdk.sandboxes.versions.*` in the TS SDK. Together also adds `recovery_status` fields for crash recovery and an explicit `ephemeral: true` flag. + + +## Features without a direct equivalent + +These CodeSandbox capabilities have **no counterpart** in the Together Sandbox TS SDK. Plan a different approach before migrating if you depend on them. + +| Capability | What to do instead | +| --- | --- | +| **Sessions / multi-user** (`createSession`, browser/node clients, per-session git & env, reconnect, collaboration) | No session layer exists. The unit is a single `Sandbox` with auto-wired agent auth. Multi-tenant isolation must be built in your own application layer. | +| **Tasks** (`client.tasks`, `.codesandbox/tasks.json`) | The Pint agent defines tasks but the TS SDK doesn't wrap them. Run dev servers / long-running processes as background `execs`. | +| **Setup hooks** (`client.setup`, `setupTasks`) | Bake environment setup into the Dockerfile, or use the boot-then-hibernate `memorySnapshot` flow. | +| **Git integration** (`connect({ git })`, default git persistence) | Run `git clone` / `git` commands via `execs` inside the sandbox. | +| **Hosts & preview URLs** | Front sandboxes with your own proxy (see Ports above). | +| **Code interpreters** (`client.interpreters.*`) | Run `node -e` / `python -c` via `execs`. | +| **Browser Preview API** (`@codesandbox/sdk/browser`) | No equivalent. | +| **OpenTelemetry tracing** (`{ tracer }`) | No equivalent; rely on the typed `HttpError` and `onRetry` hooks. | +| **Documented pricing / rate limits / region selection** | Not documented for Together Sandbox; billing routes through your Together AI account. `cluster` is read-only. | + +## Where Together Sandbox is nicer + +Migrating isn't only about gaps — several areas are genuinely better: + +- **Cleaner snapshot API** — structured `onProgress`, list/get/delete by id or alias, direct public-image registration, mutable aliases. +- **Built-in client reliability** — configurable retry with backoff/jitter and a typed `HttpError` (`status === 0` transport sentinel). +- **Richer exec primitives** — `list` / `get` / `delete` / `streamList`, resumable SSE output (`lastSequence`), `sendStdin`, run-as `user`. +- **Explicit version history** — numbered versions and `versionNumber` start for branching/rollback. +- **Finer CPU granularity** (`millicpu`) and an explicit `ephemeral` flag. +- **SSE-first streaming** across watch, exec output/list, and ports. + +## Migration checklist + +1. Swap the package (`@codesandbox/sdk` → `together-sandbox`) and env var (`CSB_API_KEY` → `TOGETHER_API_KEY`). +2. Convert templates to snapshots; move any `docker-compose` services into a single image. +3. Split the single `connect()` into `create()` + `start()`. +4. Rename `commands` → `execs`, `fs` → `files`/`directories`, and reassemble any `runBackground` usage. +5. Build your own **sandbox inventory** (list/get) and **idle-hibernation** logic — there are no built-in knobs. +6. Replace sessions, tasks, setup hooks, git integration, and preview hosts with application-layer or in-Dockerfile equivalents. +7. Adopt the built-in `retry` config, and exclude `snapshots.create` from it. + +## Need help? + +If you run into issues during migration, contact us at [support@codesandbox.io](mailto:support@codesandbox.io). + + From 22308be3d2d8f576c3711d96bd2ad8043e295676 Mon Sep 17 00:00:00 2001 From: Christian Alfoni Date: Mon, 15 Jun 2026 09:19:57 +0200 Subject: [PATCH 2/6] docs(sdk): remove stray tags that broke MDX compile Co-Authored-By: Claude Opus 4.8 --- .../projects-docs/pages/sdk/migrate-to-together-sandbox.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx index 26033ea4..4a911fdf 100644 --- a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx +++ b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx @@ -328,5 +328,3 @@ Migrating isn't only about gaps — several areas are genuinely better: ## Need help? If you run into issues during migration, contact us at [support@codesandbox.io](mailto:support@codesandbox.io). - - From ad3f431435d5b1a29a2c11905de635c777c105b0 Mon Sep 17 00:00:00 2001 From: Christian Alfoni Date: Mon, 15 Jun 2026 09:34:58 +0200 Subject: [PATCH 3/6] docs(sdk): revise Together migration guide per review - Reframe intro: new infrastructure, factual tone only (no subjective claims) - Drop client option comparisons (baseUrl/retry/tracer) - Move "features without a direct equivalent" and the checklist near the top - Remove the "where Together is nicer" section Co-Authored-By: Claude Opus 4.8 --- .../pages/sdk/migrate-to-together-sandbox.mdx | 111 +++++++----------- 1 file changed, 42 insertions(+), 69 deletions(-) diff --git a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx index 4a911fdf..1f5c24bc 100644 --- a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx +++ b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx @@ -9,9 +9,7 @@ import { Callout } from 'nextra-theme-docs' This guide walks you through moving from `@codesandbox/sdk` to `together-sandbox`. -Together Sandbox is a hosted wrapper over the same underlying infrastructure that powers the CodeSandbox SDK — the in-VM agent, the Firecracker microVMs, and the memory-snapshot mechanism are the same engine. The difference is **how much of that engine each SDK surfaces**: CodeSandbox exposes a broad, ergonomic API; Together Sandbox exposes a deliberately minimal slice with a strong snapshot, streaming, and client-reliability story. - -This means the **core loop carries over cleanly** — build a snapshot, create a sandbox, run commands, read and write files, watch ports — but several developer-experience and platform features either need a different approach or aren't available. This guide covers both. +Together Sandbox runs on new, improved infrastructure and exposes a smaller, differently shaped API. The core loop maps across — build a snapshot, create a sandbox, run commands, read and write files, watch ports — but several APIs are used differently and some features have no direct equivalent. This guide covers how each capability is used in `together-sandbox`. This guide focuses on the TypeScript SDK and CLI. If you're on the Python or Node-specific paths, the concepts map across but the exact APIs differ. @@ -23,15 +21,42 @@ Before you start, here's how CodeSandbox concepts translate to Together Sandbox. | CodeSandbox SDK | Together Sandbox SDK | Difference | | --- | --- | --- | -| Template | Snapshot | Same underlying memory-snapshot mechanism. | -| `CodeSandbox` client | `TogetherSandbox` client | Together adds `baseUrl` and `retry`; CodeSandbox adds an OpenTelemetry `tracer`. | +| Template | Snapshot | Same memory-snapshot concept. | +| `CodeSandbox` client | `TogetherSandbox` client | Top-level client. | | `sandbox.connect()` → `client` | `sdk.sandboxes.start()` → `Sandbox` | Together separates create (record only) from start (boots the VM). | -| `client.commands` | `sandbox.execs` | Together exposes more primitives but no `runBackground` helper. | +| `client.commands` | `sandbox.execs` | No `runBackground` helper — assembled from primitives. | | `client.fs` | `sandbox.files` / `sandbox.directories` | Split into two namespaces. | | `client.ports` | `sandbox.ports` | Discovery only — no preview-URL builder or host tokens. | -| Sessions | _(none)_ | Together has no session / multi-user layer. | +| Sessions | _(none)_ | No session / multi-user layer. | | `client.tasks`, `client.setup` | _(none)_ | Setup moves into the Docker image. | +## Features without a direct equivalent + +These CodeSandbox capabilities have **no counterpart** in the Together Sandbox TS SDK. Plan a different approach before migrating if you depend on them. + +| Capability | What to do instead | +| --- | --- | +| **Sessions / multi-user** (`createSession`, browser/node clients, per-session git & env, reconnect, collaboration) | No session layer exists. The unit is a single `Sandbox` with auto-wired agent auth. Multi-tenant isolation must be built in your own application layer. | +| **Tasks** (`client.tasks`, `.codesandbox/tasks.json`) | The agent defines tasks but the TS SDK doesn't wrap them. Run dev servers / long-running processes as background `execs`. | +| **Setup hooks** (`client.setup`, `setupTasks`) | Bake environment setup into the Dockerfile, or use the boot-then-hibernate `memorySnapshot` flow. | +| **Git integration** (`connect({ git })`, default git persistence) | Run `git clone` / `git` commands via `execs` inside the sandbox. | +| **Hosts & preview URLs** | Front sandboxes with your own proxy (see [Ports & hosts](#ports--hosts) below). | +| **Code interpreters** (`client.interpreters.*`) | Run `node -e` / `python -c` via `execs`. | +| **Browser Preview API** (`@codesandbox/sdk/browser`) | No equivalent. | +| **OpenTelemetry tracing** (`{ tracer }`) | No equivalent. | +| **Sandbox `list` / `get` / `listRunning`** | Track your own inventory of which sandboxes exist and are running. | +| **TTL / idle auto-hibernate** (`hibernationTimeoutSeconds`, `automaticWakeupConfig`) | Drive hibernation from your own server. | + +## Migration checklist + +1. Swap the package (`@codesandbox/sdk` → `together-sandbox`) and env var (`CSB_API_KEY` → `TOGETHER_API_KEY`). +2. Convert templates to snapshots; move any `docker-compose` services into a single image. +3. Split the single `connect()` into `create()` + `start()`. +4. Rename `commands` → `execs`, `fs` → `files`/`directories`, and reassemble any `runBackground` usage. +5. Build your own **sandbox inventory** (list/get) and **idle-hibernation** logic — there are no built-in knobs. +6. Replace sessions, tasks, setup hooks, git integration, and preview hosts with application-layer or in-Dockerfile equivalents. +7. Exclude `snapshots.create` from any retry logic — it is not idempotent. + ## Installation & authentication Swap the package and the API key environment variable. Together Sandbox requires Node 18+. @@ -60,23 +85,8 @@ const sdk = new CodeSandbox(); // reads CSB_API_KEY import { TogetherSandbox } from 'together-sandbox'; const sdk = new TogetherSandbox(); // reads TOGETHER_API_KEY - -// Optional configuration -const sdk = new TogetherSandbox({ - apiKey: process.env.TOGETHER_API_KEY, - baseUrl: 'https://...', // override the management API endpoint - retry: { // built-in configurable retry - maxAttempts: 3, - shouldRetry: (err) => err.status === 0 || err.status >= 500, - onRetry: (err, attempt) => console.warn(`retry ${attempt}`, err), - }, -}); ``` - -The built-in `retry` config (backoff + jitter, `shouldRetry`, `onRetry`) and the typed `HttpError` (`{ message, status }`, where `status === 0` signals a transport error) are a genuine improvement over the CodeSandbox client. Make sure to **exclude `snapshots.create` from retries** — it is not idempotent. - - ## The core loop The biggest structural change is that **creating a sandbox and booting its VM are two steps**. In CodeSandbox, `create()` boots a VM and you `connect()` to it. In Together, `create()` makes a record (state `created`) and `start()` boots the VM and returns a connected `Sandbox`. @@ -103,7 +113,7 @@ console.log(output); ## Templates → Snapshots -This is Together's strongest area, and the snapshot API is arguably cleaner than CodeSandbox templates — structured progress callbacks, list/get/delete by id or alias, direct registration of any public image, and mutable aliases. +Templates become snapshots. The snapshot API supports structured progress callbacks, list/get/delete by id or alias, direct registration of any public image, and mutable aliases. ```ts // Register an existing public image (remote pull + nydus optimization) @@ -166,12 +176,12 @@ Hibernate and shutdown carry over directly. Several operations are missing and n The most important lifecycle gaps to design around: - **No `list` / `get` / `listRunning`.** You must persist your own record of which sandboxes exist and which are running. -- **No TTL or idle auto-hibernate.** There is no `hibernationTimeoutSeconds` equivalent. You are responsible for hibernating idle sandboxes yourself (this was already recommended best practice on CodeSandbox). +- **No TTL or idle auto-hibernate.** There is no `hibernationTimeoutSeconds` equivalent. You are responsible for hibernating idle sandboxes yourself. ## Commands -Raw execution is roughly at parity, and Together actually exposes **more** exec primitives. The main change is the namespace (`commands` → `execs`) and that there's no single `runBackground` helper — you assemble background execution from `create({ autostart })` + `streamOutput`/`getOutput`. +The namespace changes from `commands` to `execs`. There is no single `runBackground` helper — background execution is assembled from `create({ autostart })` + `streamOutput`/`getOutput`. ```ts // Before — CodeSandbox @@ -207,7 +217,7 @@ There is **no JS/Python code interpreter** (`client.interpreters.*`). If you rel ## Filesystem -Everyday operations map cleanly. The single `fs` namespace splits into `files` and `directories`, and Together adds `stat` and recursive `mkdir -p`. +The single `fs` namespace splits into `files` and `directories`. `stat` and recursive `mkdir -p` are available via these namespaces. ```ts // Before — CodeSandbox @@ -237,18 +247,18 @@ const watcher = sandbox.files.watch('/app', { ``` -Bulk and binary file movement is clunkier on Together: +Bulk and binary file movement works differently on Together: - **No binary read** — `files.read` returns a string only. - **No `batchWrite`** (compressed multi-file write). - **No zip `download`** or dedicated upload helper. -For binary writes, `files.create` does accept a `Blob`/`File`. For bulk transfer, you'll need to move files individually or tar them inside the sandbox via `execs`. +For binary writes, `files.create` accepts a `Blob`/`File`. For bulk transfer, move files individually or tar them inside the sandbox via `execs`. ## Terminals & PTY -Together has no dedicated terminals namespace. You get an interactive PTY through `execs` with `pty: true`, but there is **no persistence (`getAll`) and no resize**. +Together has no dedicated terminals namespace. You get an interactive PTY through `execs` with `pty: true`. There is **no persistence (`getAll`) and no resize**. ```ts // After — interactive PTY via execs @@ -261,7 +271,7 @@ for await (const chunk of sandbox.execs.streamOutput(term.id)) { ## Ports & hosts -Together exposes **port discovery only**. This is the largest gap for products that serve running sandbox apps to end-users behind authenticated or branded URLs. +Together exposes **port discovery only**. ```ts // After — discovery only @@ -277,7 +287,7 @@ There is **no preview-URL builder, no host tokens, no privacy modes, and no cust ## Persistence & version history -Memory-snapshot hibernation and cross-run persistence work the same way. Together adds **explicit, numbered version history** — a clean way to branch or roll back. +Memory-snapshot hibernation and cross-run persistence work the same way. Together exposes explicit, numbered version history. ```ts // Start a specific version of a hibernated sandbox @@ -285,46 +295,9 @@ const sandbox = await sdk.sandboxes.start(id, { versionNumber: 3 }); ``` -Version *retrieval* is available at the REST level (`current_version_number`, `/versions/{n}`) but is not yet wrapped as `sdk.sandboxes.versions.*` in the TS SDK. Together also adds `recovery_status` fields for crash recovery and an explicit `ephemeral: true` flag. +Version *retrieval* is available at the REST level (`current_version_number`, `/versions/{n}`) but is not yet wrapped as `sdk.sandboxes.versions.*` in the TS SDK. Together also exposes `recovery_status` fields for crash recovery and an explicit `ephemeral: true` flag. -## Features without a direct equivalent - -These CodeSandbox capabilities have **no counterpart** in the Together Sandbox TS SDK. Plan a different approach before migrating if you depend on them. - -| Capability | What to do instead | -| --- | --- | -| **Sessions / multi-user** (`createSession`, browser/node clients, per-session git & env, reconnect, collaboration) | No session layer exists. The unit is a single `Sandbox` with auto-wired agent auth. Multi-tenant isolation must be built in your own application layer. | -| **Tasks** (`client.tasks`, `.codesandbox/tasks.json`) | The Pint agent defines tasks but the TS SDK doesn't wrap them. Run dev servers / long-running processes as background `execs`. | -| **Setup hooks** (`client.setup`, `setupTasks`) | Bake environment setup into the Dockerfile, or use the boot-then-hibernate `memorySnapshot` flow. | -| **Git integration** (`connect({ git })`, default git persistence) | Run `git clone` / `git` commands via `execs` inside the sandbox. | -| **Hosts & preview URLs** | Front sandboxes with your own proxy (see Ports above). | -| **Code interpreters** (`client.interpreters.*`) | Run `node -e` / `python -c` via `execs`. | -| **Browser Preview API** (`@codesandbox/sdk/browser`) | No equivalent. | -| **OpenTelemetry tracing** (`{ tracer }`) | No equivalent; rely on the typed `HttpError` and `onRetry` hooks. | -| **Documented pricing / rate limits / region selection** | Not documented for Together Sandbox; billing routes through your Together AI account. `cluster` is read-only. | - -## Where Together Sandbox is nicer - -Migrating isn't only about gaps — several areas are genuinely better: - -- **Cleaner snapshot API** — structured `onProgress`, list/get/delete by id or alias, direct public-image registration, mutable aliases. -- **Built-in client reliability** — configurable retry with backoff/jitter and a typed `HttpError` (`status === 0` transport sentinel). -- **Richer exec primitives** — `list` / `get` / `delete` / `streamList`, resumable SSE output (`lastSequence`), `sendStdin`, run-as `user`. -- **Explicit version history** — numbered versions and `versionNumber` start for branching/rollback. -- **Finer CPU granularity** (`millicpu`) and an explicit `ephemeral` flag. -- **SSE-first streaming** across watch, exec output/list, and ports. - -## Migration checklist - -1. Swap the package (`@codesandbox/sdk` → `together-sandbox`) and env var (`CSB_API_KEY` → `TOGETHER_API_KEY`). -2. Convert templates to snapshots; move any `docker-compose` services into a single image. -3. Split the single `connect()` into `create()` + `start()`. -4. Rename `commands` → `execs`, `fs` → `files`/`directories`, and reassemble any `runBackground` usage. -5. Build your own **sandbox inventory** (list/get) and **idle-hibernation** logic — there are no built-in knobs. -6. Replace sessions, tasks, setup hooks, git integration, and preview hosts with application-layer or in-Dockerfile equivalents. -7. Adopt the built-in `retry` config, and exclude `snapshots.create` from it. - ## Need help? If you run into issues during migration, contact us at [support@codesandbox.io](mailto:support@codesandbox.io). From 930d1d0e1e3eac99ef0aed8dbf9ee4e77953f529 Mon Sep 17 00:00:00 2001 From: Christian Alfoni Date: Mon, 15 Jun 2026 09:42:03 +0200 Subject: [PATCH 4/6] docs(sdk): drop vocabulary mapping, checklist before features Co-Authored-By: Claude Opus 4.8 --- .../pages/sdk/migrate-to-together-sandbox.mdx | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx index 1f5c24bc..91f7a41a 100644 --- a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx +++ b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx @@ -15,20 +15,15 @@ Together Sandbox runs on new, improved infrastructure and exposes a smaller, dif This guide focuses on the TypeScript SDK and CLI. If you're on the Python or Node-specific paths, the concepts map across but the exact APIs differ. -## Vocabulary mapping - -Before you start, here's how CodeSandbox concepts translate to Together Sandbox. +## Migration checklist -| CodeSandbox SDK | Together Sandbox SDK | Difference | -| --- | --- | --- | -| Template | Snapshot | Same memory-snapshot concept. | -| `CodeSandbox` client | `TogetherSandbox` client | Top-level client. | -| `sandbox.connect()` → `client` | `sdk.sandboxes.start()` → `Sandbox` | Together separates create (record only) from start (boots the VM). | -| `client.commands` | `sandbox.execs` | No `runBackground` helper — assembled from primitives. | -| `client.fs` | `sandbox.files` / `sandbox.directories` | Split into two namespaces. | -| `client.ports` | `sandbox.ports` | Discovery only — no preview-URL builder or host tokens. | -| Sessions | _(none)_ | No session / multi-user layer. | -| `client.tasks`, `client.setup` | _(none)_ | Setup moves into the Docker image. | +1. Swap the package (`@codesandbox/sdk` → `together-sandbox`) and env var (`CSB_API_KEY` → `TOGETHER_API_KEY`). +2. Convert templates to snapshots; move any `docker-compose` services into a single image. +3. Split the single `connect()` into `create()` + `start()`. +4. Rename `commands` → `execs`, `fs` → `files`/`directories`, and reassemble any `runBackground` usage. +5. Build your own **sandbox inventory** (list/get) and **idle-hibernation** logic — there are no built-in knobs. +6. Replace sessions, tasks, setup hooks, git integration, and preview hosts with application-layer or in-Dockerfile equivalents. +7. Exclude `snapshots.create` from any retry logic — it is not idempotent. ## Features without a direct equivalent @@ -47,16 +42,6 @@ These CodeSandbox capabilities have **no counterpart** in the Together Sandbox T | **Sandbox `list` / `get` / `listRunning`** | Track your own inventory of which sandboxes exist and are running. | | **TTL / idle auto-hibernate** (`hibernationTimeoutSeconds`, `automaticWakeupConfig`) | Drive hibernation from your own server. | -## Migration checklist - -1. Swap the package (`@codesandbox/sdk` → `together-sandbox`) and env var (`CSB_API_KEY` → `TOGETHER_API_KEY`). -2. Convert templates to snapshots; move any `docker-compose` services into a single image. -3. Split the single `connect()` into `create()` + `start()`. -4. Rename `commands` → `execs`, `fs` → `files`/`directories`, and reassemble any `runBackground` usage. -5. Build your own **sandbox inventory** (list/get) and **idle-hibernation** logic — there are no built-in knobs. -6. Replace sessions, tasks, setup hooks, git integration, and preview hosts with application-layer or in-Dockerfile equivalents. -7. Exclude `snapshots.create` from any retry logic — it is not idempotent. - ## Installation & authentication Swap the package and the API key environment variable. Together Sandbox requires Node 18+. From 5dea2ff734971524220ddf79ed603ac0a5e4c9c7 Mon Sep 17 00:00:00 2001 From: Christian Alfoni Date: Mon, 15 Jun 2026 13:27:41 +0200 Subject: [PATCH 5/6] docs(sdk): restructure Together migration guide into three parts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Part 1 "Should you migrate?" — decision overview leading with the two proxy requirements (previews + browser access) and the unsupported- functionality table - Part 2 "Supported features" — capability→API map plus before/after usage - Part 3 — how to implement the preview proxy and browser proxy Co-Authored-By: Claude Opus 4.8 --- .../pages/sdk/migrate-to-together-sandbox.mdx | 190 ++++++++++++------ 1 file changed, 133 insertions(+), 57 deletions(-) diff --git a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx index 91f7a41a..3784dd6e 100644 --- a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx +++ b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx @@ -7,42 +7,65 @@ import { Callout } from 'nextra-theme-docs' # Migrate from CodeSandbox SDK to Together Sandbox -This guide walks you through moving from `@codesandbox/sdk` to `together-sandbox`. +This guide walks you through moving from `@codesandbox/sdk` to `together-sandbox`. It is organized in three parts: -Together Sandbox runs on new, improved infrastructure and exposes a smaller, differently shaped API. The core loop maps across — build a snapshot, create a sandbox, run commands, read and write files, watch ports — but several APIs are used differently and some features have no direct equivalent. This guide covers how each capability is used in `together-sandbox`. +1. **[Should you migrate?](#1-should-you-migrate)** — an overview of what is no longer supported, so you can decide whether the migration is worth it. +2. **[Supported features](#2-supported-features)** — the capabilities that carry over, and the `together-sandbox` API that provides each one. +3. **[Implementing previews and browser access](#3-implementing-previews-and-browser-access)** — how to build the proxy layers that replace CodeSandbox previews and the browser SDK. This guide focuses on the TypeScript SDK and CLI. If you're on the Python or Node-specific paths, the concepts map across but the exact APIs differ. -## Migration checklist +## 1. Should you migrate? -1. Swap the package (`@codesandbox/sdk` → `together-sandbox`) and env var (`CSB_API_KEY` → `TOGETHER_API_KEY`). -2. Convert templates to snapshots; move any `docker-compose` services into a single image. -3. Split the single `connect()` into `create()` + `start()`. -4. Rename `commands` → `execs`, `fs` → `files`/`directories`, and reassemble any `runBackground` usage. -5. Build your own **sandbox inventory** (list/get) and **idle-hibernation** logic — there are no built-in knobs. -6. Replace sessions, tasks, setup hooks, git integration, and preview hosts with application-layer or in-Dockerfile equivalents. -7. Exclude `snapshots.create` from any retry logic — it is not idempotent. +Together Sandbox runs on new, improved infrastructure and exposes a smaller, differently shaped API. The core sandbox loop carries over cleanly — build a snapshot, create a sandbox, run commands, read and write files, watch ports — but several CodeSandbox capabilities are not supported and have to be rebuilt in your own application layer. -## Features without a direct equivalent +Read this section first. If any of the items below are central to your product, factor the extra work into your decision. + +### You must build two things yourself + + +These two are the most common blockers. Both require running your own server. + +- **Previews require your own proxy.** Together Sandbox exposes port *discovery* only — there is no preview-URL builder, no host tokens, no privacy modes, and no custom domains. To serve a running app from a sandbox to end users, you must run a reverse proxy that forwards requests to the sandbox's open port and applies your own access control. See [Preview proxy](#preview-proxy). +- **Using the SDK from the browser requires a server proxy.** There is no `@codesandbox/sdk/browser` equivalent and no session layer. The `TOGETHER_API_KEY` is a server credential and must never reach the browser, so any browser client has to call your own backend, which relays requests to the Together API. See [Browser proxy](#browser-proxy). + + +### Functionality that is no longer supported These CodeSandbox capabilities have **no counterpart** in the Together Sandbox TS SDK. Plan a different approach before migrating if you depend on them. | Capability | What to do instead | | --- | --- | -| **Sessions / multi-user** (`createSession`, browser/node clients, per-session git & env, reconnect, collaboration) | No session layer exists. The unit is a single `Sandbox` with auto-wired agent auth. Multi-tenant isolation must be built in your own application layer. | -| **Tasks** (`client.tasks`, `.codesandbox/tasks.json`) | The agent defines tasks but the TS SDK doesn't wrap them. Run dev servers / long-running processes as background `execs`. | +| **Previews / hosts** (`getPreviewUrl`, `hosts.createToken`, privacy modes, custom domains) | Run your own [preview proxy](#preview-proxy) over the discovered ports. | +| **Browser / Node clients & sessions** (`createSession`, `@codesandbox/sdk/browser`, per-session git & env, reconnect, collaboration) | Run a [browser proxy](#browser-proxy); reimplement per-user isolation in your backend. | +| **Tasks** (`client.tasks`, `.codesandbox/tasks.json`) | Run dev servers / long-running processes as background `execs`. | | **Setup hooks** (`client.setup`, `setupTasks`) | Bake environment setup into the Dockerfile, or use the boot-then-hibernate `memorySnapshot` flow. | | **Git integration** (`connect({ git })`, default git persistence) | Run `git clone` / `git` commands via `execs` inside the sandbox. | -| **Hosts & preview URLs** | Front sandboxes with your own proxy (see [Ports & hosts](#ports--hosts) below). | | **Code interpreters** (`client.interpreters.*`) | Run `node -e` / `python -c` via `execs`. | -| **Browser Preview API** (`@codesandbox/sdk/browser`) | No equivalent. | | **OpenTelemetry tracing** (`{ tracer }`) | No equivalent. | | **Sandbox `list` / `get` / `listRunning`** | Track your own inventory of which sandboxes exist and are running. | | **TTL / idle auto-hibernate** (`hibernationTimeoutSeconds`, `automaticWakeupConfig`) | Drive hibernation from your own server. | +| **Live tier resize** (`updateTier`), **`restart`**, **`delete`** (non-ephemeral) | Pick the size at create; shutdown + start to restart; use `ephemeral: true` to auto-delete. | +| **`docker-compose` multi-service** | Bake services into a single image or run them as processes. | + +## 2. Supported features + +Everything in this section carries over. The table maps each capability to the `together-sandbox` API, and the subsections show before/after usage. -## Installation & authentication +| Capability | CodeSandbox | Together Sandbox | +| --- | --- | --- | +| Create & start | `create()` + `connect()` | `create()` + `start()` | +| Templates / snapshots | Templates / `csb build` | `sdk.snapshots.*` | +| Run commands | `client.commands` | `sandbox.execs` | +| Filesystem | `client.fs` | `sandbox.files` / `sandbox.directories` | +| Interactive PTY | `client.terminals` | `sandbox.execs` (`pty: true`) | +| Port discovery | `client.ports` | `sandbox.ports` | +| Hibernate / shutdown | `sandbox.hibernate()` / `shutdown()` | `sandbox.hibernate()` / `shutdown()` | +| Persistence & versioning | implicit | `start(id, { versionNumber })` | + +### Installation & authentication Swap the package and the API key environment variable. Together Sandbox requires Node 18+. @@ -56,7 +79,7 @@ npm install together-sandbox # auth via TOGETHER_API_KEY The Together CLI ships as a prebuilt binary (darwin/linux/win) via a shell installer rather than as a bundled `csb` binary. -## Client initialization +### Client initialization ```ts // Before @@ -72,7 +95,7 @@ import { TogetherSandbox } from 'together-sandbox'; const sdk = new TogetherSandbox(); // reads TOGETHER_API_KEY ``` -## The core loop +### The core loop The biggest structural change is that **creating a sandbox and booting its VM are two steps**. In CodeSandbox, `create()` boots a VM and you `connect()` to it. In Together, `create()` makes a record (state `created`) and `start()` boots the VM and returns a connected `Sandbox`. @@ -96,7 +119,7 @@ const { exitCode, output } = await sandbox.execs.exec('echo', ["Hello World"]); console.log(output); ``` -## Templates → Snapshots +### Templates → Snapshots Templates become snapshots. The snapshot API supports structured progress callbacks, list/get/delete by id or alias, direct registration of any public image, and mutable aliases. @@ -122,10 +145,7 @@ await sdk.snapshots.deleteByAlias('latest'); ``` -Two things to plan for when migrating template builds: - -- **Context builds require local Docker** in the TS SDK (the Python/CLI paths can build remotely). -- There is **no `docker-compose` multi-service** story. If your CodeSandbox template ran Postgres alongside your app via `docker-compose.yml`, you'll need to bake services into a single image or run them as processes. +**Context builds require local Docker** in the TS SDK (the Python/CLI paths can build remotely), and `snapshots.create` is **not idempotent** — exclude it from any retry logic. The CLI equivalent of `csb build` is the only command Together's CLI provides: @@ -138,9 +158,9 @@ together-sandbox snapshots create \ # also supports --context / --dockerfile ``` -## Sandbox lifecycle +### Sandbox lifecycle -Hibernate and shutdown carry over directly. Several operations are missing and need to be handled by your own code. +Hibernate and shutdown carry over directly. The operations marked _(none)_ are covered in [Part 1](#functionality-that-is-no-longer-supported). | Operation | CodeSandbox | Together | Migration note | | --- | --- | --- | --- | @@ -152,19 +172,10 @@ Hibernate and shutdown carry over directly. Several operations are missing and n | Restart | `sdk.sandboxes.restart(id)` | _(none)_ | Shutdown + start. | | Delete | `sdk.sandboxes.delete(id)` | _(ephemeral only)_ | Use `ephemeral: true` to auto-delete on stop. | | Fork | `create({ id })` / Live Fork | _(indirect)_ | Create from a version's `snapshot_id`, or `start(id, { versionNumber })`. | -| List / get / listRunning | ✅ | _(none)_ | **Track your own inventory.** | +| List / get / listRunning | ✅ | _(none)_ | Track your own inventory. | | Specs | `vmTier` (named tiers) | `millicpu` / `memoryBytes` / `diskBytes` | Finer-grained on Together. | -| Live tier resize | `sandbox.updateTier()` | _(none)_ | Choose the right size at create. | -| TTL / idle auto-hibernate | `hibernationTimeoutSeconds`, `automaticWakeupConfig` | _(none)_ | **No timeout knobs** — drive lifecycle from your own server. | - -The most important lifecycle gaps to design around: - -- **No `list` / `get` / `listRunning`.** You must persist your own record of which sandboxes exist and which are running. -- **No TTL or idle auto-hibernate.** There is no `hibernationTimeoutSeconds` equivalent. You are responsible for hibernating idle sandboxes yourself. - - -## Commands +### Commands The namespace changes from `commands` to `execs`. There is no single `runBackground` helper — background execution is assembled from `create({ autostart })` + `streamOutput`/`getOutput`. @@ -196,11 +207,7 @@ await sandbox.execs.get(exec.id); await sandbox.execs.delete(exec.id); ``` - -There is **no JS/Python code interpreter** (`client.interpreters.*`). If you relied on it, run code via `execs` instead — e.g. `node -e '...'` or `python -c '...'`. - - -## Filesystem +### Filesystem The single `fs` namespace splits into `files` and `directories`. `stat` and recursive `mkdir -p` are available via these namespaces. @@ -232,18 +239,12 @@ const watcher = sandbox.files.watch('/app', { ``` -Bulk and binary file movement works differently on Together: - -- **No binary read** — `files.read` returns a string only. -- **No `batchWrite`** (compressed multi-file write). -- **No zip `download`** or dedicated upload helper. - -For binary writes, `files.create` accepts a `Blob`/`File`. For bulk transfer, move files individually or tar them inside the sandbox via `execs`. +`files.read` returns a string only (no binary read), there is no `batchWrite`, and no zip `download`/upload helper. For binary writes, `files.create` accepts a `Blob`/`File`; for bulk transfer, move files individually or tar them inside the sandbox via `execs`. -## Terminals & PTY +### Terminals & PTY -Together has no dedicated terminals namespace. You get an interactive PTY through `execs` with `pty: true`. There is **no persistence (`getAll`) and no resize**. +Together has no dedicated terminals namespace. You get an interactive PTY through `execs` with `pty: true`. There is no persistence (`getAll`) and no resize. ```ts // After — interactive PTY via execs @@ -254,9 +255,9 @@ for await (const chunk of sandbox.execs.streamOutput(term.id)) { } ``` -## Ports & hosts +### Ports -Together exposes **port discovery only**. +Together exposes **port discovery only**. Serving those ports to end users is covered in [Part 3](#preview-proxy). ```ts // After — discovery only @@ -266,11 +267,7 @@ for await (const update of sandbox.ports.streamList()) { } ``` - -There is **no preview-URL builder, no host tokens, no privacy modes, and no custom-domain support** in Together Sandbox. If your product relies on `getPreviewUrl(port)`, `hosts.createToken`, `public`/`private`/`public-hosts` privacy, or custom domains, there is no direct equivalent — you'll need to front sandboxes with your own proxy layer. - - -## Persistence & version history +### Persistence & version history Memory-snapshot hibernation and cross-run persistence work the same way. Together exposes explicit, numbered version history. @@ -283,6 +280,85 @@ const sandbox = await sdk.sandboxes.start(id, { versionNumber: 3 }); Version *retrieval* is available at the REST level (`current_version_number`, `/versions/{n}`) but is not yet wrapped as `sdk.sandboxes.versions.*` in the TS SDK. Together also exposes `recovery_status` fields for crash recovery and an explicit `ephemeral: true` flag. +## 3. Implementing previews and browser access + +These are the two pieces CodeSandbox provided out of the box that you now run yourself. Both live in your backend, where the `TOGETHER_API_KEY` stays. + +### Preview proxy + +CodeSandbox gave each open port a public, optionally token-gated URL. Together exposes only the *list* of open ports, so to show a running app to an end user you run a reverse proxy in front of the sandbox: + +1. Start the sandbox and discover the open port with `sandbox.ports.list()`. +2. Resolve the sandbox's reachable upstream address from its connection details. +3. Forward incoming requests from your public URL to that address, applying your own authentication, routing, and (if needed) custom-domain handling — this is where you reimplement host tokens and privacy modes. + +```ts +// Express-style preview proxy (illustrative) +import express from 'express'; +import { createProxyMiddleware } from 'http-proxy-middleware'; +import { TogetherSandbox } from 'together-sandbox'; + +const sdk = new TogetherSandbox(); +const app = express(); + +// e.g. https://preview.example.com/:sandboxId/:port/... +app.use('/:sandboxId/:port', async (req, res, next) => { + const { sandboxId, port } = req.params; + + // Your own authentication / authorization happens here. + // This replaces CodeSandbox host tokens and privacy modes. + if (!(await isAllowed(req, sandboxId))) return res.sendStatus(403); + + const sandbox = await sdk.sandboxes.start(sandboxId); + await sandbox.ports.list(); // confirm the port is open + const target = resolveUpstream(sandbox, Number(port)); // map to the sandbox address + + return createProxyMiddleware({ target, changeOrigin: true, ws: true })(req, res, next); +}); + +app.listen(8080); +``` + + +Cache the started sandbox and its upstream target instead of starting on every request, and keep idle sandboxes hibernated yourself — there is no automatic TTL. + + +### Browser proxy + +The `TOGETHER_API_KEY` is a server credential. CodeSandbox solved browser usage with sessions and `@codesandbox/sdk/browser`; Together has neither, so browser clients call **your** backend, which holds the key and talks to the Together API. Your backend is also where you reimplement the per-user isolation that sessions used to provide. + +```ts +// Backend — the only place the API key lives +import express from 'express'; +import { TogetherSandbox } from 'together-sandbox'; + +const sdk = new TogetherSandbox(); // reads TOGETHER_API_KEY +const app = express(); +app.use(express.json()); + +// Authenticate the end user, then scope what they're allowed to do. +app.post('/api/sandboxes/:id/exec', authenticateUser, async (req, res) => { + const sandbox = await sdk.sandboxes.start(req.params.id); + const { cmd, args } = req.body; + const result = await sandbox.execs.exec(cmd, args); + res.json(result); +}); + +app.listen(3000); +``` + +```ts +// Browser — no SDK, no API key; just calls your backend +const res = await fetch(`/api/sandboxes/${id}/exec`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ cmd: 'npm', args: ['test'] }), +}); +const { output } = await res.json(); +``` + +For streaming surfaces (exec output, file watch, ports), forward the SDK's SSE streams through your backend to the browser using `EventSource` or a `ReadableStream` response. + ## Need help? If you run into issues during migration, contact us at [support@codesandbox.io](mailto:support@codesandbox.io). From d4dcbddedb932217c3cb4d0eb0421b9ea7065c8c Mon Sep 17 00:00:00 2001 From: Christian Alfoni Date: Mon, 15 Jun 2026 15:07:33 +0200 Subject: [PATCH 6/6] docs(sdk): restructure migration guide into two paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeSandbox is now part of Together AI. Rework the guide to recommend a feature-parity destination first, then Together Sandbox: - Option 1 (recommended): Daytona — closest match to the CodeSandbox SDK (fork, snapshots, preview URLs + token auth, terminals, git, code interpreters, list/get, resize, CLI), with a feature-mapping table and before/after examples. - Option 2: Together Sandbox — highlights only the unsupported features and the two server-side pieces you must build (preview proxy, browser proxy). Rename page to "Migration Guide" (migration-guide.mdx). Co-Authored-By: Claude Opus 4.8 --- packages/projects-docs/pages/sdk/_meta.json | 2 +- .../pages/sdk/migrate-to-together-sandbox.mdx | 364 ------------------ .../pages/sdk/migration-guide.mdx | 269 +++++++++++++ 3 files changed, 270 insertions(+), 365 deletions(-) delete mode 100644 packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx create mode 100644 packages/projects-docs/pages/sdk/migration-guide.mdx diff --git a/packages/projects-docs/pages/sdk/_meta.json b/packages/projects-docs/pages/sdk/_meta.json index 1a0db702..b05b5469 100644 --- a/packages/projects-docs/pages/sdk/_meta.json +++ b/packages/projects-docs/pages/sdk/_meta.json @@ -49,7 +49,7 @@ }, "template-library": "Template Library", "version-history": "Version History", - "migrate-to-together-sandbox": "Migrate to Together Sandbox", + "migration-guide": "Migration Guide", "contact": { "title": "Contact Us", "href": "https://codesandbox.io/support#form", diff --git a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx b/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx deleted file mode 100644 index 3784dd6e..00000000 --- a/packages/projects-docs/pages/sdk/migrate-to-together-sandbox.mdx +++ /dev/null @@ -1,364 +0,0 @@ ---- -title: Migrate to Together Sandbox -description: How to migrate from the CodeSandbox SDK to the Together Sandbox SDK. ---- - -import { Callout } from 'nextra-theme-docs' - -# Migrate from CodeSandbox SDK to Together Sandbox - -This guide walks you through moving from `@codesandbox/sdk` to `together-sandbox`. It is organized in three parts: - -1. **[Should you migrate?](#1-should-you-migrate)** — an overview of what is no longer supported, so you can decide whether the migration is worth it. -2. **[Supported features](#2-supported-features)** — the capabilities that carry over, and the `together-sandbox` API that provides each one. -3. **[Implementing previews and browser access](#3-implementing-previews-and-browser-access)** — how to build the proxy layers that replace CodeSandbox previews and the browser SDK. - - -This guide focuses on the TypeScript SDK and CLI. If you're on the Python or Node-specific paths, the concepts map across but the exact APIs differ. - - -## 1. Should you migrate? - -Together Sandbox runs on new, improved infrastructure and exposes a smaller, differently shaped API. The core sandbox loop carries over cleanly — build a snapshot, create a sandbox, run commands, read and write files, watch ports — but several CodeSandbox capabilities are not supported and have to be rebuilt in your own application layer. - -Read this section first. If any of the items below are central to your product, factor the extra work into your decision. - -### You must build two things yourself - - -These two are the most common blockers. Both require running your own server. - -- **Previews require your own proxy.** Together Sandbox exposes port *discovery* only — there is no preview-URL builder, no host tokens, no privacy modes, and no custom domains. To serve a running app from a sandbox to end users, you must run a reverse proxy that forwards requests to the sandbox's open port and applies your own access control. See [Preview proxy](#preview-proxy). -- **Using the SDK from the browser requires a server proxy.** There is no `@codesandbox/sdk/browser` equivalent and no session layer. The `TOGETHER_API_KEY` is a server credential and must never reach the browser, so any browser client has to call your own backend, which relays requests to the Together API. See [Browser proxy](#browser-proxy). - - -### Functionality that is no longer supported - -These CodeSandbox capabilities have **no counterpart** in the Together Sandbox TS SDK. Plan a different approach before migrating if you depend on them. - -| Capability | What to do instead | -| --- | --- | -| **Previews / hosts** (`getPreviewUrl`, `hosts.createToken`, privacy modes, custom domains) | Run your own [preview proxy](#preview-proxy) over the discovered ports. | -| **Browser / Node clients & sessions** (`createSession`, `@codesandbox/sdk/browser`, per-session git & env, reconnect, collaboration) | Run a [browser proxy](#browser-proxy); reimplement per-user isolation in your backend. | -| **Tasks** (`client.tasks`, `.codesandbox/tasks.json`) | Run dev servers / long-running processes as background `execs`. | -| **Setup hooks** (`client.setup`, `setupTasks`) | Bake environment setup into the Dockerfile, or use the boot-then-hibernate `memorySnapshot` flow. | -| **Git integration** (`connect({ git })`, default git persistence) | Run `git clone` / `git` commands via `execs` inside the sandbox. | -| **Code interpreters** (`client.interpreters.*`) | Run `node -e` / `python -c` via `execs`. | -| **OpenTelemetry tracing** (`{ tracer }`) | No equivalent. | -| **Sandbox `list` / `get` / `listRunning`** | Track your own inventory of which sandboxes exist and are running. | -| **TTL / idle auto-hibernate** (`hibernationTimeoutSeconds`, `automaticWakeupConfig`) | Drive hibernation from your own server. | -| **Live tier resize** (`updateTier`), **`restart`**, **`delete`** (non-ephemeral) | Pick the size at create; shutdown + start to restart; use `ephemeral: true` to auto-delete. | -| **`docker-compose` multi-service** | Bake services into a single image or run them as processes. | - -## 2. Supported features - -Everything in this section carries over. The table maps each capability to the `together-sandbox` API, and the subsections show before/after usage. - -| Capability | CodeSandbox | Together Sandbox | -| --- | --- | --- | -| Create & start | `create()` + `connect()` | `create()` + `start()` | -| Templates / snapshots | Templates / `csb build` | `sdk.snapshots.*` | -| Run commands | `client.commands` | `sandbox.execs` | -| Filesystem | `client.fs` | `sandbox.files` / `sandbox.directories` | -| Interactive PTY | `client.terminals` | `sandbox.execs` (`pty: true`) | -| Port discovery | `client.ports` | `sandbox.ports` | -| Hibernate / shutdown | `sandbox.hibernate()` / `shutdown()` | `sandbox.hibernate()` / `shutdown()` | -| Persistence & versioning | implicit | `start(id, { versionNumber })` | - -### Installation & authentication - -Swap the package and the API key environment variable. Together Sandbox requires Node 18+. - -```bash -# Before -npm install @codesandbox/sdk # auth via CSB_API_KEY - -# After -npm install together-sandbox # auth via TOGETHER_API_KEY -``` - -The Together CLI ships as a prebuilt binary (darwin/linux/win) via a shell installer rather than as a bundled `csb` binary. - -### Client initialization - -```ts -// Before -import { CodeSandbox } from '@codesandbox/sdk'; - -const sdk = new CodeSandbox(); // reads CSB_API_KEY -``` - -```ts -// After -import { TogetherSandbox } from 'together-sandbox'; - -const sdk = new TogetherSandbox(); // reads TOGETHER_API_KEY -``` - -### The core loop - -The biggest structural change is that **creating a sandbox and booting its VM are two steps**. In CodeSandbox, `create()` boots a VM and you `connect()` to it. In Together, `create()` makes a record (state `created`) and `start()` boots the VM and returns a connected `Sandbox`. - -```ts -// Before — CodeSandbox -const sdk = new CodeSandbox(); -const sandbox = await sdk.sandboxes.create({ id: 'your-template-id' }); -const client = await sandbox.connect(); - -const output = await client.commands.run("echo 'Hello World'"); -console.log(output); -``` - -```ts -// After — Together Sandbox -const sdk = new TogetherSandbox(); -const { id } = await sdk.sandboxes.create({ /* params */ }); -const sandbox = await sdk.sandboxes.start(id); - -const { exitCode, output } = await sandbox.execs.exec('echo', ["Hello World"]); -console.log(output); -``` - -### Templates → Snapshots - -Templates become snapshots. The snapshot API supports structured progress callbacks, list/get/delete by id or alias, direct registration of any public image, and mutable aliases. - -```ts -// Register an existing public image (remote pull + nydus optimization) -const snapshot = await sdk.snapshots.create({ - image: 'node:20', - memorySnapshot: true, // warm-start hibernation snapshot - onProgress: (p) => console.log(p.stage), // prepare/build/auth/push/register/... -}); - -// Build from a local context + Dockerfile -const snapshot = await sdk.snapshots.create({ - context: './app', - dockerfile: 'Dockerfile', -}); - -// Manage snapshots -await sdk.snapshots.list(); -await sdk.snapshots.getByAlias('latest'); -await sdk.snapshots.alias(snapshot.id, 'latest'); // mutable "latest" pointer -await sdk.snapshots.deleteByAlias('latest'); -``` - - -**Context builds require local Docker** in the TS SDK (the Python/CLI paths can build remotely), and `snapshots.create` is **not idempotent** — exclude it from any retry logic. - - -The CLI equivalent of `csb build` is the only command Together's CLI provides: - -```bash -together-sandbox snapshots create \ - --image node:20 \ - --alias latest \ - --ci -# also supports --context / --dockerfile -``` - -### Sandbox lifecycle - -Hibernate and shutdown carry over directly. The operations marked _(none)_ are covered in [Part 1](#functionality-that-is-no-longer-supported). - -| Operation | CodeSandbox | Together | Migration note | -| --- | --- | --- | --- | -| Create | `sdk.sandboxes.create()` (boots VM) | `sdk.sandboxes.create()` (record only) | Add a `start()` step. | -| Start / connect | `sandbox.connect()` | `sdk.sandboxes.start(id, opts)` | Returns a connected `Sandbox`. | -| Resume | `sdk.sandboxes.resume(id)` | _(implicit)_ | `start()` auto-resumes a hibernated sandbox. | -| Hibernate | `sandbox.hibernate()` | `sandbox.hibernate()` | Direct. | -| Shutdown | `sandbox.shutdown()` | `sandbox.shutdown()` | Direct. | -| Restart | `sdk.sandboxes.restart(id)` | _(none)_ | Shutdown + start. | -| Delete | `sdk.sandboxes.delete(id)` | _(ephemeral only)_ | Use `ephemeral: true` to auto-delete on stop. | -| Fork | `create({ id })` / Live Fork | _(indirect)_ | Create from a version's `snapshot_id`, or `start(id, { versionNumber })`. | -| List / get / listRunning | ✅ | _(none)_ | Track your own inventory. | -| Specs | `vmTier` (named tiers) | `millicpu` / `memoryBytes` / `diskBytes` | Finer-grained on Together. | - -### Commands - -The namespace changes from `commands` to `execs`. There is no single `runBackground` helper — background execution is assembled from `create({ autostart })` + `streamOutput`/`getOutput`. - -```ts -// Before — CodeSandbox -const output = await client.commands.run('npm test'); -const bg = await client.commands.runBackground('npm run dev', { name: 'dev' }); -bg.onOutput((data) => console.log(data)); -``` - -```ts -// After — Together Sandbox -const { exitCode, output } = await sandbox.execs.exec('npm', ['test'], { - cwd: '/project/workspace', - env: { NODE_ENV: 'test' }, - user: 'root', -}); - -// Background command — assembled from primitives -const exec = await sandbox.execs.create({ autostart: true /* cmd, args, ... */ }); -for await (const chunk of sandbox.execs.streamOutput(exec.id /* { lastSequence } */)) { - console.log(chunk); // resumable SSE -} -await sandbox.execs.sendStdin(exec.id, { input: 'y\n' }); - -// Manage execs -await sandbox.execs.list(); -await sandbox.execs.get(exec.id); -await sandbox.execs.delete(exec.id); -``` - -### Filesystem - -The single `fs` namespace splits into `files` and `directories`. `stat` and recursive `mkdir -p` are available via these namespaces. - -```ts -// Before — CodeSandbox -await client.fs.writeTextFile('/app/index.js', 'console.log(1)'); -const text = await client.fs.readTextFile('/app/index.js'); -await client.fs.readdir('/app'); -await client.fs.rename('/app/a.js', '/app/b.js'); -await client.fs.copy('/app/b.js', '/app/c.js'); -await client.fs.remove('/app/c.js'); -const watcher = await client.fs.watch('/app', { recursive: true }); -``` - -```ts -// After — Together Sandbox -await sandbox.files.create('/app/index.js', 'console.log(1)'); // string or Blob/File -const text = await sandbox.files.read('/app/index.js'); // returns string -await sandbox.directories.list('/app'); -await sandbox.directories.create('/app/nested'); // mkdir -p -await sandbox.files.move('/app/a.js', '/app/b.js'); -await sandbox.files.copy('/app/b.js', '/app/c.js'); -await sandbox.files.stat('/app/b.js'); -await sandbox.files.delete('/app/c.js'); -const watcher = sandbox.files.watch('/app', { - recursive: true, - ignorePatterns: ['node_modules'], -}); // SSE -``` - - -`files.read` returns a string only (no binary read), there is no `batchWrite`, and no zip `download`/upload helper. For binary writes, `files.create` accepts a `Blob`/`File`; for bulk transfer, move files individually or tar them inside the sandbox via `execs`. - - -### Terminals & PTY - -Together has no dedicated terminals namespace. You get an interactive PTY through `execs` with `pty: true`. There is no persistence (`getAll`) and no resize. - -```ts -// After — interactive PTY via execs -const term = await sandbox.execs.create({ pty: true /* cmd, args, ... */ }); -await sandbox.execs.sendStdin(term.id, { input: 'ls\n' }); -for await (const chunk of sandbox.execs.streamOutput(term.id)) { - process.stdout.write(chunk); -} -``` - -### Ports - -Together exposes **port discovery only**. Serving those ports to end users is covered in [Part 3](#preview-proxy). - -```ts -// After — discovery only -const ports = await sandbox.ports.list(); -for await (const update of sandbox.ports.streamList()) { - console.log(update); // SSE — poll this to "wait for a port" -} -``` - -### Persistence & version history - -Memory-snapshot hibernation and cross-run persistence work the same way. Together exposes explicit, numbered version history. - -```ts -// Start a specific version of a hibernated sandbox -const sandbox = await sdk.sandboxes.start(id, { versionNumber: 3 }); -``` - - -Version *retrieval* is available at the REST level (`current_version_number`, `/versions/{n}`) but is not yet wrapped as `sdk.sandboxes.versions.*` in the TS SDK. Together also exposes `recovery_status` fields for crash recovery and an explicit `ephemeral: true` flag. - - -## 3. Implementing previews and browser access - -These are the two pieces CodeSandbox provided out of the box that you now run yourself. Both live in your backend, where the `TOGETHER_API_KEY` stays. - -### Preview proxy - -CodeSandbox gave each open port a public, optionally token-gated URL. Together exposes only the *list* of open ports, so to show a running app to an end user you run a reverse proxy in front of the sandbox: - -1. Start the sandbox and discover the open port with `sandbox.ports.list()`. -2. Resolve the sandbox's reachable upstream address from its connection details. -3. Forward incoming requests from your public URL to that address, applying your own authentication, routing, and (if needed) custom-domain handling — this is where you reimplement host tokens and privacy modes. - -```ts -// Express-style preview proxy (illustrative) -import express from 'express'; -import { createProxyMiddleware } from 'http-proxy-middleware'; -import { TogetherSandbox } from 'together-sandbox'; - -const sdk = new TogetherSandbox(); -const app = express(); - -// e.g. https://preview.example.com/:sandboxId/:port/... -app.use('/:sandboxId/:port', async (req, res, next) => { - const { sandboxId, port } = req.params; - - // Your own authentication / authorization happens here. - // This replaces CodeSandbox host tokens and privacy modes. - if (!(await isAllowed(req, sandboxId))) return res.sendStatus(403); - - const sandbox = await sdk.sandboxes.start(sandboxId); - await sandbox.ports.list(); // confirm the port is open - const target = resolveUpstream(sandbox, Number(port)); // map to the sandbox address - - return createProxyMiddleware({ target, changeOrigin: true, ws: true })(req, res, next); -}); - -app.listen(8080); -``` - - -Cache the started sandbox and its upstream target instead of starting on every request, and keep idle sandboxes hibernated yourself — there is no automatic TTL. - - -### Browser proxy - -The `TOGETHER_API_KEY` is a server credential. CodeSandbox solved browser usage with sessions and `@codesandbox/sdk/browser`; Together has neither, so browser clients call **your** backend, which holds the key and talks to the Together API. Your backend is also where you reimplement the per-user isolation that sessions used to provide. - -```ts -// Backend — the only place the API key lives -import express from 'express'; -import { TogetherSandbox } from 'together-sandbox'; - -const sdk = new TogetherSandbox(); // reads TOGETHER_API_KEY -const app = express(); -app.use(express.json()); - -// Authenticate the end user, then scope what they're allowed to do. -app.post('/api/sandboxes/:id/exec', authenticateUser, async (req, res) => { - const sandbox = await sdk.sandboxes.start(req.params.id); - const { cmd, args } = req.body; - const result = await sandbox.execs.exec(cmd, args); - res.json(result); -}); - -app.listen(3000); -``` - -```ts -// Browser — no SDK, no API key; just calls your backend -const res = await fetch(`/api/sandboxes/${id}/exec`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ cmd: 'npm', args: ['test'] }), -}); -const { output } = await res.json(); -``` - -For streaming surfaces (exec output, file watch, ports), forward the SDK's SSE streams through your backend to the browser using `EventSource` or a `ReadableStream` response. - -## Need help? - -If you run into issues during migration, contact us at [support@codesandbox.io](mailto:support@codesandbox.io). diff --git a/packages/projects-docs/pages/sdk/migration-guide.mdx b/packages/projects-docs/pages/sdk/migration-guide.mdx new file mode 100644 index 00000000..ff4d4d5e --- /dev/null +++ b/packages/projects-docs/pages/sdk/migration-guide.mdx @@ -0,0 +1,269 @@ +--- +title: SDK Migration Guide +description: How to migrate off the CodeSandbox SDK — to Daytona for feature parity, or to Together Sandbox. +--- + +import { Callout } from 'nextra-theme-docs' + +# Migrate from the CodeSandbox SDK + +CodeSandbox is now part of Together AI, and we recommend migrating off `@codesandbox/sdk`. This guide covers two destinations: + +- **[Option 1 — Daytona](#option-1--daytona-recommended-for-feature-parity)** *(recommended for feature parity)*: the provider whose SDK matches the broadest set of CodeSandbox SDK capabilities — sandboxes, fork, snapshots, preview URLs with token auth, terminals, git, and code interpreters. +- **[Option 2 — Together Sandbox](#option-2--together-sandbox)**: the Together AI sandbox SDK. It covers the core sandbox loop but does **not** support several CodeSandbox features, which you'd need to rebuild yourself. + + +This guide focuses on the TypeScript SDK and CLI. If you're on the Python or Node-specific paths, the concepts map across but the exact APIs differ. + + +## Choosing a path + +| Your situation | Recommended path | +| --- | --- | +| You rely on previews/hosts, sessions, terminals, git, code interpreters, or fork | **Daytona** — these have direct equivalents | +| You only use the core loop (create, run commands, files, ports) and want to stay within Together AI | **Together Sandbox** — minimal API, rebuild the gaps yourself | + +If in doubt, start with **Daytona**: it requires the fewest changes to an existing CodeSandbox SDK integration. + +## Option 1 — Daytona (recommended for feature parity) + +[Daytona](https://www.daytona.io/docs/) provides programmatic sandboxes with the closest match to the CodeSandbox SDK feature set. Almost every CodeSandbox capability has a direct equivalent. + +### Feature mapping + +| CodeSandbox SDK | Daytona | +| --- | --- | +| `sandboxes.create()` + `connect()` | `daytona.create()` | +| `sandboxes.list()` / `get()` | `daytona.list()` / `daytona.get(id)` | +| Templates / `csb build` | Snapshots + the [declarative builder](https://www.daytona.io/docs/en/declarative-builder/) | +| `sandboxes.resume/hibernate/shutdown` | `sandbox.start()` / `archive()` / `stop()` | +| Fork | `sandbox._experimental_fork()` | +| `updateTier` | `sandbox.resize({ cpu, memory, disk })` | +| `client.commands` | `sandbox.process.executeCommand()` | +| `client.interpreters.*` | `sandbox.process.codeRun()` / `codeInterpreter` | +| `client.fs` | `sandbox.fs` | +| `client.terminals` (PTY) | PTY support | +| Git integration | `sandbox.git` | +| `ports.getPreviewUrl()` + host tokens | `sandbox.getPreviewLink(port)` / `getSignedPreviewUrl()` | + +### Installation & client + +```bash +# Before +npm install @codesandbox/sdk # auth via CSB_API_KEY + +# After +npm install @daytona/sdk # auth via DAYTONA_API_KEY +``` + +```ts +// Before — CodeSandbox +import { CodeSandbox } from '@codesandbox/sdk'; + +const sdk = new CodeSandbox(); +const sandbox = await sdk.sandboxes.create({ id: 'your-template-id' }); +const client = await sandbox.connect(); + +const output = await client.commands.run("echo 'Hello World'"); +console.log(output); +``` + +```ts +// After — Daytona +import { Daytona } from '@daytona/sdk'; + +const daytona = new Daytona(); +const sandbox = await daytona.create(); + +const response = await sandbox.process.executeCommand("echo 'Hello World'"); +console.log(response.result); +``` + +### Previews (the big win over Together Sandbox) + +Preview URLs and token auth map directly onto CodeSandbox hosts and host tokens, so you don't have to build a proxy. + +```ts +// Before — CodeSandbox +const url = client.hosts.getUrl(3000); +const { token } = await sdk.hosts.createToken(sandbox.id); +``` + +```ts +// After — Daytona +// Standard preview link — token sent via header +const preview = await sandbox.getPreviewLink(3000); +await fetch(preview.url, { headers: { 'x-daytona-preview-token': preview.token } }); + +// Signed preview URL — token embedded in the URL, time-limited & shareable +const signed = await sandbox.getSignedPreviewUrl(3000, 3600); +await fetch(signed.url); +``` + +### Fork & snapshots + +```ts +// Fork a sandbox (filesystem + memory), like CodeSandbox fork +const forked = await sandbox._experimental_fork({ name: 'feature-branch' }); + +// Create a reusable snapshot, like a CodeSandbox template +await sandbox._experimental_createSnapshot('my-snapshot'); +``` + + +Daytona also provides `sandbox.git`, a stateful Python `codeInterpreter`, LSP servers (`createLspServer`), volumes, and a full CLI. See the [Daytona docs](https://www.daytona.io/docs/) for the complete API. The main concept without a one-to-one match is CodeSandbox's per-session/browser-client model — use Daytona preview tokens and your own auth for multi-user access. + + +## Option 2 — Together Sandbox + +[Together Sandbox](https://github.com/togethercomputer/together-sandbox) (`together-sandbox`) runs on new infrastructure within your Together AI account. It covers the core sandbox loop — create a sandbox, run commands, read and write files, watch ports — but exposes a deliberately minimal API. Choose this path only if the missing features below aren't core to your product, or you're willing to rebuild them. + +### Features without a direct equivalent + +These CodeSandbox capabilities have **no counterpart** in the Together Sandbox TS SDK. + +| Capability | What to do instead | +| --- | --- | +| **Previews / hosts** (`getPreviewUrl`, `hosts.createToken`, privacy modes, custom domains) | Run your own [preview proxy](#implementing-previews) over the discovered ports. | +| **Browser / Node clients & sessions** (`createSession`, `@codesandbox/sdk/browser`, per-session git & env, reconnect, collaboration) | Run a [browser proxy](#implementing-browser-access); reimplement per-user isolation in your backend. | +| **Tasks** (`client.tasks`, `.codesandbox/tasks.json`) | Run dev servers / long-running processes as background `execs`. | +| **Setup hooks** (`client.setup`, `setupTasks`) | Bake setup into the Dockerfile, or use the boot-then-hibernate `memorySnapshot` flow. | +| **Git integration** (`connect({ git })`) | Run `git` commands via `execs` inside the sandbox. | +| **Code interpreters** (`client.interpreters.*`) | Run `node -e` / `python -c` via `execs`. | +| **Terminals namespace** (`client.terminals`) | Use `execs.create({ pty: true })`; no persistence (`getAll`) or resize. | +| **OpenTelemetry tracing** (`{ tracer }`) | No equivalent. | +| **Sandbox `list` / `get` / `listRunning`** | Track your own inventory of which sandboxes exist and are running. | +| **TTL / idle auto-hibernate** (`hibernationTimeoutSeconds`, `automaticWakeupConfig`) | Drive hibernation from your own server. | +| **Live tier resize** (`updateTier`), **`restart`**, **`delete`** (non-ephemeral) | Pick the size at create; shutdown + start to restart; use `ephemeral: true` to auto-delete. | +| **`docker-compose` multi-service** | Bake services into a single image or run them as processes. | + + +Two of these require running your own server, and are the most common blockers: + +- **Previews require your own proxy.** Together exposes port *discovery* only — no preview URLs, host tokens, or privacy modes. See [Implementing previews](#implementing-previews). +- **Using the SDK from the browser requires a server proxy.** There is no browser client and no session layer. `TOGETHER_API_KEY` must stay server-side. See [Implementing browser access](#implementing-browser-access). + + +### What changes for the supported core loop + +Installation and auth swap the package and env var: + +```bash +# Before +npm install @codesandbox/sdk # auth via CSB_API_KEY + +# After +npm install together-sandbox # auth via TOGETHER_API_KEY (Node 18+) +``` + +The main structural change is that **create and start are two steps**: `create()` makes a record (state `created`) and `start()` boots the VM and returns a connected `Sandbox`. + +```ts +// Before — CodeSandbox +const sdk = new CodeSandbox(); +const sandbox = await sdk.sandboxes.create({ id: 'your-template-id' }); +const client = await sandbox.connect(); +const output = await client.commands.run("echo 'Hello World'"); +``` + +```ts +// After — Together Sandbox +import { TogetherSandbox } from 'together-sandbox'; + +const sdk = new TogetherSandbox(); +const { id } = await sdk.sandboxes.create({ /* params */ }); +const sandbox = await sdk.sandboxes.start(id); +const { output } = await sandbox.execs.exec('echo', ["Hello World"]); +``` + +The supported namespaces are renamed: + +| CodeSandbox | Together Sandbox | +| --- | --- | +| `client.commands.run()` | `sandbox.execs.exec()` (and `execs.create/list/get/streamOutput/sendStdin`) | +| `client.fs.*` | `sandbox.files.*` / `sandbox.directories.*` | +| `client.ports.*` | `sandbox.ports.list()` / `streamList()` (discovery only) | +| Templates / `csb build` | `sdk.snapshots.create()` (and the `together-sandbox snapshots create` CLI) | +| `sandbox.hibernate()` / `shutdown()` | `sandbox.hibernate()` / `shutdown()` (unchanged) | + + +`snapshots.create` is **not idempotent** — exclude it from any retry logic. Context builds also require local Docker in the TS SDK. + + +### Implementing previews + +CodeSandbox gave each open port a public, optionally token-gated URL. Together exposes only the *list* of open ports (`sandbox.ports.list()`), so to show a running app to an end user you run a reverse proxy in front of the sandbox: + +1. Start the sandbox and discover the open port with `sandbox.ports.list()`. +2. Resolve the sandbox's reachable upstream address from its connection details. +3. Forward requests from your public URL to that address, applying your own authentication and routing — this is where you reimplement host tokens and privacy modes. + +```ts +// Express-style preview proxy (illustrative) +import express from 'express'; +import { createProxyMiddleware } from 'http-proxy-middleware'; +import { TogetherSandbox } from 'together-sandbox'; + +const sdk = new TogetherSandbox(); +const app = express(); + +// e.g. https://preview.example.com/:sandboxId/:port/... +app.use('/:sandboxId/:port', async (req, res, next) => { + const { sandboxId, port } = req.params; + + // Your own authentication / authorization (replaces host tokens & privacy modes). + if (!(await isAllowed(req, sandboxId))) return res.sendStatus(403); + + const sandbox = await sdk.sandboxes.start(sandboxId); + await sandbox.ports.list(); // confirm the port is open + const target = resolveUpstream(sandbox, Number(port)); // map to the sandbox address + + return createProxyMiddleware({ target, changeOrigin: true, ws: true })(req, res, next); +}); + +app.listen(8080); +``` + + +Cache the started sandbox and its upstream target instead of starting on every request, and keep idle sandboxes hibernated yourself — there is no automatic TTL. + + +### Implementing browser access + +`TOGETHER_API_KEY` is a server credential. CodeSandbox solved browser usage with sessions and `@codesandbox/sdk/browser`; Together has neither, so browser clients call **your** backend, which holds the key and talks to the Together API. Your backend is also where you reimplement the per-user isolation that sessions used to provide. + +```ts +// Backend — the only place the API key lives +import express from 'express'; +import { TogetherSandbox } from 'together-sandbox'; + +const sdk = new TogetherSandbox(); // reads TOGETHER_API_KEY +const app = express(); +app.use(express.json()); + +// Authenticate the end user, then scope what they're allowed to do. +app.post('/api/sandboxes/:id/exec', authenticateUser, async (req, res) => { + const sandbox = await sdk.sandboxes.start(req.params.id); + const { cmd, args } = req.body; + const result = await sandbox.execs.exec(cmd, args); + res.json(result); +}); + +app.listen(3000); +``` + +```ts +// Browser — no SDK, no API key; just calls your backend +const res = await fetch(`/api/sandboxes/${id}/exec`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ cmd: 'npm', args: ['test'] }), +}); +const { output } = await res.json(); +``` + +For streaming surfaces (exec output, file watch, ports), forward the SDK's SSE streams through your backend to the browser using `EventSource` or a `ReadableStream` response. + +## Need help? + +If you run into issues during migration, contact us at [support@codesandbox.io](mailto:support@codesandbox.io).