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

@nimbus-sh/sdk

v0.8.1

Published

Nimbus SDK — Worker embedder, programmatic sandboxes, token mint, and client helpers.

Downloads

1,045

Readme

@nimbus-sh/sdk

Run code in a Nimbus sandbox from your own backend, and deploy the Worker that hosts it.

A sandbox has a filesystem, a shell, real processes, and ports you can reach from a browser. This package carries the Worker embedder, the programmatic sandbox handles, token mint and verify, typed errors, and session URL helpers.

Install

npm install @nimbus-sh/sdk @nimbus-sh/worker @nimbus-sh/config

Import the deploy-time API from @nimbus-sh/sdk/worker. Install @nimbus-sh/worker as well, because it carries the runtime assets and the Durable Object implementation that entrypoint serves.

Create a deployable Nimbus Worker:

npx create-nimbus-app my-nimbus-worker
cd my-nimbus-worker
npm install
CLOUDFLARE_ACCOUNT_ID=<account-id> npx @nimbus-sh/cli setup cloudflare --name my-nimbus-worker
npx wrangler secret put JWT_SECRET
npx wrangler deploy

src/index.ts:

import {
  NimbusSession,
  SupervisorRPC,
  NimbusAssetsRPC,
  NimbusLoaderRPC,
  NimbusLoadedWorker,
  NimbusLoadedEntrypoint,
  NimbusDurableObjectNamespace,
  NimbusDOStub,
  CirrusHmrRPC,
  createNimbusHandler,
} from '@nimbus-sh/sdk/worker';
import { defineNimbusConfig } from '@nimbus-sh/config';

export {
  NimbusSession,
  SupervisorRPC,
  NimbusAssetsRPC,
  NimbusLoaderRPC,
  NimbusLoadedWorker,
  NimbusLoadedEntrypoint,
  NimbusDurableObjectNamespace,
  NimbusDOStub,
  CirrusHmrRPC,
};

const nimbusConfig = defineNimbusConfig({
  sandboxes: {
    default: {
      root: '/home/user',
      runtimes: { preinstall: ['python'], onDemand: true },
      tools: { namespace: 'sandbox', kind: 'sandbox' },
    },
  },
});

export default createNimbusHandler({
  sdk: {
    remote: true,
    config: nimbusConfig,
  },
});

The bundled session UI carries an agent surface in the editor workspace. Configure it with the Worker vars NIMBUS_CF_OAUTH_CLIENT_ID, NIMBUS_CF_OAUTH_SCOPES, NIMBUS_AGENT_MODEL, and NIMBUS_AGENT_GATEWAY_ID. For Cloudflare OAuth or owner-token Workers AI access, add the secret NIMBUS_AGENT_COOKIE_SECRET or NIMBUS_CLOUDFLARE_API_TOKEN.

User OAuth uses Authorization Code with PKCE and encrypted browser cookies. Nimbus keeps no user OAuth token in Durable Object storage.

Sandboxes from your own code

The same sandbox handle API works in two modes:

  • Nimbus.fromEnv(env, config) for a Worker or Durable Object with the NIMBUS_SESSION binding.
  • Nimbus.connect({ endpoint, token, config }) for any backend that can reach a deployed Nimbus Worker.
import { Nimbus } from '@nimbus-sh/sdk';
import { defineNimbusConfig } from '@nimbus-sh/config';

const nimbusConfig = defineNimbusConfig({
  sandboxes: {
    default: {
      root: '/home/user',
      tools: { namespace: 'sandbox', kind: 'sandbox' },
      runtimes: {
        preinstall: ['python', 'clang'],
        onDemand: true,
        allow: ['node', 'bun', 'npm', 'git', 'python', 'ruby', 'clang', 'shell'],
      },
    },
  },
});

export default {
  async fetch(_request: Request, env: Env) {
    const box = Nimbus.fromEnv(env, nimbusConfig).sandbox('job-123', {
      tenant: 'acme',
      subject: 'agent',
    });

    await box.files.write('/home/user/example-app/main.py', 'print(2 + 2)\n');
    await box.runtimes.ensure('python');
    const result = await box.exec('python /home/user/example-app/main.py');

    return Response.json(result);
  },
};

Remote use:

import { Nimbus, issueNimbusToken } from '@nimbus-sh/sdk';

const token = await issueNimbusToken(env, {
  tn: 'acme',
  sub: 'agent',
  scopes: ['sandbox:use'],
  sid: 'job-123',
});

const box = Nimbus.connect({
  endpoint: 'https://my-nimbus.workers.dev',
  token,
  config: nimbusConfig,
}).sandbox('job-123');

const result = await box.exec('python -c "print(2 + 2)"');

Enable the remote API in the Nimbus Worker:

export default createNimbusHandler({
  sdk: {
    remote: true,
    config: nimbusConfig,
  },
});

