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

@local-sandbox/lsb-nodejs

v0.7.0

Published

Native Node.js bindings for lsb microVM sandboxes

Readme

@local-sandbox/lsb-nodejs

Native Node.js bindings for lsb, built with napi-rs.

This package is the canonical JavaScript and TypeScript entrypoint for lsb. It wraps the Rust lsb-sdk directly and exposes a Node-facing Sandbox API.

Install

npm install @local-sandbox/lsb-nodejs

The published npm package is split into a root package plus a platform package. On supported hosts, npm resolves and installs either @local-sandbox/lsb-nodejs-darwin-arm64 or @local-sandbox/lsb-nodejs-darwin-x64 on macOS, or @local-sandbox/lsb-nodejs-win32-x64-msvc on Windows x64, automatically.

For local development, use Corepack to run the Yarn version pinned in package.json:

corepack yarn install

Requirements

  • Node.js 18+
  • macOS 14+ on Apple Silicon or Intel x86_64, or Windows 11 on x86_64
  • Runtime assets initialized with initSandbox() or lsb init. On Windows, that initialization also installs LocalSandbox-managed QEMU host tools. Sandbox.start() still expects the lsb runtime data directory to already contain Image, rootfs.ext4, and initramfs.cpio.gz; it does not download assets or host tools implicitly.
  • On macOS, the node executable loading this SDK must be code signed with the com.apple.security.virtualization entitlement. For a project-local workflow, sign a copied Node binary with ../../lsb.entitlements, or use test:signed-node as a reference.
  • On Windows, npm packages do not contain QEMU. Enable Windows Hypervisor Platform, then initialize host tools through initSandbox() or lsb init. The Windows backend requires WHPX and does not fall back to TCG for production Node users. LSB_QEMU and LSB_QEMU_IMG remain supported override/debug paths.
  • Windows support covers sandbox start/stop, non-interactive exec() / execShell(), streaming spawn(), guest file APIs, watch(), overlay mounts, SMB/CIFS direct mounts, loopback port forwarding, policy-mediated proxy networking, and checkpoint restore/save. Interactive PTY shells remain macOS-only.

Usage

Experimental Windows service client

SeaWork integrations on Windows use the machine service instead of the direct Sandbox API. The remote API contains no caller-selected runtime directory, QEMU path, instance ID, checkpoint, or identity fields:

import { connectSeaWorkService } from '@local-sandbox/lsb-nodejs'

const service = await connectSeaWorkService({ connectTimeoutMs: 10_000 })
const info = await service.getServiceInfo()
const health = await service.healthCheck()
if (!health.ready) throw new Error(health.stableCode)

const sandbox = await service.startSandbox({
  cpus: 2,
  memoryMb: 2048,
  diskSizeMb: 4096,
  mounts: [{
    type: 'direct',
    hostPath: 'C:\\work\\project',
    guestPath: '/workspace',
    flags: 0,
    pruneSubtrees: ['node_modules', '.seawork'],
  }],
  network: {
    allow: ['api.example.com'],
    secrets: { API_TOKEN: { value: 'secret', hosts: ['api.example.com'] } },
  },
})
const result = await sandbox.exec(['sh', '-lc', 'echo service-ready'])
await sandbox.mkdir('/workspace/out', { recursive: true })
await sandbox.stop()
await service.close()

This surface includes lifecycle, bounded unary/cancellable exec, credited process streams, guest filesystem metadata and byte transfer, direct SMB mounts, and managed watches. Host ports remain intentionally unavailable in the initial service scope. SeaWorkService.connect(), health(), and start() remain compatibility aliases. The existing Sandbox API remains the direct SDK path.

SeaWork direct mounts prune node_modules and .seawork directory basenames from the recursive SMB ACL startup traversal by default, at any depth and case-insensitively. Set pruneSubtrees to a custom list to replace those defaults, or to [] to disable pruning. Pruning only affects startup inspection and ACL work; it does not hide those paths from the SMB share.

