@machinen/runtime 0.1.2 → 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
@@ -19,7 +19,14 @@ interface VsockExecOptions {
19
19
  }
20
20
  interface VsockExecResult {
21
21
  exitCode: number;
22
+ /**
23
+ * Concatenated stdout bytes, decoded as UTF-8. Always `""` when the
24
+ * caller passed `onStdout` — streaming callers already have the
25
+ * bytes and a parallel buffered copy would defeat the streaming
26
+ * (and at multi-GB volumes would crash with ERR_STRING_TOO_LONG).
27
+ */
22
28
  stdout: string;
29
+ /** Same shape as `stdout` for the stderr channel + `onStderr`. */
23
30
  stderr: string;
24
31
  }
25
32
  declare const VsockExec: {
@@ -121,6 +128,8 @@ interface PhaseLogEvent {
121
128
  type LogEvent = ChunkLogEvent | PhaseLogEvent;
122
129
  type OnLog = (evt: LogEvent) => void;
123
130
 
131
+ type SnapshotEngine = "criu" | "vmstate";
132
+
124
133
  interface AttachOptions {
125
134
  /**
126
135
  * Look up a VM by the host pid of its VMM process. Kernel-unique
@@ -318,34 +327,45 @@ interface BootOptions {
318
327
  upperPath: string;
319
328
  };
320
329
  /**
321
- * Host directories exposed to the guest as live-share FUSE mounts
322
- * (#78). Unlike `mount` (copy-once into the boot rootfs), these stay
323
- * connected to the host: the guest reads on demand via a vsock FUSE
324
- * relay, and nothing is copied at boot. `mode` defaults to `"rw"`
325
- * guest writes land on the host (#151, #156). Set `"ro"` for a
326
- * 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).
327
346
  *
328
347
  * Each guest path must live under `/mnt/` (same rule as `mount`).
329
- * 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.)
330
355
  *
331
356
  * Snapshot / restore / fork (#273): liveMount has no guest-side
332
357
  * state worth checkpointing — reads come from the host on demand,
333
- * writes (in `"rw"`) land on the host immediately. The runtime
334
- * unmounts each mount before CRIU dumps, then re-establishes a
335
- * fresh window on the other side: for `vm.snapshot({ leaveRunning:
336
- * true })` and `vm.fork()` the source's workload sees `/mnt/<guest>/`
337
- * disappear for the dump duration (typically seconds, scales with
338
- * memory size) before reappearing under fresh server state. Open
339
- * fds across that window see EBADF on next syscall — same shape
340
- * as "don't snapshot during a database write." Workloads that
341
- * 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.
342
362
  *
343
363
  * Concurrent writes from multiple forks against the same host
344
364
  * directory are no different from any other shared filesystem —
345
- * the runtime re-establishes the window per-VM but doesn't
346
- * coordinate writes between siblings. If two forks need
347
- * non-overlapping write surfaces, point each at a distinct
348
- * `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).
349
369
  *
350
370
  * Restore on a host where the recorded `host` path doesn't exist:
351
371
  * fails loudly via `BOOT_MOUNT_HOST_NOT_FOUND`. Pass
@@ -469,23 +489,18 @@ interface BootOptions {
469
489
  declare function boot(opts?: BootOptions): Promise<VmHandle>;
470
490
 
471
491
  /**
472
- * A caller-provided `liveMounts` entry after validation, with the
473
- * vsock port + host UDS path allocated. Threaded from `boot()` into
474
- * the initramfs packer so the config and the host servers agree on
475
- * 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.
476
498
  */
477
499
  interface ResolvedLiveMount {
478
500
  host: string;
479
501
  guest: string;
480
- port: number;
481
- udsPath: string;
482
- /**
483
- * Per-mount stats file the detached helper writes its
484
- * bytesServedOnPagesImg counter to. Lives next to `udsPath` under
485
- * `vsockTempDir` so the supervisor's cleanupPaths sweep covers it.
486
- */
487
- statsPath: string;
488
502
  mode: "ro" | "rw";
503
+ tag: string;
489
504
  }
490
505
  /**
491
506
  * Build the synthesized `machinen-config.json` payload that /init
@@ -517,9 +532,12 @@ declare function validateMemoryMib(mib: number): number;
517
532
  /**
518
533
  * Locate the VMM binary using the same lookup order as `@machinen/cli`:
519
534
  * 1. `MACHINEN_VMM` env var (dev-mode override)
520
- * 2. `require.resolve("@machinen/vmm-<arch>-<os>")` → `binary` export
535
+ * 2. `require.resolve("@machinen/native-<arch>-<os>")` → `binary` export
521
536
  *
522
- * 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.
523
541
  *
524
542
  * @throws {BootError} BOOT_VMM_MISSING | BOOT_VMM_PACKAGE_BROKEN
525
543
  */
@@ -930,10 +948,20 @@ interface SnapshotOptions {
930
948
  tcpClose?: boolean;
931
949
  }
932
950
  interface SnapshotResult {
951
+ /** Which backend produced the bundle. */
952
+ engine: SnapshotEngine;
933
953
  /** Absolute path to the snapshot bundle directory. */
934
954
  snapDir: string;
935
- /** Absolute path to the CRIU image directory inside the bundle. */
936
- 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;
937
965
  /** Time from `snapshot()` entry to VMM exit, in milliseconds. */
938
966
  elapsedMs: number;
939
967
  /** Guest console output captured during the dump. */
@@ -944,6 +972,14 @@ interface SnapshotResult {
944
972
  * to reconstruct the source VM's name when registering the fork.
945
973
  */
946
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;
947
983
  /** Name passed to `boot({ name })` when the source VM was started. */
948
984
  sourceName?: string;
949
985
  /**
@@ -972,20 +1008,20 @@ interface SnapshotMeta {
972
1008
  upper: string;
973
1009
  };
974
1010
  /**
975
- * #273: live-share FUSE mounts (`liveMounts: [...]` at boot) the
976
- * source VM had at snapshot time. Unlike `mountDisk`, no bytes are
977
- * captured — `host` is the path on the host that was being live-
978
- * shared, recorded so `restore()` can re-establish the same window
979
- * 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
980
1016
  * source's `resolveLiveMounts()`:
981
- * - `guest`: absolute guest path the FUSE mount lands at.
1017
+ * - `guest`: absolute guest path the mount lands at.
982
1018
  * - `host`: absolute host path that was being shared.
983
1019
  * - `mode`: `"ro"` or `"rw"`, the share's write semantics.
984
1020
  *
985
1021
  * Restore policy: the bundle's recorded mounts are re-established
986
1022
  * verbatim by default. Pass `restore({ liveMounts })` to override
987
- * per-guest `host`/`mode` — each override entry's `guest` must
988
- * 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.
989
1025
  * Cross-host bundles where a recorded `host` doesn't exist on the
990
1026
  * restoring host fail loudly via the boot-time existence check —
991
1027
  * users remap with the override knob.
@@ -1338,7 +1374,7 @@ interface ProvisionOptions {
1338
1374
  env?: Record<string, string>;
1339
1375
  /**
1340
1376
  * Optional VMM binary path. Same lookup rules as `boot()` — if
1341
- * omitted, resolves `@machinen/vmm-<arch>-<os>`.
1377
+ * omitted, resolves `@machinen/native-<arch>-<os>`.
1342
1378
  */
1343
1379
  binary?: string;
1344
1380
  /** Working directory. Defaults to process.cwd(). */
@@ -1714,6 +1750,14 @@ interface RegistryEntry {
1714
1750
  * guest-side scratch disk that backs the in-VM dump.
1715
1751
  */
1716
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;
1717
1761
  /**
1718
1762
  * Absolute path to the snapshot directory this VM was forked from
1719
1763
  * (set by `restore({ snapDir })`). Visible in `ls`; informational.
@@ -1797,14 +1841,6 @@ interface RegistryEntry {
1797
1841
  * `vm.memoryStats().lazyPagesPending`.
1798
1842
  */
1799
1843
  lazyPagesTotal?: number;
1800
- /**
1801
- * Absolute path under which the lazy-restore FUSE mount serves
1802
- * `pages-*.img` reads. The mount-server tracks bytes served below
1803
- * this prefix; `vm.memoryStats()` divides that by 4096 and
1804
- * subtracts from `lazyPagesTotal` to derive `lazyPagesPending`.
1805
- * Undefined when the VM wasn't lazy-restored.
1806
- */
1807
- lazyPagesMountRoot?: string;
1808
1844
  /**
1809
1845
  * #272: when the VM was booted with `mount: { host, guest }`, the
1810
1846
  * runtime materialized a squashfs RO lower + ext4 RW upper. Persist
@@ -1821,35 +1857,19 @@ interface RegistryEntry {
1821
1857
  upperPath: string;
1822
1858
  };
1823
1859
  /**
1824
- * #273: live-share FUSE mounts (`liveMounts: [...]` at boot) the
1825
- * VM was started with. Persisted so an attach-owned `vm.snapshot()`
1826
- * / `vm.fork()` can record the same `meta.liveMounts` block in the
1827
- * bundle and trigger /sbin/machinen-remount post-dump on
1828
- * leaveRunning paths. Host UDS paths and vsock ports are NOT
1829
- * recorded those are the boot process's private state and aren't
1830
- * useful to other processes (the owning process keeps the servers
1831
- * listening through the dump, so attach reconnects without having
1832
- * 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.
1833
1867
  */
1834
1868
  liveMounts?: Array<{
1835
1869
  guest: string;
1836
1870
  host: string;
1837
1871
  mode: "ro" | "rw";
1838
1872
  }>;
1839
- /**
1840
- * #150 phase 3: pids + exes of the detached mount-server helpers
1841
- * spawned alongside this VMM, one per live-mount. The helpers die
1842
- * with the VMM via `pdeathsig --watch-pid` already, but `machinen
1843
- * stop` SIGTERMs them up-front so the VMM exit hook doesn't race
1844
- * with the helper's own pdeathsig-driven shutdown, and `machinen
1845
- * gc` validates pid+exe to detect recycled pids the same way the
1846
- * VMM and gvproxy entries do. Empty / undefined for VMs booted
1847
- * without `liveMounts`.
1848
- */
1849
- liveMountServers?: Array<{
1850
- pid: number;
1851
- exe: string;
1852
- }>;
1853
1873
  /** ms epoch when the entry was created. */
1854
1874
  startedAt: number;
1855
1875
  }
@@ -1893,12 +1913,6 @@ interface PackBundleOptions {
1893
1913
  excludes?: string[];
1894
1914
  /** Optional path to the compiled /init. Default: ../microvm/test-fixtures/init relative to this file. */
1895
1915
  initPath?: string;
1896
- /**
1897
- * Optional host path to the compiled fuse-agent binary. When set,
1898
- * the binary is injected at `/fuse-agent` (mode 0755) inside the
1899
- * initramfs so /init can fork it per live-share mount. See #78.
1900
- */
1901
- fuseAgentPath?: string;
1902
1916
  /**
1903
1917
  * Optional path to the compiled /exec-agent. Default: same dir as
1904
1918
  * /init under packages/microvm/test-fixtures/. Used to override the
@@ -1924,8 +1938,6 @@ interface PackTinyBundleOptions {
1924
1938
  mountGuest?: string;
1925
1939
  /** Optional override for the compiled /init. Default: ../microvm/test-fixtures/init relative to this file. */
1926
1940
  initPath?: string;
1927
- /** Optional path to the compiled fuse-agent; staged at /fuse-agent when set. */
1928
- fuseAgentPath?: string;
1929
1941
  }
1930
1942
  /**
1931
1943
  * Build the tiny initramfs used by every user-facing boot() (#119).
@@ -1940,7 +1952,6 @@ interface PackTinyBundleOptions {
1940
1952
  * blk slots 5+6, not in the cpio.
1941
1953
  * /dev/console char node 5,1 — kernel needs it
1942
1954
  * before /init re-opens the console
1943
- * /fuse-agent optional, only when liveMounts
1944
1955
  * /tmp sticky 1777
1945
1956
  *
1946
1957
  * No /lib/modules tree, no kmod, no /modules/*.ko, no Debian userland.
@@ -2025,8 +2036,6 @@ declare const ErrorCode: {
2025
2036
  readonly FILES_AGENT_UNAVAILABLE: "FILES_AGENT_UNAVAILABLE";
2026
2037
  readonly MOUNT_PATH_INVALID: "MOUNT_PATH_INVALID";
2027
2038
  readonly MOUNT_PATH_ESCAPE: "MOUNT_PATH_ESCAPE";
2028
- readonly MOUNT_SERVER_BIN_MISSING: "MOUNT_SERVER_BIN_MISSING";
2029
- readonly MOUNT_SERVER_SPAWN_FAILED: "MOUNT_SERVER_SPAWN_FAILED";
2030
2039
  readonly SECRETS_VALUE_INVALID: "SECRETS_VALUE_INVALID";
2031
2040
  readonly SECRETS_AGENT_UNAVAILABLE: "SECRETS_AGENT_UNAVAILABLE";
2032
2041
  readonly WINSIZE_AGENT_UNAVAILABLE: "WINSIZE_AGENT_UNAVAILABLE";
@@ -2224,4 +2233,4 @@ interface BalloonCounters {
2224
2233
  */
2225
2234
  declare function readBalloonStats(path: string): BalloonCounters | null;
2226
2235
 
2227
- 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 };