Nimbus.connect() calls the versioned /api/nimbus/v1 API internally. The Worker verifies the JWT, enforces sid pins and sandbox:use scope, and applies the configured runtime policy. It then delegates to the same NimbusSession RPC methods Nimbus.fromEnv() uses.

Sandbox destruction is a separate lifecycle operation. Tokens that call box.destroy() must include session:destroy or session:admin in addition to the normal sandbox scope.

Flue connector

Use @nimbus-sh/sdk/flue when an agent runtime expects Flue's sandbox provider contract:

import { Nimbus } from '@nimbus-sh/sdk/sandbox';
import { nimbusFlue } from '@nimbus-sh/sdk/flue';

const box = Nimbus.fromEnv(env, nimbusConfig).sandbox('job-123');
await box.ready();

const factory = nimbusFlue(box);
const sessionEnv = await factory.createSessionEnv({
  id: 'job-123',
  cwd: '/home/user',
});

await sessionEnv.writeFile('/home/user/main.py', 'print(2 + 2)\n');
const result = await sessionEnv.exec('python /home/user/main.py');

Sandbox handle API

const nimbus = Nimbus.fromEnv(env, config, { binding: 'NIMBUS_SESSION' });
// or:
const nimbus = Nimbus.connect({ endpoint, token, config });

const box = nimbus.sandbox('session-or-job-id', {
  profile: 'default',
  tenant: 'acme',
  subject: 'agent-7',
  root: '/home/user',
});

await box.ready();
await box.exec('node -e "console.log(2 + 2)"');
const proc = await box.startProcess('node --watch /home/user/example-app/server.js');
// returns immediately with proc.pid; poll box.processes.logs(proc.pid) for
// output and the exit record, or box.processes.kill(proc.pid) to stop it
await box.runCode('print(2 + 2)', { language: 'python', install: 'ifMissing' });

await box.files.write('/home/user/example-app/a.txt', 'hello');
await box.files.read('/home/user/example-app/a.txt');
await box.files.list('/home/user/example-app');
await box.files.stat('/home/user/example-app/a.txt');
await box.files.lstat('/home/user/example-app/link');
await box.files.rename('/home/user/example-app/a.txt', '/home/user/example-app/b.txt');
await box.files.chmod('/home/user/example-app/b.txt', 0o755);
await box.files.readRange('/home/user/example-app/b.txt', 0, 64);
await box.files.delete('/home/user/example-app/b.txt');

await box.runtimes.available();
await box.runtimes.installed();
await box.runtimes.install('python');
await box.runtimes.ensure(['python', 'clang']);

await box.processes.list();
await box.processes.logs(7);
await box.processes.kill(7);

box.ports.url(3000);
// https://my-nimbus.workers.dev/s/<session-or-job-id>/port/3000/   (owner only)
await box.ports.list();

// Share a running server with anyone who has the link.
const web = await box.apps.expose(3000, { visibility: 'public', name: 'web' });
// web.url, web.capability, web.owner, web.port, web.pid
await box.apps.list();   // adds status and restart policy per app
await box.apps.rotateLink('web');                    // new link, old one revoked
await box.apps.expose('web', { visibility: 'scoped' }); // back to owner only
await box.apps.remove('web');                        // stop it, release the port and its storage

Every server is durable: its journal row is restarted after a platform reset, on the next request to its URL. Identity is derived from the command and its working directory, so re-running the same command in the same directory is the same app, and a shared link is bound to that program rather than to the port. startProcess(cmd, { restart: 'on-failure' }) also restarts it after a crash; the default restarts only after a reset. An app target can be a port, a pid, a name, or an owner.

On deployments with a preview host suffix configured (the hosted product sets NIMBUS_PREVIEW_HOST_SUFFIX=nimbus-os.dev), URLs take the hostname form: https://<port>--<session-id>.<suffix>/ for the private preview, https://<name>--<session-id>.<suffix>/ for a named app, and https://<capability>--<name>--<session-id>.<suffix>/ for a public link. Public links need the NIMBUS_PUBLIC_DIRECTORY Durable Object binding, which @nimbus-sh/config emits with nimbusPublicDirectory: true. The /s/<id>/port/<n>/ path route works on every deployment.

Runtime policy comes from the sandbox profile:

  • runtimes.preinstall is applied by box.ready().
  • runtimes.allow gates SDK runtime operations and runCode() language use.
  • runtimes.onDemand: false blocks installing runtimes that are not listed in preinstall.

Agent tool provider

box.tools({ namespace: 'sandbox', kind: 'sandbox' }) returns a provider-like object, in the shape Proteus's tool provider takes. It carries tools.exec.execute, runCode, readFile, writeFile, listFiles/readdir, deleteFile, exists, startProcess, killProcess, logs, exposePort, unexposePort, listPorts, exposeApp, listApps, installRuntime, and listRuntimes.

