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

@push.rocks/smartvm

v1.4.1

Published

A TypeScript module wrapping Amazon Firecracker VMM for managing lightweight microVMs

Readme

@push.rocks/smartvm

Boot and control Firecracker microVMs from TypeScript without rebuilding the same host plumbing every time. @push.rocks/smartvm handles Firecracker binaries, Unix-socket API calls, per-VM runtime directories, base-image bundles, TAP/bridge networking, optional egress firewalling, host-side WireGuard routing, and cleanup so you can focus on the VM workload.

Issue Reporting and Security

For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.

Why It Rocks

smartvm is for programmers who want real microVM isolation with a TypeScript control plane and sane defaults:

  • 🚀 Boot Firecracker VMs with a few typed calls.
  • 🧊 Keep runtime state ephemeral by default with tmpfs-backed sockets and staged writable drives.
  • 🧰 Use known-good Firecracker CI images for quick starts, or ship your own hosted image manifest with SHA256 verification.
  • 🌐 Create Linux TAP devices, bridges, deterministic guest MACs, static guest IP data, and NAT without custom scripts.
  • 🔒 Apply VM-subnet egress policy with ordered IPv4 firewall rules.
  • 🕳️ Route all VM traffic through a host WireGuard interface without installing WireGuard in the guest.
  • 🧹 Tear down processes, sockets, TAP devices, bridge/NAT/firewall/WireGuard state, and staged drive copies.

What It Does

smartvm wraps the operational parts of Amazon Firecracker:

  • Downloads and caches Firecracker binaries.
  • Resolves bootable Firecracker CI base-image bundles with latest and lts presets.
  • Supports project-owned hosted base-image manifests with SHA256 verification.
  • Creates and controls microVMs through Firecracker's HTTP-over-Unix-socket API.
  • Converts TypeScript camelCase config into Firecracker's snake_case payloads.
  • Creates TAP devices, a Linux bridge, static guest network assignments, and NAT rules.
  • Applies optional global VM egress firewall rules for the managed subnet.
  • Routes VM egress through host-side WireGuard when configured.
  • Defaults VM runtime artifacts to tmpfs via /dev/shm/.smartvm/runtime when available.
  • Stages writable drives into per-VM ephemeral storage by default so guest writes do not touch cached rootfs files.
  • Cleans up Firecracker processes, sockets, TAPs, bridges, NAT/firewall/WireGuard rules, and staged drive copies.

The default mental model is: immutable base image, explicit writable scratch, no accidental persistent state, and persistence only when you opt in.

Install

pnpm add @push.rocks/smartvm

Runtime Requirements

Firecracker is a Linux/KVM technology. The package is TypeScript, but the runtime host must provide the VM substrate.

| Requirement | Why it matters | |---|---| | Linux with /dev/kvm | Firecracker needs KVM acceleration. | | Firecracker binary | Downloaded by ensureBinary() or supplied through firecrackerBinaryPath. | | Root privileges for networking | TAP devices, bridges, IP forwarding, iptables NAT, firewalling, and WireGuard policy routing require elevated privileges. | | Host tools: curl, tar, ip, sysctl, iptables, wg when WireGuard is used | Used for binary/image downloads and network setup. dd and mkfs.ext4 are only needed for ImageManager.createBlankRootfs(). | | Enough tmpfs memory | Writable VM drives are copied into /dev/shm by default when available. |

If you only use VMConfig, SocketClient, or custom low-level flows without creating host networking, those pieces do not need root. Actual Firecracker boot still needs a Linux/KVM-capable host.

Quick Start

This is the happy path: let smartvm download Firecracker, resolve a known-good base image, boot it, and clean everything up. Run it on a Linux host with KVM access.

import { SmartVM } from '@push.rocks/smartvm';

const smartvm = new SmartVM({
  // Optional. Defaults are intentionally disk-light.
  dataDir: '/tmp/.smartvm',
  runtimeDir: '/dev/shm/.smartvm/runtime',
});

const baseImage = await smartvm.ensureBaseImage({ preset: 'latest' });

const vm = await smartvm.createVM({
  id: 'hello-firecracker',
  bootSource: {
    kernelImagePath: baseImage.kernelImagePath,
    bootArgs: baseImage.bootArgs,
  },
  machineConfig: {
    vcpuCount: 1,
    memSizeMib: 256,
  },
  drives: [
    {
      driveId: 'rootfs',
      pathOnHost: baseImage.rootfsPath,
      isRootDevice: true,
      isReadOnly: baseImage.rootfsIsReadOnly,
    },
  ],
});

