@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
latestandltspresets. - 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/runtimewhen 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/smartvmRuntime 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/shmexists. - 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 thisSmartVMinstance.
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
squashfsor 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
runtimeDiron a real tmpfs if/dev/shmis 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 configFirecracker 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:
kernelImagePathrootfsPathrootfsTyperootfsIsReadOnlybootArgsfirecrackerVersionchecksumssizes- source metadata
Cache behavior:
- Default cache directory:
/tmp/.smartvm/base-images - Default retention:
2bundles - Older bundles are evicted with a
console.warnwhen 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
\-> errorconst 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
NetworkManagerinstance - 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. toaccepts IPv4 addresses or CIDR ranges only.protocolcan beall,tcp,udp, oricmp.portsare destination ports and requireprotocol: 'tcp'orprotocol: 'udp'.- The implementation uses an owned
iptableschain jumped fromFORWARDfor 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:
routeAllVmTrafficdefaults totrue; set it tofalseto keep normal default-route NAT.failCloseddefaults totrue; VM forwarding to non-WireGuard egress interfaces is dropped when WireGuard routing is active.- Managed configs accept wg-quick-style
AddressandMTU, but rejectPreUp,PostUp,PreDown,PostDown, andSaveConfig. DNSandTablein managed configs are ignored; use host DNS and therouteTableoption instead.- IPv4 addresses and IPv4
AllowedIPsare 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 buildThe 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 testAccepted 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.