provider.capabilities reports what Nimbus can do. Nimbus claims shell, JavaScript/TypeScript, npm, git, owned filesystem, outbound fetch, inbound HTTP-like port routing, process spawn/long-running processes, Python/Ruby when allowed, and clang-backed WASI/WebAssembly execution. It does not claim Docker, apt, GPU, custom Linux images, native Linux ELF execution, or raw TCP listeners.

Mint a session token

import { issueNimbusToken } from '@nimbus-sh/sdk/token';

const token = await issueNimbusToken(
  { JWT_SECRET: process.env.JWT_SECRET! },
  { tn: 'acme', sub: 'alice' },
  { ttlMs: 60 * 60 * 1000 },  // 1h
);
// → 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6Im5pbWJ1cyIsInRu...'

Verify a token

import { verifyNimbusToken } from '@nimbus-sh/sdk/token';

try {
  const { claims, doInstanceName } = await verifyNimbusToken(env, token);
  // claims.tn, claims.sub, claims.scopes, claims.sid
  // doInstanceName = `${tn}:${sub || '_'}` — feed to idFromName
} catch (e) {
  // every error extends NimbusAuthError with stable .code + .httpStatus
}

Typed errors

import {
  NimbusAuthError,
  NimbusTokenExpiredError,
  NimbusScopeError,
} from '@nimbus-sh/sdk/errors';

try { await verifyNimbusToken(env, token); }
catch (e) {
  if (e instanceof NimbusTokenExpiredError) return Response.json({ refresh: true }, { status: 401 });
  if (e instanceof NimbusScopeError)        return Response.json({ scope: e.requiredScope }, { status: 403 });
  if (e instanceof NimbusAuthError)         return Response.json({ error: e.message, code: e.code }, { status: e.httpStatus });
  throw e;
}

| Error class | code | httpStatus | |---|---|---| | NimbusAuthConfigError | E_AUTH_CONFIG_MISSING | 500 | | NimbusTokenMalformedError | E_TOKEN_MALFORMED | 401 | | NimbusTokenSignatureError | E_TOKEN_SIGNATURE | 401 | | NimbusTokenClaimsError | E_TOKEN_CLAIMS | 401 | | NimbusTokenExpiredError | E_TOKEN_EXPIRED | 401 | | NimbusTokenTtlError | E_TOKEN_TTL_TOO_LARGE | 400 | | NimbusScopeError | E_SCOPE_MISSING | 403 | | NimbusSessionPinError | E_SESSION_PIN_MISMATCH | 403 |

Session URL helpers

import { sessionAttachUrl, mintAndAttach } from '@nimbus-sh/sdk';

const url = sessionAttachUrl(
  'https://my-nimbus.workers.dev',
  'pretty-otter-1234',
  token,
);
// → "https://my-nimbus.workers.dev/s/pretty-otter-1234/?nimbus_token=…"

// Or combined in one call:
const { token, url } = await mintAndAttach(
  env,
  { tn: 'acme', sub: 'alice' },
  { endpoint: 'https://my-nimbus.workers.dev', sessionId: 'pretty-otter-1234' },
);

Subpath exports

| Subpath | What | |---|---| | @nimbus-sh/sdk | Everything re-exported from a single entry. | | @nimbus-sh/sdk/token | issueNimbusToken, verifyNimbusToken, types. | | @nimbus-sh/sdk/errors | NimbusAuthError class hierarchy. | | @nimbus-sh/sdk/session | sessionAttachUrl, mintAndAttach. | | @nimbus-sh/sdk/sandbox | Nimbus, NimbusSandbox, programmatic exec/files/runtimes/processes/ports/tools. | | @nimbus-sh/sdk/worker | Worker embedder entrypoint: NimbusSession, RPC classes, createNimbusHandler, auth helpers. |

Token wire format

JWT (HS256) with these claims:

{
  scope: 'nimbus',           // always — discriminator vs other JWTs
  tn:    'acme',             // tenant (required)
  sub?:  'alice',            // user within tenant (optional)
  scopes?: ['session:create','session:attach'],  // capability list
  sid?:  'pretty-otter-1234', // pin to a specific session (optional)
  iat:   1731612345,         // issued-at (UNIX seconds)
  exp:   1731615945,         // expires-at (UNIX seconds)
}

Both tn and sub must match [A-Za-z0-9._-]{1,128}. The pattern is exported as ID_COMPONENT_RE if you need to validate user input.

The scope discriminator rejects a token minted for another product (e.g. Mossaic VFS, scope: "vfs") even when the same secret signed it.

Secret rotation

Set JWT_SECRET_PREVIOUS during a rotation window. Both old and new secrets verify; new tokens are always signed with the primary.

# Phase 1: New secret in place, old secret as fallback.
wrangler secret put JWT_SECRET           # the new one
wrangler secret put JWT_SECRET_PREVIOUS  # the old one

# Phase 2 (after the longest token TTL has elapsed):
wrangler secret delete JWT_SECRET_PREVIOUS

MIT.