@machinen/runtime 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -128,6 +128,8 @@ interface PhaseLogEvent {
128
128
  type LogEvent = ChunkLogEvent | PhaseLogEvent;
129
129
  type OnLog = (evt: LogEvent) => void;
130
130
 
131
+ type SnapshotEngine = "criu" | "vmstate";
132
+
131
133
  interface AttachOptions {
132
134
  /**
133
135
  * Look up a VM by the host pid of its VMM process. Kernel-unique
@@ -325,34 +327,45 @@ interface BootOptions {
325
327
  upperPath: string;
326
328
  };
327
329
  /**
328
- * Host directories exposed to the guest as live-share FUSE mounts
329
- * (#78). Unlike `mount` (copy-once into the boot rootfs), these stay
330
- * connected to the host: the guest reads on demand via a vsock FUSE
331
- * relay, and nothing is copied at boot. `mode` defaults to `"rw"`
332
- * guest writes land on the host (#151, #156). Set `"ro"` for a
333
- * one-way share (host caches, untrusted guests).
330
+ * Vmstate engine restore: absolute path to the bundle's
331
+ * `state.vmstate`. Set by `restore()` when it detects a vmstate
332
+ * bundle. `boot()` forwards it to the VMM as `MACHINEN_RESTORE_PATH`
333
+ * the VMM loads that whole-VM state before the first vCPU run, so
334
+ * the guest resumes mid-execution instead of cold-booting.
335
+ *
336
+ * @internal
337
+ */
338
+ _vmstateRestorePath?: string;
339
+ /**
340
+ * Host directories exposed to the guest as live-share mounts (#78,
341
+ * #332). Unlike `mount` (copy-once into the boot rootfs), these stay
342
+ * connected to the host: the guest reads on demand and nothing is
343
+ * copied at boot. `mode` defaults to `"rw"` — guest writes land on
344
+ * the host (#151, #156). Set `"ro"` for a one-way share (host
345
+ * caches, untrusted guests).
334
346
  *
335
347
  * Each guest path must live under `/mnt/` (same rule as `mount`).
336
- * Repeatable; each entry gets its own vsock port.
348
+ * Repeatable up to 4 entries per VM — each is served by its own
349
+ * in-VMM virtio-fs device (the VMM wires 4 virtio-fs slots). The
350
+ * FUSE opcode handlers run inside the VMM and the guest mounts each
351
+ * share directly with `mount -t virtiofs` — no agent process, no
352
+ * vsock hop. Requires a guest kernel with `CONFIG_VIRTIO_FS` — every
353
+ * machinen-built kernel has it. (The older FUSE-over-vsock transport
354
+ * and its `protocol` knob were removed in #338.)
337
355
  *
338
356
  * Snapshot / restore / fork (#273): liveMount has no guest-side
339
357
  * state worth checkpointing — reads come from the host on demand,
340
- * writes (in `"rw"`) land on the host immediately. The runtime
341
- * unmounts each mount before CRIU dumps, then re-establishes a
342
- * fresh window on the other side: for `vm.snapshot({ leaveRunning:
343
- * true })` and `vm.fork()` the source's workload sees `/mnt/<guest>/`
344
- * disappear for the dump duration (typically seconds, scales with
345
- * memory size) before reappearing under fresh server state. Open
346
- * fds across that window see EBADF on next syscall — same shape
347
- * as "don't snapshot during a database write." Workloads that
348
- * quiesce before snapshot are unaffected.
358
+ * writes (in `"rw"`) land on the host immediately. The in-VMM
359
+ * virtio-fs device persists across the CRIU dump, so the workload's
360
+ * view of `/mnt/<guest>/` survives `vm.snapshot({ leaveRunning:
361
+ * true })` and `vm.fork()` without an unmount/remount window.
349
362
  *
350
363
  * Concurrent writes from multiple forks against the same host
351
364
  * directory are no different from any other shared filesystem —
352
- * the runtime re-establishes the window per-VM but doesn't
353
- * coordinate writes between siblings. If two forks need
354
- * non-overlapping write surfaces, point each at a distinct
355
- * `host` path or use `mount` (copy-once, per-VM upper).
365
+ * each VM gets its own device but the runtime doesn't coordinate
366
+ * writes between siblings. If two forks need non-overlapping write
367
+ * surfaces, point each at a distinct `host` path or use `mount`
368
+ * (copy-once, per-VM upper).
356
369
  *
357
370
  * Restore on a host where the recorded `host` path doesn't exist:
358
371
  * fails loudly via `BOOT_MOUNT_HOST_NOT_FOUND`. Pass
@@ -476,23 +489,18 @@ interface BootOptions {
476
489
  declare function boot(opts?: BootOptions): Promise<VmHandle>;
477
490
 
478
491
  /**
479
- * A caller-provided `liveMounts` entry after validation, with the
480
- * vsock port + host UDS path allocated. Threaded from `boot()` into
481
- * the initramfs packer so the config and the host servers agree on
482
- * ports and guest paths.
492
+ * A caller-provided `liveMounts` entry after validation. Served by an
493
+ * in-VMM virtio-fs device (#332) no detached process, no vsock port,
494
+ * no guest fuse-agent. `tag` is the device's config-space identifier;
495
+ * `/init` runs `mount -t virtiofs <tag> <guest>`. Threaded from
496
+ * `boot()` into the initramfs packer so the config and the VMM env
497
+ * agree on guest paths and per-mount tags.
483
498
  */
484
499
  interface ResolvedLiveMount {
485
500
  host: string;
486
501
  guest: string;
487
- port: number;
488
- udsPath: string;
489
- /**
490
- * Per-mount stats file the detached helper writes its
491
- * bytesServedOnPagesImg counter to. Lives next to `udsPath` under
492
- * `vsockTempDir` so the supervisor's cleanupPaths sweep covers it.
493
- */
494
- statsPath: string;
495
502
  mode: "ro" | "rw";
503
+ tag: string;
496
504
  }
497
505
  /**
498
506
  * Build the synthesized `machinen-config.json` payload that /init
@@ -524,9 +532,12 @@ declare function validateMemoryMib(mib: number): number;
524
532
  /**
525
533
  * Locate the VMM binary using the same lookup order as `@machinen/cli`:
526
534
  * 1. `MACHINEN_VMM` env var (dev-mode override)
527
- * 2. `require.resolve("@machinen/vmm-<arch>-<os>")` → `binary` export
535
+ * 2. `require.resolve("@machinen/native-<arch>-<os>")` → `binary` export
528
536
  *
529
- * Callers can pass an explicit `binary` to `boot()` to bypass this.
537
+ * `@machinen/native-arm64-{darwin,linux}` is the consolidated host-tool
538
+ * package — it carries the VMM, gvproxy, guest ELFs, mke2fs,
539
+ * mksquashfs, and the mount server. Callers can pass an explicit
540
+ * `binary` to `boot()` to bypass this.
530
541
  *
531
542
  * @throws {BootError} BOOT_VMM_MISSING | BOOT_VMM_PACKAGE_BROKEN
532
543
  */
@@ -937,10 +948,20 @@ interface SnapshotOptions {
937
948
  tcpClose?: boolean;
938
949
  }
939
950
  interface SnapshotResult {
951
+ /** Which backend produced the bundle. */
952
+ engine: SnapshotEngine;
940
953
  /** Absolute path to the snapshot bundle directory. */
941
954
  snapDir: string;
942
- /** Absolute path to the CRIU image directory inside the bundle. */
943
- imgDir: string;
955
+ /**
956
+ * Absolute path to the CRIU image directory inside the bundle.
957
+ * Set by the criu engine only; undefined for vmstate bundles.
958
+ */
959
+ imgDir?: string;
960
+ /**
961
+ * Absolute path to the `.vmstate` whole-VM state file inside the
962
+ * bundle. Set by the vmstate engine only; undefined for criu bundles.
963
+ */
964
+ vmstatePath?: string;
944
965
  /** Time from `snapshot()` entry to VMM exit, in milliseconds. */
945
966
  elapsedMs: number;
946
967
  /** Guest console output captured during the dump. */
@@ -951,6 +972,14 @@ interface SnapshotResult {
951
972
  * to reconstruct the source VM's name when registering the fork.
952
973
  */
953
974
  interface SnapshotMeta {
975
+ /**
976
+ * Which backend wrote this bundle — `"criu"` (process-tree images
977
+ * under `img/`) or `"vmstate"` (whole-VM `state.vmstate`). `restore()`
978
+ * also auto-detects from the bundle's contents; this field is the
979
+ * explicit record. Absent on bundles predating the vmstate engine
980
+ * (treated as `"criu"`).
981
+ */
982
+ engine?: SnapshotEngine;
954
983
  /** Name passed to `boot({ name })` when the source VM was started. */
955
984
  sourceName?: string;
956
985
  /**
@@ -979,20 +1008,20 @@ interface SnapshotMeta {
979
1008
  upper: string;
980
1009
  };
981
1010
  /**
982
- * #273: live-share FUSE mounts (`liveMounts: [...]` at boot) the
983
- * source VM had at snapshot time. Unlike `mountDisk`, no bytes are
984
- * captured — `host` is the path on the host that was being live-
985
- * shared, recorded so `restore()` can re-establish the same window
986
- * on the restoring host. Each entry is the resolved config from the
1011
+ * #273: live-share mounts (`liveMounts: [...]` at boot) the source
1012
+ * VM had at snapshot time. Unlike `mountDisk`, no bytes are captured
1013
+ * — `host` is the path on the host that was being live-shared,
1014
+ * recorded so `restore()` can re-establish the same window on the
1015
+ * restoring host. Each entry is the resolved config from the
987
1016
  * source's `resolveLiveMounts()`:
988
- * - `guest`: absolute guest path the FUSE mount lands at.
1017
+ * - `guest`: absolute guest path the mount lands at.
989
1018
  * - `host`: absolute host path that was being shared.
990
1019
  * - `mode`: `"ro"` or `"rw"`, the share's write semantics.
991
1020
  *
992
1021
  * Restore policy: the bundle's recorded mounts are re-established
993
1022
  * verbatim by default. Pass `restore({ liveMounts })` to override
994
- * per-guest `host`/`mode` — each override entry's `guest` must
995
- * match a recorded entry, else BOOT_LIVE_MOUNT_OVERRIDE_UNKNOWN.
1023
+ * per-guest `host`/`mode` — each override entry's `guest` must match
1024
+ * a recorded entry, else BOOT_LIVE_MOUNT_OVERRIDE_UNKNOWN.
996
1025
  * Cross-host bundles where a recorded `host` doesn't exist on the
997
1026
  * restoring host fail loudly via the boot-time existence check —
998
1027
  * users remap with the override knob.
@@ -1345,7 +1374,7 @@ interface ProvisionOptions {
1345
1374
  env?: Record<string, string>;
1346
1375
  /**
1347
1376
  * Optional VMM binary path. Same lookup rules as `boot()` — if
1348
- * omitted, resolves `@machinen/vmm-<arch>-<os>`.
1377
+ * omitted, resolves `@machinen/native-<arch>-<os>`.
1349
1378
  */
1350
1379
  binary?: string;
1351
1380
  /** Working directory. Defaults to process.cwd(). */
@@ -1721,6 +1750,14 @@ interface RegistryEntry {
1721
1750
  * guest-side scratch disk that backs the in-VM dump.
1722
1751
  */
1723
1752
  diskPath?: string;
1753
+ /**
1754
+ * Vmstate engine only: absolute path the VMM writes its `.vmstate`
1755
+ * whole-VM state file to (the `MACHINEN_SNAPSHOT_PATH` it booted
1756
+ * with). Persisted so an attach-owned `vm.snapshot()` / `vm.fork()`
1757
+ * can SIGUSR1 the VMM and pick the state file up. Undefined for VMs
1758
+ * booted without the vmstate engine.
1759
+ */
1760
+ vmstatePath?: string;
1724
1761
  /**
1725
1762
  * Absolute path to the snapshot directory this VM was forked from
1726
1763
  * (set by `restore({ snapDir })`). Visible in `ls`; informational.
@@ -1804,14 +1841,6 @@ interface RegistryEntry {
1804
1841
  * `vm.memoryStats().lazyPagesPending`.
1805
1842
  */
1806
1843
  lazyPagesTotal?: number;
1807
- /**
1808
- * Absolute path under which the lazy-restore FUSE mount serves
1809
- * `pages-*.img` reads. The mount-server tracks bytes served below
1810
- * this prefix; `vm.memoryStats()` divides that by 4096 and
1811
- * subtracts from `lazyPagesTotal` to derive `lazyPagesPending`.
1812
- * Undefined when the VM wasn't lazy-restored.
1813
- */
1814
- lazyPagesMountRoot?: string;
1815
1844
  /**
1816
1845
  * #272: when the VM was booted with `mount: { host, guest }`, the
1817
1846
  * runtime materialized a squashfs RO lower + ext4 RW upper. Persist
@@ -1828,35 +1857,19 @@ interface RegistryEntry {
1828
1857
  upperPath: string;
1829
1858
  };
1830
1859
  /**
1831
- * #273: live-share FUSE mounts (`liveMounts: [...]` at boot) the
1832
- * VM was started with. Persisted so an attach-owned `vm.snapshot()`
1833
- * / `vm.fork()` can record the same `meta.liveMounts` block in the
1834
- * bundle and trigger /sbin/machinen-remount post-dump on
1835
- * leaveRunning paths. Host UDS paths and vsock ports are NOT
1836
- * recorded those are the boot process's private state and aren't
1837
- * useful to other processes (the owning process keeps the servers
1838
- * listening through the dump, so attach reconnects without having
1839
- * to bind anything).
1860
+ * #273: live-share mounts (`liveMounts: [...]` at boot) the VM was
1861
+ * started with. Persisted so an attach-owned `vm.snapshot()` /
1862
+ * `vm.fork()` can record the same `meta.liveMounts` block in the
1863
+ * bundle. Since #332 every live mount is served by an in-VMM
1864
+ * virtio-fs device there's no host-side process to record, reap,
1865
+ * or reconnect to. The per-mount virtio-fs tag isn't recorded
1866
+ * either; it's re-derived from the resolved order on restore.
1840
1867
  */
1841
1868
  liveMounts?: Array<{
1842
1869
  guest: string;
1843
1870
  host: string;
1844
1871
  mode: "ro" | "rw";
1845
1872
  }>;
1846
- /**
1847
- * #150 phase 3: pids + exes of the detached mount-server helpers
1848
- * spawned alongside this VMM, one per live-mount. The helpers die
1849
- * with the VMM via `pdeathsig --watch-pid` already, but `machinen
1850
- * stop` SIGTERMs them up-front so the VMM exit hook doesn't race
1851
- * with the helper's own pdeathsig-driven shutdown, and `machinen
1852
- * gc` validates pid+exe to detect recycled pids the same way the
1853
- * VMM and gvproxy entries do. Empty / undefined for VMs booted
1854
- * without `liveMounts`.
1855
- */
1856
- liveMountServers?: Array<{
1857
- pid: number;
1858
- exe: string;
1859
- }>;
1860
1873
  /** ms epoch when the entry was created. */
1861
1874
  startedAt: number;
1862
1875
  }
@@ -1900,12 +1913,6 @@ interface PackBundleOptions {
1900
1913
  excludes?: string[];
1901
1914
  /** Optional path to the compiled /init. Default: ../microvm/test-fixtures/init relative to this file. */
1902
1915
  initPath?: string;
1903
- /**
1904
- * Optional host path to the compiled fuse-agent binary. When set,
1905
- * the binary is injected at `/fuse-agent` (mode 0755) inside the
1906
- * initramfs so /init can fork it per live-share mount. See #78.
1907
- */
1908
- fuseAgentPath?: string;
1909
1916
  /**
1910
1917
  * Optional path to the compiled /exec-agent. Default: same dir as
1911
1918
  * /init under packages/microvm/test-fixtures/. Used to override the
@@ -1931,8 +1938,6 @@ interface PackTinyBundleOptions {
1931
1938
  mountGuest?: string;
1932
1939
  /** Optional override for the compiled /init. Default: ../microvm/test-fixtures/init relative to this file. */
1933
1940
  initPath?: string;
1934
- /** Optional path to the compiled fuse-agent; staged at /fuse-agent when set. */
1935
- fuseAgentPath?: string;
1936
1941
  }
1937
1942
  /**
1938
1943
  * Build the tiny initramfs used by every user-facing boot() (#119).
@@ -1947,7 +1952,6 @@ interface PackTinyBundleOptions {
1947
1952
  * blk slots 5+6, not in the cpio.
1948
1953
  * /dev/console char node 5,1 — kernel needs it
1949
1954
  * before /init re-opens the console
1950
- * /fuse-agent optional, only when liveMounts
1951
1955
  * /tmp sticky 1777
1952
1956
  *
1953
1957
  * No /lib/modules tree, no kmod, no /modules/*.ko, no Debian userland.
@@ -2032,8 +2036,6 @@ declare const ErrorCode: {
2032
2036
  readonly FILES_AGENT_UNAVAILABLE: "FILES_AGENT_UNAVAILABLE";
2033
2037
  readonly MOUNT_PATH_INVALID: "MOUNT_PATH_INVALID";
2034
2038
  readonly MOUNT_PATH_ESCAPE: "MOUNT_PATH_ESCAPE";
2035
- readonly MOUNT_SERVER_BIN_MISSING: "MOUNT_SERVER_BIN_MISSING";
2036
- readonly MOUNT_SERVER_SPAWN_FAILED: "MOUNT_SERVER_SPAWN_FAILED";
2037
2039
  readonly SECRETS_VALUE_INVALID: "SECRETS_VALUE_INVALID";
2038
2040
  readonly SECRETS_AGENT_UNAVAILABLE: "SECRETS_AGENT_UNAVAILABLE";
2039
2041
  readonly WINSIZE_AGENT_UNAVAILABLE: "WINSIZE_AGENT_UNAVAILABLE";
@@ -2231,4 +2233,4 @@ interface BalloonCounters {
2231
2233
  */
2232
2234
  declare function readBalloonStats(path: string): BalloonCounters | null;
2233
2235
 
2234
- export { type AttachOptions, type BalloonCounters, BootError, type BootOptions, CacheError, type CheckForkBackpressureOptions, type ChunkLogEvent, DEFAULT_FREE_MEMORY_THRESHOLD, type EnsureMountDiskImageOptions, type EnsureMountDiskImageResult, type EnsureMountDiskUpperOptions, type EnsureMountDiskUpperResult, type EnsureRootfsImageOptions, ErrorCode, ExecError, FilesError, type ForkOptions, type GcResult, GvproxyError, type ImageConfig, type LogEvent, MachinenError, type MachinenErrorOptions, type MemoryStats, MkinitramfsError, MountError, type OnLog, type OnOutputListener, type PackBundleOptions, type PackMinimalOptions, type PackRootfsOptions, type PackTinyBundleOptions, type PackWorkspaceOptions, ParseError, type PhaseLogEvent, type PidStatus, ProvisionError, type ProvisionOptions, type ProvisionResult, type PtyBootOptions, type PtyVmHandle, type RegistryEntry, RegistryError, type RestoreOptions, type RssTarget, type RunGcOptions, STATS_FILE_SIZE, type SandboxEntry, SandboxError, Sandboxes, SecretsError, SnapshotError, type SnapshotMeta, type SnapshotOptions, type SnapshotResult, Supervisor, type SupervisorOptions, type VmHandle, VsockExec, type VsockExecOptions, type VsockExecPtyHandle, type VsockExecPtyOptions, type VsockExecPtyResult, type VsockExecResult, VsockFiles, type VsockFilesOptions, VsockSecrets, type VsockSecretsOptions, VsockWinsize, type VsockWinsizeOptions, WinsizeError, type WriteFileOptions, _internal, attach, autoSizeMemoryMib, boot, bootPty, bootSnapshotPath, buildMachinenConfig, buildWriteFileCmd, buildWriteFileCmds, checkForkBackpressure, detachedLogRoot, ensureMountDiskImage, ensureMountDiskUpper, ensureRootfsImage, formatMachinenError, isMachinenError, list, markMountDiskImageClean, markRootfsImageClean, measureFirstByte, packBundle as mkinitramfsBundle, cli as mkinitramfsCli, packMinimal as mkinitramfsMinimal, packRootfs as mkinitramfsRootfs, packTinyBundle as mkinitramfsTinyBundle, packWorkspace as mkinitramfsWorkspace, mountdiskImgCacheDir, provision, readBalloonStats, readHostFreeBytes, readHostRssBytes, readHostRssBytesMulti, readHostTotalBytes, registryRoot, resolveBaseDtb, resolveBaseKernel, resolveBaseRootfs, resolveMke2fs, resolveMksquashfs, resolveVmmBinary, restore, rootfsImgCacheDir, runGc, validatePid, warmImageConfigCache, writeBootSnapshot };
2236
+ export { type AttachOptions, type BalloonCounters, BootError, type BootOptions, CacheError, type CheckForkBackpressureOptions, type ChunkLogEvent, DEFAULT_FREE_MEMORY_THRESHOLD, type EnsureMountDiskImageOptions, type EnsureMountDiskImageResult, type EnsureMountDiskUpperOptions, type EnsureMountDiskUpperResult, type EnsureRootfsImageOptions, ErrorCode, ExecError, FilesError, type ForkOptions, type GcResult, GvproxyError, type ImageConfig, type LogEvent, MachinenError, type MachinenErrorOptions, type MemoryStats, MkinitramfsError, MountError, type OnLog, type OnOutputListener, type PackBundleOptions, type PackMinimalOptions, type PackRootfsOptions, type PackTinyBundleOptions, type PackWorkspaceOptions, ParseError, type PhaseLogEvent, type PidStatus, ProvisionError, type ProvisionOptions, type ProvisionResult, type PtyBootOptions, type PtyVmHandle, type RegistryEntry, RegistryError, type RestoreOptions, type RssTarget, type RunGcOptions, STATS_FILE_SIZE, type SandboxEntry, SandboxError, Sandboxes, SecretsError, type SnapshotEngine, SnapshotError, type SnapshotMeta, type SnapshotOptions, type SnapshotResult, Supervisor, type SupervisorOptions, type VmHandle, VsockExec, type VsockExecOptions, type VsockExecPtyHandle, type VsockExecPtyOptions, type VsockExecPtyResult, type VsockExecResult, VsockFiles, type VsockFilesOptions, VsockSecrets, type VsockSecretsOptions, VsockWinsize, type VsockWinsizeOptions, WinsizeError, type WriteFileOptions, _internal, attach, autoSizeMemoryMib, boot, bootPty, bootSnapshotPath, buildMachinenConfig, buildWriteFileCmd, buildWriteFileCmds, checkForkBackpressure, detachedLogRoot, ensureMountDiskImage, ensureMountDiskUpper, ensureRootfsImage, formatMachinenError, isMachinenError, list, markMountDiskImageClean, markRootfsImageClean, measureFirstByte, packBundle as mkinitramfsBundle, cli as mkinitramfsCli, packMinimal as mkinitramfsMinimal, packRootfs as mkinitramfsRootfs, packTinyBundle as mkinitramfsTinyBundle, packWorkspace as mkinitramfsWorkspace, mountdiskImgCacheDir, provision, readBalloonStats, readHostFreeBytes, readHostRssBytes, readHostRssBytesMulti, readHostTotalBytes, registryRoot, resolveBaseDtb, resolveBaseKernel, resolveBaseRootfs, resolveMke2fs, resolveMksquashfs, resolveVmmBinary, restore, rootfsImgCacheDir, runGc, validatePid, warmImageConfigCache, writeBootSnapshot };