TypeScript reference
Import Beachside and Image from beachside. Each @beachside/<provider> package exports its adapter definition as <provider>Adapter (for example daytonaAdapter from @beachside/daytona) and an optional factory named after the provider. See the generated export index for public names.
Consumer vocabulary and compatibility
Section titled “Consumer vocabulary and compatibility”Use BeachsideClient, SandboxHandle, SnapshotHandle, VolumeHandle, ImageHandle, OperationHandle<T>, RecoveryReference, ConnectOptions and CloseOptions in application annotations. Adapter-author contracts remain in @beachside/adapter.
Factory results preserve the complete advanced API and existing runtime identity. During this compatibility stage their declarations use BeachsideClient<true> and SandboxHandle<true>; true is a type parameter, not a runtime setting. Default annotations retain the previous structural views, which applications may implement or mock. Concrete operation results provide their kind type parameter, preserving kind narrowing and continue(), while OperationHandle<T> alone keeps the original minimal observation/wait interface.
Existing AdapterDirectClient, AdapterSandbox, AdapterSnapshot, AdapterVolume, AdapterOperation, AdapterRecoveryReference, AdapterConnectOptions, DirectConnectOptions, DirectBeachsideClient and DirectSandboxHandle exports remain supported. Runtime constructors and instanceof checks still use the same classes. Migrate annotations incrementally; no wrapper or identity conversion is needed. Inferred complete clients retain advanced compatibility access to session, telemetry and scope, with deprecation guidance. Ordinary BeachsideClient annotations omit this machinery; prefer documented operations and resource references. Exported advanced methods and prepare/submit/recover capabilities remain available.
For ordinary directory listings, use listFiles: it returns a complete bounded array or rejects. Use readDirectory when completeness and observation metadata matter. Both APIs remain available with their distinct semantics.
Connect
Section titled “Connect”Every adapter connects the same way: pass its definition with credentials and config.
import { e2bAdapter } from "@beachside/e2b";
const beachside = await Beachside.connect({ adapter: e2bAdapter, credentials: { apiKey }, config: { network: "blocked" },});Beachside.connect validates config and credentials against the adapter’s schemas, verifies the native connection, and owns the session. Use the SDK in a server-side Node.js or Bun process. The same shape works for any adapter built with defineAdapter, including third-party ones:
const beachside = await Beachside.connect({ adapter: acme, credentials: { token }, config: { region: "us" }, onReference: async (reference) => { await persistReference(reference); },});Every first-party package also exports a factory named after the provider (daytona, e2b, boxd, boat, runloop, modal, vercel) that takes credentials and config in one object, as in Beachside.connect(e2b({ apiKey })). It does no provider IO and builds the same setup; it helps when code picks a provider at run time, since every factory returns the same BoundAdapter type. Definitions are named <provider>Adapter.
acme is the installed adapter definition; config and credentials follow its schemas. Ordinary capture results provide snapshot.reference for application persistence. The existing explicit connection onReference callback is a compatibility API for operation checkpoints: it is awaited before dispatch and when native tokens or pending observations change. See Results and saved resources and the tested custom adapter example.
Images
Section titled “Images”| API | Behavior |
|---|---|
Image.prepared(value) |
Select an existing image ID/name or scoped PreparedImage result. |
Image.oci(reference) |
Describe an OCI source supported by the adapter. |
beachside.images.build({ source }, { signal? }) |
Build and wait for a scoped prepared image plus retained-resource metadata. |
beachside.images.submitBuild(input, options?) |
Submit a build and return an operation handle. |
beachside.images.get(reference) |
Reopen a scoped image reference as an ImageHandle. |
beachside.images.list({ limit, cursor? }) |
Read scoped image inventory and coverage. |
image.inspect() |
Read state, visibility, source and known native dependencies. |
image.delete() / beachside.images.delete(reference) |
Delete an eligible image; submitDelete returns an operation handle. |
Build results contain prepared, retainedResources and reference, an image resource reference whose ownership is verified-created only when the adapter proved the build created it. A prepared handle is bound to its verified provider scope. Destroying a sandbox does not delete a built image. See Images and networking.
Sandboxes
Section titled “Sandboxes”| API | Behavior |
|---|---|
beachside.sandboxes.create(input?, { signal? }) |
Submit creation and wait for a sandbox handle. |
beachside.sandboxes.submitCreate(input?, options?) |
Submit creation and return an operation handle. |
beachside.sandboxes.get(reference, { signal?, pollMs? }) |
Reopen the same verified sandbox without resuming or renewing it. |
beachside.sandboxes.list({ limit?, cursor? }) |
Page scoped sandboxes with state, labels, creation time, coverage and references. |
beachside.sandboxes.checkCreate(input?) |
Read-only evaluation of environment and snapshot requirements. |
beachside.capabilities() / box.capabilities() |
Observe supported profiles and current eligibility. |
box.reference |
Verified SandboxReference, or null for adapters without identity support. |
box.id |
Sandbox identifier. |
box.preview(port, { signal? }) |
Resolve ephemeral native HTTP access; availability is not readiness. |
box.inspect({ signal? }) |
Read current state where supported. |
box.exec(input, { signal?, onOutput? }) |
Run a command and return bounded binary output. |
box.submitExec(input, options?) |
Submit execution and return an operation handle. |
box.readTextFile(path, { maxBytes?, signal? }) |
Read the complete bounded file as UTF-8. |
box.writeTextFile(path, text, { overwrite?, maxBytes?, signal? }) |
Write UTF-8; byte limits apply; overwrite defaults to false. |
box.readFile(path, { maxBytes?, signal? }) |
Read bounded bytes (1 MiB default, explicit max 16 MiB). |
box.writeFile(path, bytes, { overwrite?, maxBytes?, signal? }) |
Write bounded bytes (1 MiB default, explicit max 16 MiB). |
box.readDirectory(path, { signal? }) |
Bounded immediate entries, completeness and observation timestamp. |
box.listFiles(path, { signal? }) |
Complete bounded immediate entries, or reject. |
box.statFile(path, { followSymlinks?, signal? }) |
Type and available metadata; final link unfollowed by default. |
box.fileExists(path, { signal? }) |
False only for confirmed entry absence. |
box.makeDirectory(path, { recursive?, signal? }) |
Create a directory, recursion opt-in. |
box.removeFile(path, { recursive?, signal? }) |
Remove an entry; missing succeeds, recursion opt-in. |
box.copyFile(source, destination, { overwrite?, signal? }) |
Copy regular-file bytes; no-clobber by default. |
box.moveFile(source, destination, { overwrite?, signal? }) |
Same-filesystem native rename; no-clobber by default. |
box.readFileStream(path, options?) |
Incremental AsyncIterable<Uint8Array> with transfer timeouts. |
box.writeFileStream(path, chunks, options?) |
Incremental upload; return confirmed byte count. |
box.walkFiles(path, { maxDepth?, maxEntries?, exclude?, signal? }) |
Bounded depth-first traversal with exact subtree exclusions. |
box.readTextLines(path, { maxLineBytes?, ...transferOptions }) |
Incremental UTF-8 lines; delimiters removed and line bytes bounded. |
box.destroy({ signal?, storage? }) |
Wait for confirmed compute termination. |
beachside.close() |
Release client resources; does not destroy sandboxes. |
Creation input accepts environment, network (deprecated alias networkPolicy), region, labels, env (sandbox-wide variables; see environment variables), mounts: MountSpec[] and requirements: { snapshot: SnapshotRequest }. Omitted input resolves the adapter’s configured environment. Optional operations reject with UNSUPPORTED when their hooks or requested guarantees are absent; current access/state can instead be unavailable or unknown.
Lifecycle and saved storage
Section titled “Lifecycle and saved storage”| API | Behavior |
|---|---|
box.renew({ forSeconds }?, { signal?, pollMs? }) |
Reset the native lifetime window; omission uses the connection default. |
box.suspend({ signal?, pollMs? }) |
Preserve eligible inactive compute using the adapter’s native profile. |
box.resume({ signal?, pollMs? }) |
Reactivate the same resource; execution continuity can remain unknown. |
box.checkSnapshot(request?) |
Read-only evaluation of configured capture guarantees. |
box.snapshot(request?, { signal?, pollMs? }) |
Return { snapshot, capture, source }. |
beachside.snapshots.get(reference) |
Reopen a verified snapshot with a native inspection. |
beachside.snapshots.list({ limit, cursor? }) |
Read scoped snapshot inventory and coverage. |
snapshot.inspect({ signal?, pollMs? }) |
Observe preservation, identity and restore facts. |
snapshot.restore(request, { signal?, pollMs? }) |
Create independent compute under an explicit network policy. |
snapshot.delete({ signal?, pollMs? }) |
Delete an eligible retained artifact, separately from compute. |
beachside.volumes.create(input, { signal?, pollMs? }) |
Create retained storage and return its handle. |
beachside.volumes.get(reference) |
Reopen a verified volume with a native inspection. |
beachside.volumes.list({ limit, cursor? }) |
Read scoped volume inventory and coverage. |
volume.inspect({ signal?, pollMs? }) |
Observe volume readiness and storage facts. |
volume.at(path, { access?, subpath? }) |
Create a mount descriptor; default access is read-write. |
volume.delete({ signal?, pollMs? }) |
Explicitly delete eligible retained storage. |
Lifecycle, capture, restore, volume creation and artifact deletion also expose submit… forms returning operation handles. Managers additionally expose snapshots.delete(reference, options?) and volumes.delete(reference, options?). Snapshot and volume handles expose provider, id and reference; persist the complete reference. Restore accepts mounts descriptors, optional resources, and requireIndependentLifecycle (default true); unsupported combinations reject before allocation.
renew is not additive and may shorten a longer deadline. get and inspect never renew or resume. Suspension is distinct from snapshot capture. See lifecycle guarantees and snapshots and volumes for native restrictions and cleanup. destroy returns confirmed cleanup details and retains volumes; its storage policy is "require-durable" by default or explicitly "allow-unconfirmed".
Processes and terminals
Section titled “Processes and terminals”box.processes.start({ command, cwd?, env?, stdin?, output?, maxOutputBytes?, deadlineSeconds? }, { signal? }) returns a ProcessHandle. stdin is "closed" by default or "pipe". Select output: { mode: "stream", format?: "text" | "bytes" } for sustained output; omit maxOutputBytes in this mode. Without it, finite text output defaults to a 1 MiB cumulative limit. Explicit deadlineSeconds rejects because no portable process-runtime bound is guaranteed.
A process exposes output({ signal? }), wait({ signal? }), status({ signal? }), write(input, { signal? }), closeStdin({ signal? }), signal("SIGTERM" | "SIGKILL", { signal? }), terminate({ signal? }), reference(), disconnect() and detach(). wait returns { exitCode, outputComplete }; nonzero process exit is an ordinary result. Signal acknowledgement does not confirm exit. Output has one consumer and a bounded queue; disconnect preserves stdin for reopening, while detach may close it.
box.terminals.start({ command, columns, rows, cwd?, env? }, { signal? }) returns a terminal with combined byte output and resize({ columns, rows }, { signal? }). It has no closeStdin. box.processes.reopen(reference, { signal? }) and box.terminals.reopen(reference, { signal? }) attach to existing work with outputGap: true; they never launch a replacement or replay historical output. See streaming and interactive processes for limits, control and reference lifetime.
Execution input and output
Section titled “Execution input and output”exec and submitExec accept readonly string[] or an ExecInput object. An array is shorthand for { command: { kind: "argv", argv } }. Each element is literal, the array is copied before dispatch, and an empty array is invalid.
Use the object form for cwd, env, finite stdin (UTF-8 string or exact bytes, up to 1 MiB), deadlineSeconds, maxOutputBytes, or { command: { kind: "shell", script } }. The default deadline is 300 seconds and the default output limit is 1 MiB combined across stdout and stderr. deadlineSeconds is a provider execution/observation setting, not a total SDK waiting budget; use the call signal to bound local waiting. onOutput adds an awaitable live text callback while retaining bounded original output bytes from the same dispatch. Explicit deadlines reject on that callback path.
ExecOutput contains stdout and stderr byte arrays, exitCode, truncated, and stdoutText(maxBytes?) / stderrText(maxBytes?) helpers, which also accept { full: true }. No-argument text helpers decode all captured bytes; explicit numeric limits shorten display. Structured previews default to 16 KiB. stdoutPreview({ maxBytes? }) / stderrPreview({ maxBytes? }) return exported OutputPreview ({ text, shortened }); display shortening is independent of capture truncation. Exported outputText supports the same numeric and full overloads. See Files and output.
Operations and recovery
Section titled “Operations and recovery”An SDK operation exposes reference, durability: "process", observe(), and wait({ signal?, pollMs? }). Observation returns null while completion is unknown. Poll intervals are between 50 and 60,000 milliseconds; adapter delays can set a later earliest poll.
beachside.recover(reference) imports a saved reference in the same verified scope and returns an operation handle. It never resubmits. observe() and wait() read existing evidence; advanced continue({ signal? }) may dispatch a proven never-submitted next stage and must be serialized by the application. Aborting a wait stops local waiting, not provider compute.
BeachsideError exposes code and effect. OutcomeUnknownError and WaitAbortedError carry recovery references. NonzeroExitError carries a completed command’s output; NoExitCodeError means its exit code is unconfirmed. See Errors and recovery.
Advanced lifecycle
Section titled “Advanced lifecycle”beachside.operations.inventory({ limit, cursor? }) reads scoped { id, state } inventory where supported; sandboxes.list() adds references. operations.prepare(kind, input) returns a single-use prepared attempt; its submit(identity, { beforeSubmit }) hook lets an application commit a durable submission marker before native IO. Returning false or throwing in the hook prevents dispatch.
Persist accepted pending tokens with their versions. operations.observe(...) reads prior evidence without preparing or submitting again. These lower-level calls use adapter input shapes and require application-owned persistence. See Asynchronous adapter recovery.
Adapter authoring
Section titled “Adapter authoring”@beachside/adapter exports defineAdapter, AdapterError, operation result helpers, and scoped operation types. @beachside/adapter/testing exports adapterSuite. An adapter with no host policy does not expose withPolicy; policy-bearing definitions validate and clone host policy synchronously. Start with Write an adapter.
Creation input may be omitted when the adapter supplies a default environment. E2B uses its configured templateId (default base); Daytona accepts an environment setup option. Explicit per-call environments win. checkCreate() and submitCreate() resolve the same defaults without relaxing native create inputs. Missing defaults reject with INVALID_ARGUMENT before creation.