lightbox

Mounts

Bind-mount host directories and share named volumes between sandboxes.

Bind-mount a host directory

Ship local code into the sandbox without baking it into the snapshot:

import { runInSandbox } from "@beamhop/lightbox";

const out = await runInSandbox(
  {
    snapshot: "node-ci",
    name: "test-run",
    mounts: { "/work": "./my-project" },  // host path → guest path
    workdir: "/work",
  },
  (sb) => sb.shell("npm test"),
);
console.log(out.stdout());

The host directory is visible inside the guest at /work for the lifetime of the sandbox. Writes from the guest appear on the host.

Share a persistent volume across sandboxes

A named volume survives sandbox restarts and can be mounted into many sandboxes — perfect for caches:

import { launchSandbox } from "@beamhop/lightbox";

async function run(jobId: string) {
  const sb = await launchSandbox({
    snapshot: "node-ci",
    name: `job-${jobId}`,
    mounts: {
      "/work":  "./my-project",
      "/root/.npm": { volume: "npm-cache" },   // shared across all jobs
    },
    workdir: "/work",
  });
  await sb.shell("npm ci && npm test");
  await sb.stop();
}

await Promise.all([run("1"), run("2"), run("3")]);

The first launchSandbox creates the npm-cache volume on demand. The second and third calls reuse it. After all three jobs finish, the cache persists for the next run.

Configure quota / labels up front

If you care about the volume’s quota or labels, create it explicitly. After that, just reference the name in mounts:

import { ensureVolume, launchSandbox } from "@beamhop/lightbox";

await ensureVolume("npm-cache", {
  quotaMib: 4096,
  labels: { team: "platform", purpose: "ci-cache" },
});

const sb = await launchSandbox({
  snapshot: "node-ci",
  name: "ci-1",
  mounts: { "/root/.npm": { volume: "npm-cache" } },
});

ensureVolume is idempotent — calling it on an existing volume is a no-op. Quota and labels are only applied on initial creation; they’re not edited on subsequent calls.

Clean up

import { removeVolume } from "@beamhop/lightbox";

await removeVolume("npm-cache");   // idempotent — silent if it doesn't exist