The service start type preserves exact ports and network.exposeHost requests for the NET-02 contract, but does not claim those capabilities yet. Host ports return PORT_ISOLATION_UNAVAILABLE, and expose-host relay requests fail feature negotiation until the owner-token relay and Windows WFP isolation gates are implemented and enabled.

Start a sandbox and run commands

import { Sandbox, initSandbox } from '@local-sandbox/lsb-nodejs'

const init = await initSandbox()

const sandbox = await Sandbox.start({
  dataDir: init.dataDir,
  cpus: 2,
  memoryMb: 2048,
  mounts: [{ type: 'overlay', hostPath: './src', guestPath: '/workspace' }],
  network: { allow: ['registry.npmjs.org'] },
})

const result = await sandbox.exec('echo hello from lsb')
console.log(result.stdout)

await sandbox.writeFile('/tmp/demo.txt', 'hello')
const content = await sandbox.readFile('/tmp/demo.txt')
console.log(content.toString())

await sandbox.stop()

Initialize runtime assets

import { initSandbox } from '@local-sandbox/lsb-nodejs'

const init = await initSandbox()
console.log(init.dataDir, init.version, init.downloaded)

initSandbox() defaults to this package version and pins that base rootfs. Sandbox.start() defaults to the initialized VERSION in the runtime data directory. You only need to pass a version when preparing or booting from an older pinned base.

To show first-start download progress, pass onProgress while keeping the returned promise as the completion and error channel:

const init = await initSandbox({
  onProgress(progress) {
    if (progress.downloadedBytes !== undefined && progress.totalBytes !== undefined) {
      const percent = Math.min(
        100,
        Math.floor((progress.downloadedBytes / progress.totalBytes) * 100),
      )
      process.stdout.write(`\r${progress.phase}: ${percent}%`)
    } else {
      console.log(progress.phase)
    }
  },
})

console.log(`\nready: ${init.dataDir}`)

Download counters represent compressed response bytes consumed. totalBytes is omitted when the server does not provide a valid positive Content-Length; downloadedBytes still advances in that case. Runtime download and extraction are pipelined, so they appear as the single downloading-and-extracting-runtime-assets phase.

On a first Windows startup, managed host tools and runtime assets are separate downloads. The byte counter resets to zero when the runtime-assets download begins. Ready assets do not emit download phases, while force: true downloads them again. Notifications are queued non-blockingly on the JavaScript thread, so a slow handler does not control installation speed and the promise may settle before the final queued callback runs. The callback return value is ignored and the callback must not throw.

On Windows, pass fix: true from an elevated process to apply every available automatic host configuration repair. The result reports each attempted fix and whether it changed the host.

const init = await initSandbox({ fix: true })
console.log(init.fixes) // [{ name: 'windows-smb-policy', changed: true }]
await initSandbox({ version: '0.3.8' })

const sandbox = await Sandbox.start({ baseVersion: '0.3.8' })

Pass argv directly or run through a shell

import { Sandbox } from '@local-sandbox/lsb-nodejs'

const sandbox = await Sandbox.start()

const argvResult = await sandbox.exec(['sh', '-lc', 'printf "%s" "$HOME"'])
console.log(argvResult.stdout)

const shellResult = await sandbox.execShell('uname -a')
console.log(shellResult.stdout)

await sandbox.stop()

Inspect the guest filesystem

import { Sandbox } from '@local-sandbox/lsb-nodejs'

const sandbox = await Sandbox.start()

await sandbox.writeFile('/tmp/demo.txt', 'hello from lsb')

const entries = await sandbox.readDir('/tmp')
const stat = await sandbox.stat('/tmp/demo.txt')
const exists = await sandbox.exists('/tmp/demo.txt')

console.log(entries.map((entry) => `${entry.type}: ${entry.name}`))
console.log({ size: stat.size, mode: stat.mode, exists })

await sandbox.stop()

Save and resume from a checkpoint

import { Sandbox } from '@local-sandbox/lsb-nodejs'

const base = await Sandbox.start()
await base.exec('mkdir -p /workspace && echo ready > /workspace/state.txt')
await base.checkpoint('my-env')

