Sandbox Kit

Manage a sandbox

Open a sandbox you already have, stop or terminate it, and close your local connection.

Create returns a handle. Later calls use that handle, or a handle you open from an id. Pick the row for the call you need.

You want toDaytonaModal
Open an existing sandboxget looks the id upattach takes the id and does not look it up
Start, stop, pause, archiveSupportedRejected before the call
Remove the sandboxdeleteUse terminate, or a lifetime set at create time
Drop your local connectioncloseclose

supports reports whether this provider implements the call. Import LifecycleCapability from @sandbox-kit/sandbox. In Go the constants are sandbox.LifecycleCapabilityStop and the rest of that set. A missing operation fails before the provider is contacted. Daytona can still reject a supported call for one sandbox, and that error comes back to you.

The snippets below assume you already have a client. Build one on the provider page.

Open a sandbox

get loads a sandbox that already exists. refresh loads it again.

const sandbox = await client.get(process.env.DAYTONA_SANDBOX_ID!);
const info = await sandbox.refresh();
console.log(sandbox.id, info.state);

Stop, start, pause, and archive

These four calls are Daytona calls. Modal rejects them. stop asks Daytona to stop the sandbox. The sandbox stays available to start again. start brings a stopped or archived sandbox back. pause keeps memory and stops the sandbox. archive moves a retained sandbox into archive storage.

const sandbox = await client.get(process.env.DAYTONA_SANDBOX_ID!);

if (!sandbox.supports(LifecycleCapability.Stop)) {
  throw new Error(`sandbox ${sandbox.id} cannot be stopped`);
}

const info = await sandbox.stop({ timeout: 60_000 });
console.log(sandbox.id, info.state);

timeout is milliseconds. Pass force: true to request an immediate stop. Check supports the same way before start, pause, or archive.

Daytona decides whether that sandbox can pause, archive, or stop. An ephemeral sandbox can still be removed by the lifetime you set at create time.

Delete

delete removes a Daytona sandbox. Modal rejects it.

const sandbox = await client.get(process.env.DAYTONA_SANDBOX_ID!);
await sandbox.delete({ timeout: 60_000 });

Terminate

terminate finishes a Modal sandbox. It is not a stop, and it is not a delete. Daytona rejects it. A maximum lifetime or an idle timeout set at create time also ends the sandbox.

const sandbox = await client.attach(process.env.MODAL_SANDBOX_ID!);

if (!sandbox.supports(LifecycleCapability.Terminate)) {
  throw new Error(`sandbox ${sandbox.id} cannot be terminated`);
}

const exit = await sandbox.terminate({ wait: true, timeout: 60_000 });
console.log(sandbox.id, exit.exitCode);

wait: true polls until the sandbox exits or the timeout fires. The timeout is how long the client waits. Modal does not receive that duration as its own terminate timeout.

Close the client

await client.close();

Prefer await using when the client's lifetime matches the block. Disposing the client closes the connection. Disposing the handle leaves the sandbox as it is.

Read the state

info.state is the portable state. The provider's own string is kept beside it. After a lifecycle call, read the handle again before you treat the sandbox as stopped or gone. A failed call can mean the provider already changed it. See Read an error.

On this page