npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@docker/sandboxes

v0.52.0

Published

TypeScript REST SDK for Docker Cloud Sandboxes, with typed HTTP and WebSocket streams.

Readme

Docker Sandboxes for TypeScript

Run code in isolated Docker Cloud Sandboxes from Node.js 20 or later. Your Docker account needs Cloud Sandboxes access.

npm install @docker/sandboxes

Sign in

The SDK gives your application a URL and code to show the user. It waits for sign-in, then keeps the session in memory and refreshes it when needed.

import { oauth, Sandboxes, SandboxesError } from '@docker/sandboxes';

const auth = oauth({
  onVerification({ verificationUriComplete, verificationUri, userCode }) {
    console.log(`Open ${verificationUriComplete ?? verificationUri} and enter ${userCode}`);
  },
});
await auth.getAccessToken(); // Complete attended sign-in before the first API deadline starts.
const client = new Sandboxes({ auth });

Attended sign-in follows the device code's lifetime, not the 30-second request timeout. Individual authentication requests remain bounded. Pass auth.getAccessToken({ signal }) to stop waiting; canceling one caller does not cancel a shared sign-in. API requests still have their own deadlines, so complete attended sign-in before making them.

OAuth credentials stay in memory by default, so a new process signs in again. For explicit local persistence, pass an intentional ignored path such as store: fileOAuthCredentialStore({path: '.docker-sandboxes/oauth.json'}); it stores plaintext credentials with restrictive POSIX permissions. Keep that path out of source control. Use a keychain, encrypted database, or secret manager for production workloads.

Custom credential stores should honor the supplied abort signal. The SDK waits for an in-flight save to settle before publishing the new credentials or starting another save.

For automation with a Docker personal access token, use the PAT authenticator. It exchanges the PAT over HTTPS for a short-lived access token, keeps it in memory, and refreshes it automatically. The API validates the token; the SDK reads its expiry only to schedule renewal.

import { pat } from '@docker/sandboxes';

declare const username: string;
declare const personalAccessToken: string;

const patAuth = pat({ username, personalAccessToken });
const automatedClient = new Sandboxes({ auth: patAuth });

Custom authentication transport

If sign-in must use a proxy or custom CA configuration, pass your Fetch-compatible function to pat() or oauth(). A client's fetch option controls API requests only; it does not reconfigure a shared authenticator. To use the same transport for both:

declare const customFetch: (request: Request) => Promise<Response>;
const proxyAuth = pat({
  username,
  personalAccessToken,
  fetch: customFetch,
  transportRetries: 'none',
});
const proxyClient = new Sandboxes({
  auth: proxyAuth,
  fetch: customFetch,
  transportRetries: 'none',
});

Each authenticator retains its custom function for initial sign-in and refresh. oauth() uses it for device-code requests and token polling too. The function must support concurrent requests, honor the request's abort signal and redirect policy, and make one attempt per call. Declare transportRetries: 'none' on each owner using custom fetch. Authentication keeps its existing deadlines and does not retry or fall back to global fetch when the custom transport fails. Without custom fetch, authentication uses the platform's global fetch. Closing a client does not close its authenticator or either borrowed transport.

By default, the same access token, token provider, or sign-in session authenticates Sandbox requests at https://connect.docker.com/sandboxes and personal Governance requests at https://hub.docker.com/rpc. Sign-in does not read or change Docker CLI credentials.

Inherited environment variables can change where the SDK sends credentials. Unless you intentionally use internal integration overrides, unset SANDBOXES_API_URL, DOCKER_OAUTH_ISSUER, DOCKER_TOKEN_EXCHANGE_URL, and DOCKER_GOVERNANCE_URL before creating clients or authenticators.

These controls are retained for internal testing, not supported end-user configuration.

| Variable | Selects | Default | | --- | --- | --- | | SANDBOXES_API_URL | Management API base, without /v1 | https://connect.docker.com/sandboxes | | DOCKER_OAUTH_ISSUER | OAuth issuer and its device and token endpoints | https://login.docker.com/ | | DOCKER_TOKEN_EXCHANGE_URL | PAT exchange endpoint | https://hub.docker.com/v2/auth/token | | DOCKER_GOVERNANCE_URL | Personal Governance RPC base | https://hub.docker.com/rpc |

Each omitted variable keeps its own production default. Governance is not inferred from the management URL. Overrides must be nonempty HTTPS URLs without user information, a query or a fragment. A management base ending in a version such as /v1 is rejected rather than rewritten.

Set overrides before constructing pat(), oauth() or Sandboxes. Each retains its initial configuration; later environment changes do not redirect requests or token refreshes. A client using a Docker authenticator must match that authenticator's management and Governance bases. An explicit baseUrl takes precedence over SANDBOXES_API_URL, but does not bypass override validation or this authority check. Use a separate OAuth credential store for each issuer; stored records are not bound to an issuer.

The recommended credential configuration is auth: use oauth() for attended sign-in, pat() for automation, bearer() for an existing access token, or a custom Authenticator when the application owns token acquisition. SandboxesOptions also supports accessToken for a fixed bearer token and tokenProvider for a token read on each request.

Create a sandbox and run code

Creation returns when the server accepts the request. Wait separately when your next step needs a running sandbox. Later examples reuse client and sandbox.

const accepted = await client.create({
  imageRef: 'python:3.12',
  displayName: 'python-worker',
  resources: { cpus: 2, memoryMib: '4096' },
});
const sandbox = await accepted.waitUntilRunning();