const resumed = await Sandbox.start({ from: 'my-env' })
const state = await resumed.readFile('/workspace/state.txt')
console.log(state.toString())

await resumed.stop()

Configure mounts, ports, secrets, and network policy

import { Sandbox } from '@local-sandbox/lsb-nodejs'

const sandbox = await Sandbox.start({
  cpus: 4,
  memoryMb: 4096,
  diskSizeMb: 8192,
  ports: [{ host: 8080, guest: 80 }],
  mounts: [{ type: 'overlay', hostPath: './src', guestPath: '/workspace' }],
  network: {
    allow: ['api.openai.com', 'registry.npmjs.org'],
    exposeHost: [{ host: 3000, guest: 3000 }],
    secrets: {
      API_KEY: { value: 'sk-test', hosts: ['api.openai.com'] },
    },
    httpsInterception: {
      enabled: true,
      requestHeaders: [
        {
          name: 'User-Agent',
          value: 'my-sandbox-agent/1.0',
          hosts: { allow: ['api.openai.com'], deny: ['private.api.openai.com'] },
        },
      ],
    },
  },
})

console.log(sandbox.instanceDir)

await sandbox.stop()

httpsInterception is off by default. Rules without hosts are global; an allow match is required when allow is present, and deny always wins. The scope follows TLS SNI rather than the HTTP Host header. Interception supports HTTP/1.1 over TCP port 443 with visible SNI and installs an ephemeral proxy CA in the guest. Certificate pinning, mutual TLS, private trust stores, HTTP/2, HTTP/3, QUIC, and TLS without usable SNI are unsupported. Connections without an applicable header or secret remain blind tunnels.

Each rule has set semantics: on every request, existing instances of the named header are removed case-insensitively and one configured value is inserted. Header values may be sensitive, so prefer an allow scope instead of a global rule for credentials or private identifiers. Explicitly empty allow or deny arrays, duplicate names (ignoring case), and an enabled configuration with no rules are rejected before the sandbox starts.

Routing, framing, proxy, and hop-by-hop headers such as Host, Content-Length, Transfer-Encoding, Connection, Upgrade, and Expect cannot be configured. A configuration may contain at most 64 rules, with a 128-byte name, an 8 KiB value, and 64 KiB total across names and values.

Secret substitution uses the same framing-aware HTTP/1.1 path. When a fixed-length request body must be scanned for a secret placeholder, the proxy streams it upstream with Transfer-Encoding: chunked so replacements cannot leave a stale content length. Origins that reject chunked HTTP/1.1 request bodies may be incompatible with body-based secret substitution.

Start options

| Option | Type | Description | | ------------ | ----------------------------------- | ------------------------------ | | instanceId | string | Stable instance directory name | | from | string | Checkpoint name to start from | | cpus | number | Number of vCPUs | | memoryMb | number | Memory in MB | | diskSizeMb | number | Disk size in MB | | dataDir | string | lsb runtime data directory | | ports | { host: number; guest: number }[] | Host-to-guest port forwards | | mounts | MountConfig[] | Directory mounts | | network | NetworkConfig | Network access policy |

mounts accepts discriminated entries:

| Type | Shape | Behavior | | --------- | ------------------------------------------------------------------------ | --------------------------------------------- | | overlay | { type: 'overlay'; hostPath: string; guestPath: string } | Host is read-only; guest writes go to overlay | | direct | { type: 'direct'; hostPath: string; guestPath: string; flags: number } | Direct mount with libc flags |

For direct mounts, flags: 0 is read-write and flags: 1 is MS_RDONLY. On Windows, direct mounts use SMB/CIFS, require an elevated Administrator shell, and use LocalSandbox-controlled proxy networking without enabling arbitrary outbound network access. If Windows local security policy denies network logon to NT AUTHORITY\Local account, direct mounts fail preflight; use lsb doctor windows-smb-policy to diagnose or lsb doctor windows-smb-policy --fix to apply the recommended local policy repair. lsb init --fix and initSandbox({ fix: true }) apply this repair as part of initialization.

