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

@beamhop/microsandbox

v0.1.2

Published

Execute Dockerfile RUN steps inside a microsandbox microVM, and load built images straight into its cache. No Docker.

Downloads

452

Readme

@beamhop/microsandbox

Execute Dockerfile RUN steps inside a microsandbox microVM, and load finished images straight into its cache. This is the piece that removes Docker from the build.

bun add @beamhop/microsandbox microsandbox

Loading an image into microsandbox

The handoff the whole project exists for: an image goes from beambox to a runnable microVM with no registry, no daemon, and no Docker in the path.

import { loadIntoMicrosandbox } from "@beamhop/microsandbox"

const loaded = await loadIntoMicrosandbox(image, { tags: ["app:local"] })
console.log(loaded[0]?.reference) // "app:local"
msb run app:local

An archive is written to a temp file, imported via the microsandbox SDK, and deleted. The OCI layout form is used by default because it keeps layers compressed and so costs no recompression; pass { format: "docker" } for a docker save archive instead.

The RUN executor

import { build } from "@beamhop/builder"
import { microsandboxExecutor } from "@beamhop/microsandbox"

const image = await build(plan, {
  store,
  executor: microsandboxExecutor({ memory: 2048, cpus: 2, timeout: 600_000 }),
})

Options: memory (MiB), cpus, timeout (ms per step), cacheDir for the busybox bootstrap binary, and keepOnFailure to leave the sandbox and staged image in place for debugging.

How it works

For each stage:

  1. The image built so far is written to an OCI archive and Image.loaded into microsandbox's cache under a temporary tag.
  2. A sandbox is created from it, with a host scratch directory bind-mounted at /beambox so layer tars land directly on the host instead of being streamed through the agent.
  3. A statically linked busybox is placed in that scratch directory — pulled from busybox:musl through beambox's own registry client, then cached. The guest therefore needs no shell, tar, or find of its own, so RUN works on scratch and distroless bases too.
  4. Each step runs, and the layer it produced is captured.
  5. On close the sandbox is stopped and removed, the staged image is deleted, and the scratch directory is cleaned up — including when a step failed.

Capturing what a step changed

microsandbox composes the sandbox root as an overlay whose writable upper would be the layer diff exactly — but that upper is not reachable from inside the guest, so it cannot be read directly. Instead the root filesystem is indexed before and after each step and the listings compared.

find -xdev keeps the walk on the overlay itself. /proc, /sys, /dev, /tmp, cache mounts, and beambox's own scratch mount are each a separate filesystem, so they drop out for free rather than needing an exclusion list that could drift — and that is also why a RUN --mount=type=cache never leaks into the image.

Deletions become OCI .wh. whiteouts, so rm inside a RUN behaves exactly as it does under Docker. Directories are synthesised from the listing rather than handed to tar, which would otherwise recurse and drag unchanged files from earlier steps into the layer.

The diff functions are pure and exported, so this logic is testable without a VM:

import { diffListings, parseListing, repackLayerTar } from "@beamhop/microsandbox"

const before = parseListing(await snapshotOne())
const after = parseListing(await snapshotTwo())
const diff = diffListings(before, after)

diff.changed  // entries that appeared or changed
diff.deleted  // paths that vanished, collapsed to subtree roots

Mounts

RUN --mount declarations become sandbox volumes. Because a sandbox takes its mounts at creation time, the whole stage's set is collected up front.

  • type=cache becomes a named volume keyed on the mount's id, so a package cache survives across builds.
  • type=tmpfs becomes an in-memory filesystem.
  • type=bind becomes a read-only bind mount from the build context.

Limits

  • Host architecture only. microsandbox boots native microVMs with no emulation layer, so supports() rejects a foreign architecture and the build fails with PlatformMismatchError rather than mislabelling the image.
  • Change detection uses size, mtime, and mode. Content rewritten in place with all three preserved would not be detected.
  • RUN --mount=type=bind,from=… is not implemented. Use COPY --from instead; the error says so.