const result = await sandbox.processes.run({
  args: ['python', '-c', 'print("Hello from a sandbox")'],
});
process.stdout.write(result.stdout);
process.stderr.write(result.stderr);
console.log(result.exitCode);

await sandbox.files.write('/tmp/message.txt', 'Hello\n');
const message = await sandbox.files.read('/tmp/message.txt', { encoding: 'utf8' });
console.log(message);

Waiters default to five minutes. Pass { timeoutMs: 60_000 } to change the deadline. A timeout leaves the sandbox in place.

Use a returned name to reconnect. List one page or iterate over all pages:

const existing = await client.get(sandbox.name);
for await (const item of client.allSandboxes({ query: { pageSize: 25 } })) {
  console.log(item.name, item.core.status);
}

Pagination has no default total timeout; each page request retains its 30-second timeout. An explicit timeoutMs bounds the whole traversal from the start of iteration, including time spent processing items. Caller cancellation, client closure and an enclosing workflow deadline stop traversal before another request or buffered item. collect() and listAll*() retain all results in memory.

Launch a bundled kit

A kit supplies a sandbox image and its agent configuration. The SDK includes a versioned catalog; list() reads it locally. The server must support kit launches, and the selected agent may need its own credentials.

console.log(client.kits.list());

const agentSandbox = await client.kits.launchAndWait('claude', {
  displayName: 'coding-agent',
  resources: 'small',
});

launchAndWait() creates the sandbox and waits for its running state under one deadline. Use launch() when you want the accepted handle immediately and will wait separately. Neither starts an agent task or checks files created by kit setup.

Kit launches default to Small (2 vCPUs, 4 GiB). Choose micro, small, medium, large, or xl through resources, or supply an explicit CPU/memory object. The same named sizes work with client.create().

Manage personal network policies

Governance uses the same client credentials. These methods manage policies owned by the signed-in user; they do not select an organization or another user. This example creates an empty allowlist. Add the required network rules before attaching it to a sandbox.

const policy = await client.governance.policies.create({
  name: 'project-network',
  policyType: 'POLICY_TYPE_ALLOWLIST',
  allowlist: { rules: [] },
});

const policies = await client.governance.policies.list();
const currentPolicy = await client.governance.policies.get(policy.id);
const updatedPolicy = await client.governance.policies.update(
  policy.id,
  { description: 'Network access for this project' },
  { updateMask: 'description' },
);
await client.governance.policies.delete(policy.id);

To use an existing policy when creating a sandbox, pass its ID in network.policyIds.

Handle errors and retries

HTTP failures throw SandboxesError. Wait failures retain the last observed resource in WaitError.resource.

try {
  await client.get(sandbox.name, { timeoutMs: 30_000, maxRetries: 0 });
} catch (error) {
  if (!(error instanceof SandboxesError)) throw error;
  console.error(error.message, error.requestId);
}

The default is at most two additional attempts for transient failures on safe unary requests. Supported mutations get one idempotency key per call, reused with the same body on retries. Supply { idempotencyKey: 'your-request-id' } to reuse a key across application calls. maxRetries: 0 disables retries for a call or client. processes.run(), raw streams and Governance mutations are never replayed.

processes.run() has no default total deadline; process creation and each output read retain their 30-second request timeout. Pass { timeoutMs: 300_000 } as its second argument to bound the whole run, including creation. Caller cancellation, client closure and an enclosing workflow deadline also stop local waiting. run() does not kill the process; withSandbox() retains its separate sandbox cleanup policy. After creation, a failure retains the process identity in ResourceWaitError.resource.

process.connect() automatically reconnects while you consume output after a temporary disconnect. It resumes after the last chunk delivered to your iterator, without restarting the process or replaying input. Each recovery episode has a 30-second budget. Set { reconnect: false } or { reconnectTimeoutMs: 60_000 } in the second argument to control recovery. Authentication failures, service refusals and invalid output cursors stop the connection.

Connection setup has a 30-second timeout, but an attached process session has no default lifetime limit. An explicit timeoutMs, abort signal or enclosing workflow deadline still applies to the whole session. A failed input write may have reached the process; inspect its state before sending the input again.

Custom fetch implementations and borrowed clients require transportRetries: 'none': disable any retries inside that transport first. Each call retains its selected credential; the SDK never refreshes it to rescue an in-flight call. After a token rejection, a later independent call can renew the managed login or PAT. Retries and waiters use valid server delay guidance exactly when the deadline allows it.

Clean up

Fetch the latest resource before deleting it. HTTP 204 confirms deletion; otherwise, wait on the returned resource. An unexplained 404 does not confirm deletion.

const latest = await sandbox.refresh();
const deleting = await latest.delete();
if (deleting) await deleting.waitUntilDeleted();

const latestAgent = await agentSandbox.refresh();
const deletingAgent = await latestAgent.delete();
if (deletingAgent) await deletingAgent.waitUntilDeleted();

await automatedClient.close();
await client.close();

close() cancels client-owned work and clears its in-memory credentials. It does not delete sandboxes or revoke remote credentials.

Advanced HTTP and browser use

client.api exposes explicit request envelopes. For a conditional read, pass If-None-Match; a 304 response becomes undefined. client.api.raw retains HTTP status and response headers.

Browser bundles support sandbox management, process WebSockets and raw file reads where the service permits cross-origin requests. Streaming uploads and multipart transfers require Node.js. Closing a process connection detaches it without killing its process.

See the API specification for request fields and supported operations.