On Windows, watch() on a direct SMB mount path uses a host-side Windows directory watcher and maps events back to guest paths. It reports host-created, modified, renamed, and deleted files, and it also reports guest writes that go through the CIFS mount. Read-only direct mounts can still be watched for host-originated changes while guest writes remain denied.

network enables proxy networking when present. It accepts:

| Option | Type | Description | | ------------------- | ------------------------------------ | ---------------------------------------- | | allow | string[] | Allowed outbound host patterns | | exposeHost | { host: number; guest?: number }[] | Host ports exposed to the guest | | secrets | Record<string, SecretConfig> | Secrets injected via the lsb proxy | | httpsInterception | HttpsInterceptionConfig | Opt-in HTTPS request-header interception |

Stream process output

import { Sandbox } from '@local-sandbox/lsb-nodejs'

const sandbox = await Sandbox.start()
const proc = await sandbox.spawn('echo out; echo err >&2')

for await (const chunk of proc.stdout) {
  process.stdout.write(chunk)
}

console.log(await proc.exited)

await sandbox.stop()

spawn() streams stdout and stderr on macOS and Windows x64. On Windows it runs over the virtio-serial session mux and supports cwd, stdin writes, kill, non-zero exits, and concurrent processes. Interactive PTY shells are still outside the Node API.

Watch files

import { Sandbox } from '@local-sandbox/lsb-nodejs'

const sandbox = await Sandbox.start()
const events = await sandbox.watch('/tmp')

for await (const event of events) {
  console.log(event.path, event.event)
}

watch() works on macOS and Windows x64. On Windows, normal guest paths and overlay/import mounts use guest-side inotify over the session mux. Direct SMB mount paths use the host-side watcher described above. A recursive Windows watch whose root is an ancestor of a direct SMB mount is rejected; watch the direct mount target directly or start separate watches.

Scripts

corepack yarn build
corepack yarn test
corepack yarn test:signed-node

corepack yarn test always builds the native binding first, then runs AVA against the generated root entrypoint. The positive VM smoke tests only run when runtime assets already exist in the platform default lsb data directory (~/.local/share/lsb on macOS/Linux, %LOCALAPPDATA%\lsb on Windows) or in LSB_NODEJS_TEST_DATA_DIR (Image is expected there and usually needs to be provisioned manually).

On macOS, positive VM tests also require the current node executable to have the com.apple.security.virtualization entitlement. To avoid modifying your global Node installation, use test:signed-node, which copies the current node binary into .signed-node/node, signs that local copy with ../../lsb.entitlements, prepends it to PATH, and then runs the local napi build --platform plus ava commands through that signed Node:

corepack yarn test:signed-node

If runtime assets are missing, provision them in the lsb data directory first. On Windows, positive VM tests require Windows 11 x64 with WHPX enabled and initialized managed QEMU host tools. The generated build outputs (index.js, index.d.ts, lsb-nodejs.*.node) are local artifacts and are ignored by git.

Platform Notes

  • Supported targets: macOS on Apple Silicon (aarch64-apple-darwin), macOS Intel (x86_64-apple-darwin), and Windows 11 x64 (x86_64-pc-windows-msvc / win32-x64-msvc).
  • Installation is limited to supported operating systems and CPU families where npm can express them. Unsupported platform packages should fail clearly instead of masking native-module load failures.
  • npm cannot express supported OS/CPU pairs in the root package metadata, so Windows ARM64 may be accepted by the root package metadata even though no Windows ARM64 native package is published. The loader reports this as unsupported Windows architecture; only win32-x64-msvc is supported for Windows.
  • The published native binaries live in the platform packages @local-sandbox/lsb-nodejs-darwin-arm64, @local-sandbox/lsb-nodejs-darwin-x64, and @local-sandbox/lsb-nodejs-win32-x64-msvc.
  • If the Windows native package is missing, the load error should name @local-sandbox/lsb-nodejs-win32-x64-msvc or lsb-nodejs.win32-x64-msvc.node. If the native module loads but QEMU, WHPX, or runtime assets are not ready, Sandbox.start() surfaces the Rust backend preflight error with the relevant remediation.