try {
  await vm.start();
  console.log(vm.state); // "running"
  console.log(await vm.getVersion());
  console.log(await vm.getInfo());
} finally {
  if (vm.state === 'running' || vm.state === 'paused') {
    await vm.stop();
  }
  await vm.cleanup();
  await smartvm.cleanup();
}

What happened:

  • Firecracker was downloaded or reused from /tmp/.smartvm/bin.
  • A base image bundle was resolved and cached under /tmp/.smartvm/base-images.
  • A per-VM socket directory was created under /dev/shm/.smartvm/runtime/<vmId> when /dev/shm exists.
  • Writable drives would be staged into that runtime directory before boot; read-only drives stay in place.
  • cleanup() removed VM runtime files and networking resources owned by this SmartVM instance.

Disk-Light Runtime Model

By default, smartvm treats VMs as ephemeral execution units.

| Path | Default | Persistence model | |---|---|---| | Firecracker binaries | /tmp/.smartvm/bin | Cached for reuse. | | Base images | /tmp/.smartvm/base-images | Cached, retention-limited, verified before reuse. | | VM sockets | /dev/shm/.smartvm/runtime/<vmId>/firecracker.sock | Per-VM tmpfs, deleted on cleanup. | | Writable drives | /dev/shm/.smartvm/runtime/<vmId>/drives/* | Per-VM tmpfs copy, deleted on cleanup. | | Read-only drives | Original path | Not copied unless ephemeral: true. |

Writable drives are staged into the VM runtime directory before boot. Firecracker receives the staged path, so guest writes do not modify cached base images or source rootfs files.

const vm = await smartvm.createVM({
  bootSource: { kernelImagePath: baseImage.kernelImagePath, bootArgs: baseImage.bootArgs },
  machineConfig: { vcpuCount: 1, memSizeMib: 256 },
  drives: [
    {
      driveId: 'rootfs',
      pathOnHost: baseImage.rootfsPath,
      isRootDevice: true,
      isReadOnly: false,
      // Default for writable drives: true
      ephemeral: true,
    },
  ],
});

Opt into persistence only when that is the point:

const persistentVm = await smartvm.createVM({
  bootSource: { kernelImagePath: '/images/vmlinux', bootArgs: 'console=ttyS0 reboot=k panic=1 pci=off' },
  machineConfig: { vcpuCount: 2, memSizeMib: 512 },
  drives: [
    {
      driveId: 'state',
      pathOnHost: '/var/lib/my-vm/state.ext4',
      isRootDevice: true,
      isReadOnly: false,
      ephemeral: false,
    },
  ],
});

You can also disable writable-drive staging globally:

const smartvm = new SmartVM({
  ephemeralWritableDrives: false,
});

Best practice for high-volume VM starts:

  • Prefer squashfs or another read-only root filesystem.
  • Put mutable scratch data on tmpfs-backed writable drives.
  • Keep shared assets read-only by default.
  • Use external services, object storage, databases, or explicit persistent drives for durable state.
  • Use a dedicated runtimeDir on a real tmpfs if /dev/shm is too small or unavailable.

Architecture

SmartVM
  ImageManager          downloads/caches Firecracker binaries and manual images
  BaseImageManager      resolves known-good base-image bundles
  NetworkManager        creates TAP devices, bridge, NAT, firewall/WireGuard egress, and static guest network data
  MicroVM
    FirecrackerProcess  starts/stops the VMM process
    SocketClient        talks HTTP over the Firecracker Unix socket
    VMConfig            validates and transforms TypeScript config

Firecracker exposes a REST API over a Unix domain socket. smartvm starts the child process, waits for readiness, sends pre-boot config in the right order, starts the instance, and tears down host resources when you are done.

SmartVM

SmartVM is the top-level orchestrator.

import { SmartVM } from '@push.rocks/smartvm';
import type { ISmartVMOptions } from '@push.rocks/smartvm';

const options: ISmartVMOptions = {
  dataDir: '/tmp/.smartvm',
  runtimeDir: '/dev/shm/.smartvm/runtime',
  ephemeralWritableDrives: true,
  firecrackerVersion: 'v1.7.0',
  arch: 'x86_64',
  firecrackerBinaryPath: '/usr/bin/firecracker',
  bridgeName: 'svbr0',
  subnet: '172.30.0.0/24',
  firewall: {
    egress: {
      defaultAction: 'allow',
      rules: [],
    },
  },
  wireguard: {
    existingInterface: 'wg0',
    failClosed: true,
  },
  baseImageCacheDir: '/tmp/.smartvm/base-images',
  maxStoredBaseImages: 2,
  baseImageManifestUrl: 'https://assets.example.com/smartvm/manifest.json',
  baseImageManifestPath: './assets/base-images/local.manifest.json',
};

const smartvm = new SmartVM(options);

| API | Description | |---|---| | ensureBinary() | Ensures the Firecracker binary exists and returns its path. | | ensureBaseImage(options) | Resolves/downloads a base-image bundle and returns kernel/rootfs paths plus boot args. | | createVM(config) | Creates a MicroVM instance. It does not boot until vm.start(). | | getRuntimeDir() | Returns the active runtime directory used for per-VM tmpfs artifacts. | | getVM(id) | Looks up an active VM by ID. | | listVMs() | Lists active VM IDs. | | vmCount | Number of tracked VMs. | | removeVM(id) | Removes a VM from the internal tracking map. | | stopAll() | Stops every running or paused VM. | | cleanup() | Cleans up all tracked VMs and networking resources. |

Base Images

BaseImageManager gives you fast bootable image discovery without committing giant rootfs files to git. The latest and lts presets use Firecracker CI artifacts, which are excellent for tests, demos, and bring-up. For product workloads, prefer the hosted manifest path so you control the kernel/rootfs pair and verify artifacts with SHA256.

const baseImage = await smartvm.ensureBaseImage(); // preset: "latest"
const ltsBaseImage = await smartvm.ensureBaseImage({ preset: 'lts' });
const freshBaseImage = await smartvm.ensureBaseImage({ preset: 'latest', forceDownload: true });

Presets:

| Preset | Behavior | |---|---| | latest | Resolves the latest Firecracker release and matching CI demo artifacts. | | lts | Uses the pinned Firecracker CI train v1.7 / Firecracker v1.7.0. | | hosted | Uses a project-owned manifest. Requires manifestUrl, manifestPath, or manager-level hosted manifest options. |

The resolver prefers read-only squashfs rootfs artifacts when Firecracker CI exposes them, falling back to ext4 when needed.

import { BaseImageManager } from '@push.rocks/smartvm';

const baseImageManager = new BaseImageManager({
  arch: 'x86_64',
  cacheDir: '/tmp/.smartvm/base-images',
  maxStoredBaseImages: 4,
  hostedManifestPath: './assets/base-images/smartvm-minimal.manifest.json',
});

console.log(baseImageManager.getCacheDir());
console.log(baseImageManager.getMaxStoredBaseImages());

const hosted = await baseImageManager.ensureBaseImage({ preset: 'hosted' });
const evictedBundleIds = await baseImageManager.pruneBaseImageCache(hosted.bundleId);

IBaseImageBundle contains:

  • kernelImagePath
  • rootfsPath
  • rootfsType
  • rootfsIsReadOnly
  • bootArgs
  • firecrackerVersion
  • checksums
  • sizes
  • source metadata

Cache behavior:

  • Default cache directory: /tmp/.smartvm/base-images
  • Default retention: 2 bundles
  • Older bundles are evicted with a console.warn when retention is exceeded
  • Cached artifacts are checked for size and SHA256 before reuse
  • Hosted URL artifacts require SHA256 hashes
  • Hosted local-path artifacts may omit SHA256, but hashes are still recorded in the cached manifest

Hosted manifests are the clean way to ship a project-owned minimal image. Keep big binaries in object storage or release assets, keep only the manifest in git, and let smartvm verify the exact bytes before boot.

Hosted manifest example:

The repository ships an example at assets/base-images/smartvm-minimal.manifest.example.json.

{
  "schemaVersion": 1,
  "bundleId": "smartvm-minimal-v1-x86_64",
  "name": "SmartVM minimal x86_64 bundle",
  "arch": "x86_64",
  "firecrackerVersion": "v1.15.1",
  "rootfsType": "squashfs",
  "rootfsIsReadOnly": true,
  "bootArgs": "console=ttyS0 reboot=k panic=1 pci=off ro rootfstype=squashfs",
  "kernel": {
    "url": "https://assets.example.com/smartvm/vmlinux",
    "fileName": "vmlinux",
    "sha256": "0000000000000000000000000000000000000000000000000000000000000000",
    "sizeBytes": 12345678
  },
  "rootfs": {
    "url": "https://assets.example.com/smartvm/rootfs.squashfs",
    "fileName": "rootfs.squashfs",
    "sha256": "0000000000000000000000000000000000000000000000000000000000000000",
    "sizeBytes": 12345678
  }
}

MicroVM Lifecycle

MicroVM is a single Firecracker instance with a strict state machine:

created -> configuring -> running -> paused -> stopped
                         \-> error
const vm = await smartvm.createVM({
  id: 'api-worker-1',
  bootSource: {
    kernelImagePath: baseImage.kernelImagePath,
    bootArgs: baseImage.bootArgs,
  },
  machineConfig: {
    vcpuCount: 2,
    memSizeMib: 512,
    smt: false,
    cpuTemplate: 'T2',
    trackDirtyPages: true,
  },
  drives: [
    {
      driveId: 'rootfs',
      pathOnHost: baseImage.rootfsPath,
      isRootDevice: true,
      isReadOnly: baseImage.rootfsIsReadOnly,
      cacheType: 'Unsafe',
      ephemeral: true,
      rateLimiter: {
        bandwidth: { size: 100_000_000, refillTime: 1_000_000_000 },
        ops: { size: 1000, refillTime: 1_000_000_000 },
      },
    },
  ],
  networkInterfaces: [{ ifaceId: 'eth0' }],
  vsock: {
    guestCid: 3,
    udsPath: '/dev/shm/api-worker-1.vsock',
  },
  balloon: {
    amountMib: 128,
    deflateOnOom: true,
    statsPollingIntervalS: 5,
  },
  mmds: {
    version: 'V2',
    networkInterfaces: ['eth0'],
  },
});

await vm.start();
await vm.pause();
await vm.resume();
await vm.stop();
await vm.cleanup();

| API | Valid state | Description | |---|---|---| | start() | created | Stages ephemeral drives, starts Firecracker, applies config, boots the VM. | | pause() | running | Pauses execution. | | resume() | paused | Resumes execution. | | stop() | running, paused | Sends Ctrl+Alt+Del, waits briefly, then stops the process. | | cleanup() | any | Stops process, deletes sockets/runtime dir, removes auto-created TAPs. | | getInfo() | after start | Returns Firecracker instance info. | | getVersion() | after start | Returns Firecracker version info. | | setMetadata(data) | running, paused | Writes MMDS metadata. | | getMetadata() | running, paused | Reads MMDS metadata. | | updateDrive(id, path) | running, paused | Hot-updates a drive path. | | updateNetworkInterface(id, update) | running, paused | Updates network interface config such as rate limiters. | | updateBalloon(mib) | running, paused | Resizes the balloon device. | | createSnapshot(params) | paused | Creates a Firecracker snapshot. | | loadSnapshot(params) | created, configuring | Low-level Firecracker snapshot-load call; requires an initialized socket client. | | getTapDevices() | any | Returns TAP devices created automatically by this VM. | | getVMConfig() | any | Returns the internal VMConfig instance. | | getRuntimeDir() | any | Returns the per-VM runtime directory after it has been created. |

Snapshot caveat: diff snapshots require dirty-page tracking to be enabled before boot through machineConfig.trackDirtyPages. Snapshot restore is currently exposed as a low-level API; full restore orchestration should be built around the Firecracker process/config lifecycle intentionally.

Networking

NetworkManager creates host-side networking primitives. It does not run DHCP inside the guest. Your guest image must configure its interface itself, or you must pass static ip= kernel boot arguments.

Automatic mode:

const vm = await smartvm.createVM({
  bootSource: { kernelImagePath: baseImage.kernelImagePath, bootArgs: baseImage.bootArgs },
  machineConfig: { vcpuCount: 1, memSizeMib: 256 },
  drives: [{ driveId: 'rootfs', pathOnHost: baseImage.rootfsPath, isRootDevice: true, isReadOnly: baseImage.rootfsIsReadOnly }],
  networkInterfaces: [{ ifaceId: 'eth0' }],
});

Static-kernel-args mode:

const tap = await smartvm.networkManager.createTapDevice('net-vm', 'eth0');

const vm = await smartvm.createVM({
  id: 'net-vm',
  bootSource: {
    kernelImagePath: baseImage.kernelImagePath,
    bootArgs: `${baseImage.bootArgs} ${smartvm.networkManager.getGuestNetworkBootArgs(tap)}`,
  },
  machineConfig: { vcpuCount: 1, memSizeMib: 256 },
  drives: [{ driveId: 'rootfs', pathOnHost: baseImage.rootfsPath, isRootDevice: true, isReadOnly: baseImage.rootfsIsReadOnly }],
  networkInterfaces: [
    {
      ifaceId: 'eth0',
      hostDevName: tap.tapName,
      guestMac: tap.mac,
    },
  ],
});

Networking behavior:

  • Default bridge: svbr0
  • Default subnet: 172.30.0.0/24
  • Subnet input is normalized to the network address
  • Prefix length must be 1-30
  • Gateway uses the first usable address
  • Guest IP allocation starts at the second usable address
  • Allocation is sequential and not reused within the same NetworkManager instance
  • MAC addresses are deterministic and locally administered (02:xx:xx:xx:xx:xx)
  • TAP names are capped to Linux's 15-character IFNAMSIZ limit
  • NAT masquerade uses the host default route interface unless WireGuard egress is configured
  • Use a dedicated bridge name; cleanup() tears down the bridge configured by this manager

Egress Firewall

Configure firewall.egress on SmartVM to apply one ordered policy to all VMs using that manager's subnet. The default action is allow, so existing behavior is preserved unless you opt into a stricter policy.

const smartvm = new SmartVM({
  firewall: {
    egress: {
      defaultAction: 'deny',
      rules: [
        { action: 'allow', to: '1.1.1.1', protocol: 'udp', ports: 53, comment: 'DNS' },
        { action: 'allow', to: '203.0.113.0/24', protocol: 'tcp', ports: [443] },
      ],
    },
  },
});

Firewall behavior:

  • Rules are evaluated in order before the final defaultAction.
  • to accepts IPv4 addresses or CIDR ranges only.
  • protocol can be all, tcp, udp, or icmp.
  • ports are destination ports and require protocol: 'tcp' or protocol: 'udp'.
  • The implementation uses an owned iptables chain jumped from FORWARD for traffic from the VM subnet.

WireGuard Egress

WireGuard routing is host-side. The guest does not need WireGuard installed; smartvm policy-routes packets from the VM subnet through a WireGuard interface and leaves normal host traffic on the host default route.

Managed interface mode creates and removes the WireGuard interface:

const smartvm = new SmartVM({
  wireguard: {
    interfaceName: 'svwg0',
    routeTable: 51820,
    failClosed: true,
    config: `
[Interface]
PrivateKey = <private-key>
Address = 10.70.0.2/32
MTU = 1420

[Peer]
PublicKey = <public-key>
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.example.com:51820
PersistentKeepalive = 25
`,
  },
});

Existing interface mode uses an interface you manage outside smartvm:

const smartvm = new SmartVM({
  wireguard: {
    existingInterface: 'wg0',
    routeTable: 51820,
    failClosed: true,
  },
});

WireGuard behavior:

  • routeAllVmTraffic defaults to true; set it to false to keep normal default-route NAT.
  • failClosed defaults to true; VM forwarding to non-WireGuard egress interfaces is dropped when WireGuard routing is active.
  • Managed configs accept wg-quick-style Address and MTU, but reject PreUp, PostUp, PreDown, PostDown, and SaveConfig.
  • DNS and Table in managed configs are ignored; use host DNS and the routeTable option instead.
  • IPv4 addresses and IPv4 AllowedIPs are supported in this release.
  • cleanup() removes owned policy routes, iptables rules, NAT rules, and managed WireGuard interfaces. Existing WireGuard interfaces are not deleted.

ImageManager

ImageManager is the lower-level helper for Firecracker binaries and manually managed kernel/rootfs files.

const imageManager = smartvm.imageManager;

await imageManager.ensureDirectories();

const latest = await imageManager.getLatestVersion();
const firecrackerPath = await imageManager.downloadFirecracker(latest);

const kernelPath = await imageManager.downloadKernel(
  'https://example.com/vmlinux',
  'vmlinux',
);

const rootfsPath = await imageManager.downloadRootfs(
  'https://example.com/rootfs.ext4',
  'rootfs.ext4',
);

const blankRootfs = await imageManager.createBlankRootfs('scratch.ext4', 1024);
const clonedRootfs = await imageManager.cloneRootfs(rootfsPath, 'vm-rootfs.ext4');

Useful path helpers:

  • getBinDir()
  • getKernelsDir()
  • getRootfsDir()
  • getSocketsDir()
  • getFirecrackerPath(version)
  • getJailerPath(version)
  • getSocketPath(vmId)

Note: SmartVM.createVM() uses runtimeDir/<vmId>/firecracker.sock for new VM sockets by default. ImageManager.getSocketPath() remains available for lower-level/custom flows.

VMConfig

VMConfig validates IMicroVMConfig and transforms it into Firecracker API payloads.

import { VMConfig } from '@push.rocks/smartvm';

const vmConfig = new VMConfig({
  bootSource: { kernelImagePath: '/images/vmlinux', bootArgs: 'console=ttyS0 reboot=k panic=1 pci=off' },
  machineConfig: { vcpuCount: 2, memSizeMib: 256 },
  drives: [{ driveId: 'rootfs', pathOnHost: '/images/rootfs.ext4', isRootDevice: true }],
});

const validation = vmConfig.validate();
if (!validation.valid) {
  throw new Error(validation.errors.join('; '));
}

console.log(vmConfig.toBootSourcePayload());
console.log(vmConfig.toMachineConfigPayload());
console.log(vmConfig.toDrivePayload(vmConfig.config.drives![0]));

The constructor clones caller-provided config, so internal normalization does not mutate your original object.

SocketClient

SocketClient is the raw Firecracker API client. Most users should go through MicroVM, but the low-level client is exported for tooling and diagnostics.

import { SocketClient } from '@push.rocks/smartvm';

const client = new SocketClient({ socketPath: '/dev/shm/.smartvm/runtime/vm/firecracker.sock' });

const version = await client.get('/version');
const machineConfig = await client.put('/machine-config', {
  vcpu_count: 1,
  mem_size_mib: 256,
});
const paused = await client.patch('/vm', { state: 'Paused' });

console.log(version.ok, version.statusCode, version.body);
console.log(machineConfig.ok, machineConfig.statusCode, machineConfig.body);
console.log(paused.ok, paused.statusCode, paused.body);

SocketClient returns { ok, statusCode, body }. Non-2xx responses do not become API_ERROR until higher-level MicroVM helpers validate them.

Metadata Service

Firecracker MMDS lets the host pass structured metadata to a running VM.

const vm = await smartvm.createVM({
  bootSource: { kernelImagePath: baseImage.kernelImagePath, bootArgs: baseImage.bootArgs },
  machineConfig: { vcpuCount: 1, memSizeMib: 256 },
  drives: [{ driveId: 'rootfs', pathOnHost: baseImage.rootfsPath, isRootDevice: true, isReadOnly: baseImage.rootfsIsReadOnly }],
  networkInterfaces: [{ ifaceId: 'eth0' }],
  mmds: {
    version: 'V2',
    networkInterfaces: ['eth0'],
  },
});

await vm.start();

await vm.setMetadata({
  instance: { id: 'api-worker-1', region: 'local' },
  config: { mode: 'ephemeral' },
});

console.log(await vm.getMetadata());

Error Handling

All package-level failures use SmartVMError with structured codes.

import { SmartVMError } from '@push.rocks/smartvm';

try {
  await vm.start();
} catch (err) {
  if (err instanceof SmartVMError) {
    console.error(err.code);
    console.error(err.statusCode);
    console.error(err.details);
  }
  throw err;
}

| Code | Meaning | |---|---| | INVALID_STATE | Operation is invalid for the current VM state. | | INVALID_CONFIG | VM configuration failed validation. | | SOCKET_TIMEOUT | Firecracker did not create its socket in time. | | API_TIMEOUT | Firecracker API readiness check timed out. | | SOCKET_REQUEST_FAILED | Unix-socket HTTP request failed. | | API_ERROR | Firecracker returned a non-2xx response through a high-level VM call. | | BINARY_NOT_FOUND | Custom Firecracker binary path does not exist. | | DOWNLOAD_FAILED | Binary, kernel, or rootfs download failed. | | VERSION_FETCH_FAILED | Latest Firecracker version lookup failed. | | BASE_IMAGE_RESOLVE_FAILED | Firecracker CI base-image artifact resolution failed. | | BASE_IMAGE_MANIFEST_FAILED | Hosted manifest could not be loaded or used. | | BASE_IMAGE_PREPARE_FAILED | Base-image download/copy/verification failed. | | INVALID_BASE_IMAGE_MANIFEST | Hosted manifest schema or artifact metadata is invalid. | | INVALID_BASE_IMAGE_CACHE_LIMIT | Base-image retention limit is invalid. | | INVALID_SUBNET | Subnet is not a supported IPv4 CIDR. | | INVALID_INTERFACE_NAME | Bridge or TAP name is invalid. | | INVALID_FIREWALL_CONFIG | VM egress firewall config is invalid. | | INVALID_WIREGUARD_CONFIG | WireGuard egress config is invalid. | | IP_EXHAUSTED | No guest IPs remain in the configured subnet. | | BRIDGE_SETUP_FAILED | Bridge/NAT setup failed. | | WIREGUARD_SETUP_FAILED | WireGuard interface or policy-route setup failed. | | TAP_CREATE_FAILED | TAP creation failed. | | ROOTFS_CREATE_FAILED | Blank rootfs creation failed. | | ROOTFS_CLONE_FAILED | Rootfs clone failed. | | START_FAILED | VM start sequence failed. | | NO_CLIENT | Socket client is not initialized. |

Testing

Default tests are safe on machines without KVM or root privileges:

pnpm test
pnpm run build

The default suite covers config validation, payload generation, lifecycle guards, base-image cache behavior, hosted manifest validation, VM tracking, ephemeral drive staging, subnet/IP behavior, and firewall/WireGuard option validation.

Opt into real Firecracker boot tests on a Linux/KVM host:

SMARTVM_RUN_INTEGRATION=true pnpm test

Accepted truthy values for SMARTVM_RUN_INTEGRATION: 1, true, yes.

Useful integration-test environment variables:

| Variable | Purpose | |---|---| | SMARTVM_BASE_IMAGE_PRESET | latest or lts; default is latest. | | SMARTVM_BASE_IMAGE_MANIFEST_URL | Hosted/project-owned base-image manifest URL. | | SMARTVM_BASE_IMAGE_MANIFEST_PATH | Local hosted manifest path. | | SMARTVM_BASE_IMAGE_CACHE_DIR | Override /tmp/.smartvm/base-images. | | SMARTVM_MAX_STORED_BASE_IMAGES | Override default retention of 2. | | SMARTVM_FIRECRACKER_VERSION | Override the Firecracker binary version. | | SMARTVM_ARCH | x86_64 or aarch64; defaults from host architecture. | | SMARTVM_INTEGRATION_DATA_DIR | Override the Firecracker binary data directory used by integration tests. |

TypeScript Surface

Main exports:

export {
  SmartVM,
  MicroVM,
  NetworkManager,
  FirecrackerProcess,
  BaseImageManager,
  ImageManager,
  SocketClient,
  VMConfig,
};

Important exported types:

import type {
  ISmartVMOptions,
  IMicroVMRuntimeOptions,
  IMicroVMConfig,
  IBootSource,
  IMachineConfig,
  IDriveConfig,
  INetworkInterfaceConfig,
  IVsockConfig,
  IBalloonConfig,
  IMmdsConfig,
  ILoggerConfig,
  IMetricsConfig,
  IBaseImageManagerOptions,
  IEnsureBaseImageOptions,
  IBaseImageBundle,
  IBaseImageHostedManifest,
  IBaseImageArtifactManifest,
  ISnapshotCreateParams,
  ISnapshotLoadParams,
  IRateLimiter,
  INetworkManagerOptions,
  ITapDevice,
  IFirewallConfig,
  IFirewallEgressConfig,
  IFirewallRule,
  TFirewallAction,
  TFirewallProtocol,
  TWireGuardConfig,
  IWireGuardManagedConfig,
  IWireGuardExistingInterfaceConfig,
  ISocketClientOptions,
  IApiResponse,
  TVMState,
  TFirecrackerArch,
  TCacheType,
  TSnapshotType,
  TLogLevel,
  TBaseImagePreset,
  TBaseImageRootfsType,
} from '@push.rocks/smartvm';

License and Legal Information

This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the LICENSE file.

Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.

Trademarks

This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.

Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.

Company Information

Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany

For any legal inquiries or further information, please contact us via email at [email protected].

By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.