@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/API.md CHANGED
@@ -35,6 +35,7 @@
35
35
  - [`SnapshotOptions`](#snapshotoptions)
36
36
  - [`SnapshotResult`](#snapshotresult)
37
37
  - [`SnapshotMeta`](#snapshotmeta)
38
+ - [`SnapshotEngine`](#snapshotengine)
38
39
  - [`bootSnapshotPath`](#bootsnapshotpath)
39
40
  - [`writeBootSnapshot`](#writebootsnapshot)
40
41
  - [`detachedLogRoot`](#detachedlogroot)
@@ -1778,14 +1779,6 @@ fnmatch patterns matched against each rootfs-relative path.
1778
1779
 
1779
1780
  Optional path to the compiled /init. Default: ../microvm/test-fixtures/init relative to this file.
1780
1781
 
1781
- ##### fuseAgentPath?
1782
-
1783
- > `optional` **fuseAgentPath?**: `string`
1784
-
1785
- Optional host path to the compiled fuse-agent binary. When set,
1786
- the binary is injected at `/fuse-agent` (mode 0755) inside the
1787
- initramfs so /init can fork it per live-share mount. See #78.
1788
-
1789
1782
  ##### execAgentPath?
1790
1783
 
1791
1784
  > `optional` **execAgentPath?**: `string`
@@ -1834,12 +1827,6 @@ not in the cpio. Must be an absolute path under `/mnt/`.
1834
1827
 
1835
1828
  Optional override for the compiled /init. Default: ../microvm/test-fixtures/init relative to this file.
1836
1829
 
1837
- ##### fuseAgentPath?
1838
-
1839
- > `optional` **fuseAgentPath?**: `string`
1840
-
1841
- Optional path to the compiled fuse-agent; staged at /fuse-agent when set.
1842
-
1843
1830
  ***
1844
1831
 
1845
1832
  ### PackRootfsOptions
@@ -2180,7 +2167,7 @@ overriding on key collision.
2180
2167
  > `optional` **binary?**: `string`
2181
2168
 
2182
2169
  Optional VMM binary path. Same lookup rules as `boot()` — if
2183
- omitted, resolves `@machinen/vmm-<arch>-<os>`.
2170
+ omitted, resolves `@machinen/native-<arch>-<os>`.
2184
2171
 
2185
2172
  ##### cwd?
2186
2173
 
@@ -2421,6 +2408,16 @@ Host-side path of the scratch disk attached to the guest. Used by
2421
2408
  `attach().snapshot()` so an attach-owned handle can find the
2422
2409
  guest-side scratch disk that backs the in-VM dump.
2423
2410
 
2411
+ ##### vmstatePath?
2412
+
2413
+ > `optional` **vmstatePath?**: `string`
2414
+
2415
+ Vmstate engine only: absolute path the VMM writes its `.vmstate`
2416
+ whole-VM state file to (the `MACHINEN_SNAPSHOT_PATH` it booted
2417
+ with). Persisted so an attach-owned `vm.snapshot()` / `vm.fork()`
2418
+ can SIGUSR1 the VMM and pick the state file up. Undefined for VMs
2419
+ booted without the vmstate engine.
2420
+
2424
2421
  ##### forkedFrom?
2425
2422
 
2426
2423
  > `optional` **forkedFrom?**: `string`
@@ -2532,16 +2529,6 @@ the VM was restored. Set on restore-derived entries, undefined
2532
2529
  for plain boots and eager restores. Surfaced via
2533
2530
  `vm.memoryStats().lazyPagesPending`.
2534
2531
 
2535
- ##### lazyPagesMountRoot?
2536
-
2537
- > `optional` **lazyPagesMountRoot?**: `string`
2538
-
2539
- Absolute path under which the lazy-restore FUSE mount serves
2540
- `pages-*.img` reads. The mount-server tracks bytes served below
2541
- this prefix; `vm.memoryStats()` divides that by 4096 and
2542
- subtracts from `lazyPagesTotal` to derive `lazyPagesPending`.
2543
- Undefined when the VM wasn't lazy-restored.
2544
-
2545
2532
  ##### mountDisk?
2546
2533
 
2547
2534
  > `optional` **mountDisk?**: `object`
@@ -2571,15 +2558,13 @@ silently boots without the overlay.
2571
2558
 
2572
2559
  > `optional` **liveMounts?**: `object`[]
2573
2560
 
2574
- #273: live-share FUSE mounts (`liveMounts: [...]` at boot) the
2575
- VM was started with. Persisted so an attach-owned `vm.snapshot()`
2576
- / `vm.fork()` can record the same `meta.liveMounts` block in the
2577
- bundle and trigger /sbin/machinen-remount post-dump on
2578
- leaveRunning paths. Host UDS paths and vsock ports are NOT
2579
- recorded those are the boot process's private state and aren't
2580
- useful to other processes (the owning process keeps the servers
2581
- listening through the dump, so attach reconnects without having
2582
- to bind anything).
2561
+ #273: live-share mounts (`liveMounts: [...]` at boot) the VM was
2562
+ started with. Persisted so an attach-owned `vm.snapshot()` /
2563
+ `vm.fork()` can record the same `meta.liveMounts` block in the
2564
+ bundle. Since #332 every live mount is served by an in-VMM
2565
+ virtio-fs device there's no host-side process to record, reap,
2566
+ or reconnect to. The per-mount virtio-fs tag isn't recorded
2567
+ either; it's re-derived from the resolved order on restore.
2583
2568
 
2584
2569
  ###### guest
2585
2570
 
@@ -2593,27 +2578,6 @@ to bind anything).
2593
2578
 
2594
2579
  > **mode**: `"ro"` \| `"rw"`
2595
2580
 
2596
- ##### liveMountServers?
2597
-
2598
- > `optional` **liveMountServers?**: `object`[]
2599
-
2600
- #150 phase 3: pids + exes of the detached mount-server helpers
2601
- spawned alongside this VMM, one per live-mount. The helpers die
2602
- with the VMM via `pdeathsig --watch-pid` already, but `machinen
2603
- stop` SIGTERMs them up-front so the VMM exit hook doesn't race
2604
- with the helper's own pdeathsig-driven shutdown, and `machinen
2605
- gc` validates pid+exe to detect recycled pids the same way the
2606
- VMM and gvproxy entries do. Empty / undefined for VMs booted
2607
- without `liveMounts`.
2608
-
2609
- ###### pid
2610
-
2611
- > **pid**: `number`
2612
-
2613
- ###### exe
2614
-
2615
- > **exe**: `string`
2616
-
2617
2581
  ##### startedAt
2618
2582
 
2619
2583
  > **startedAt**: `number`
@@ -3193,17 +3157,31 @@ Default: false (preserve TCP — current snapshot/restore behavior).
3193
3157
 
3194
3158
  #### Properties
3195
3159
 
3160
+ ##### engine
3161
+
3162
+ > **engine**: [`SnapshotEngine`](#snapshotengine)
3163
+
3164
+ Which backend produced the bundle.
3165
+
3196
3166
  ##### snapDir
3197
3167
 
3198
3168
  > **snapDir**: `string`
3199
3169
 
3200
3170
  Absolute path to the snapshot bundle directory.
3201
3171
 
3202
- ##### imgDir
3172
+ ##### imgDir?
3203
3173
 
3204
- > **imgDir**: `string`
3174
+ > `optional` **imgDir?**: `string`
3205
3175
 
3206
3176
  Absolute path to the CRIU image directory inside the bundle.
3177
+ Set by the criu engine only; undefined for vmstate bundles.
3178
+
3179
+ ##### vmstatePath?
3180
+
3181
+ > `optional` **vmstatePath?**: `string`
3182
+
3183
+ Absolute path to the `.vmstate` whole-VM state file inside the
3184
+ bundle. Set by the vmstate engine only; undefined for criu bundles.
3207
3185
 
3208
3186
  ##### elapsedMs
3209
3187
 
@@ -3226,6 +3204,16 @@ to reconstruct the source VM's name when registering the fork.
3226
3204
 
3227
3205
  #### Properties
3228
3206
 
3207
+ ##### engine?
3208
+
3209
+ > `optional` **engine?**: [`SnapshotEngine`](#snapshotengine)
3210
+
3211
+ Which backend wrote this bundle — `"criu"` (process-tree images
3212
+ under `img/`) or `"vmstate"` (whole-VM `state.vmstate`). `restore()`
3213
+ also auto-detects from the bundle's contents; this field is the
3214
+ explicit record. Absent on bundles predating the vmstate engine
3215
+ (treated as `"criu"`).
3216
+
3229
3217
  ##### sourceName?
3230
3218
 
3231
3219
  > `optional` **sourceName?**: `string`
@@ -3277,20 +3265,20 @@ consulting the host source dir.
3277
3265
 
3278
3266
  > `optional` **liveMounts?**: `object`[]
3279
3267
 
3280
- #273: live-share FUSE mounts (`liveMounts: [...]` at boot) the
3281
- source VM had at snapshot time. Unlike `mountDisk`, no bytes are
3282
- captured — `host` is the path on the host that was being live-
3283
- shared, recorded so `restore()` can re-establish the same window
3284
- on the restoring host. Each entry is the resolved config from the
3268
+ #273: live-share mounts (`liveMounts: [...]` at boot) the source
3269
+ VM had at snapshot time. Unlike `mountDisk`, no bytes are captured
3270
+ — `host` is the path on the host that was being live-shared,
3271
+ recorded so `restore()` can re-establish the same window on the
3272
+ restoring host. Each entry is the resolved config from the
3285
3273
  source's `resolveLiveMounts()`:
3286
- - `guest`: absolute guest path the FUSE mount lands at.
3274
+ - `guest`: absolute guest path the mount lands at.
3287
3275
  - `host`: absolute host path that was being shared.
3288
3276
  - `mode`: `"ro"` or `"rw"`, the share's write semantics.
3289
3277
 
3290
3278
  Restore policy: the bundle's recorded mounts are re-established
3291
3279
  verbatim by default. Pass `restore({ liveMounts })` to override
3292
- per-guest `host`/`mode` — each override entry's `guest` must
3293
- match a recorded entry, else BOOT_LIVE_MOUNT_OVERRIDE_UNKNOWN.
3280
+ per-guest `host`/`mode` — each override entry's `guest` must match
3281
+ a recorded entry, else BOOT_LIVE_MOUNT_OVERRIDE_UNKNOWN.
3294
3282
  Cross-host bundles where a recorded `host` doesn't exist on the
3295
3283
  restoring host fail loudly via the boot-time existence check —
3296
3284
  users remap with the override knob.
@@ -3602,34 +3590,35 @@ Must be a positive multiple of 4096. Default 4 GiB.
3602
3590
 
3603
3591
  > `optional` **liveMounts?**: `object`[]
3604
3592
 
3605
- Host directories exposed to the guest as live-share FUSE mounts
3606
- (#78). Unlike `mount` (copy-once into the boot rootfs), these stay
3607
- connected to the host: the guest reads on demand via a vsock FUSE
3608
- relay, and nothing is copied at boot. `mode` defaults to `"rw"` —
3609
- guest writes land on the host (#151, #156). Set `"ro"` for a
3610
- one-way share (host caches, untrusted guests).
3593
+ Host directories exposed to the guest as live-share mounts (#78,
3594
+ #332). Unlike `mount` (copy-once into the boot rootfs), these stay
3595
+ connected to the host: the guest reads on demand and nothing is
3596
+ copied at boot. `mode` defaults to `"rw"` — guest writes land on
3597
+ the host (#151, #156). Set `"ro"` for a one-way share (host
3598
+ caches, untrusted guests).
3611
3599
 
3612
3600
  Each guest path must live under `/mnt/` (same rule as `mount`).
3613
- Repeatable; each entry gets its own vsock port.
3601
+ Repeatable up to 4 entries per VM — each is served by its own
3602
+ in-VMM virtio-fs device (the VMM wires 4 virtio-fs slots). The
3603
+ FUSE opcode handlers run inside the VMM and the guest mounts each
3604
+ share directly with `mount -t virtiofs` — no agent process, no
3605
+ vsock hop. Requires a guest kernel with `CONFIG_VIRTIO_FS` — every
3606
+ machinen-built kernel has it. (The older FUSE-over-vsock transport
3607
+ and its `protocol` knob were removed in #338.)
3614
3608
 
3615
3609
  Snapshot / restore / fork (#273): liveMount has no guest-side
3616
3610
  state worth checkpointing — reads come from the host on demand,
3617
- writes (in `"rw"`) land on the host immediately. The runtime
3618
- unmounts each mount before CRIU dumps, then re-establishes a
3619
- fresh window on the other side: for `vm.snapshot({ leaveRunning:
3620
- true })` and `vm.fork()` the source's workload sees `/mnt/<guest>/`
3621
- disappear for the dump duration (typically seconds, scales with
3622
- memory size) before reappearing under fresh server state. Open
3623
- fds across that window see EBADF on next syscall — same shape
3624
- as "don't snapshot during a database write." Workloads that
3625
- quiesce before snapshot are unaffected.
3611
+ writes (in `"rw"`) land on the host immediately. The in-VMM
3612
+ virtio-fs device persists across the CRIU dump, so the workload's
3613
+ view of `/mnt/<guest>/` survives `vm.snapshot({ leaveRunning:
3614
+ true })` and `vm.fork()` without an unmount/remount window.
3626
3615
 
3627
3616
  Concurrent writes from multiple forks against the same host
3628
3617
  directory are no different from any other shared filesystem —
3629
- the runtime re-establishes the window per-VM but doesn't
3630
- coordinate writes between siblings. If two forks need
3631
- non-overlapping write surfaces, point each at a distinct
3632
- `host` path or use `mount` (copy-once, per-VM upper).
3618
+ each VM gets its own device but the runtime doesn't coordinate
3619
+ writes between siblings. If two forks need non-overlapping write
3620
+ surfaces, point each at a distinct `host` path or use `mount`
3621
+ (copy-once, per-VM upper).
3633
3622
 
3634
3623
  Restore on a host where the recorded `host` path doesn't exist:
3635
3624
  fails loudly via `BOOT_MOUNT_HOST_NOT_FOUND`. Pass
@@ -4013,34 +4002,35 @@ Must be a positive multiple of 4096. Default 4 GiB.
4013
4002
 
4014
4003
  > `optional` **liveMounts?**: `object`[]
4015
4004
 
4016
- Host directories exposed to the guest as live-share FUSE mounts
4017
- (#78). Unlike `mount` (copy-once into the boot rootfs), these stay
4018
- connected to the host: the guest reads on demand via a vsock FUSE
4019
- relay, and nothing is copied at boot. `mode` defaults to `"rw"` —
4020
- guest writes land on the host (#151, #156). Set `"ro"` for a
4021
- one-way share (host caches, untrusted guests).
4005
+ Host directories exposed to the guest as live-share mounts (#78,
4006
+ #332). Unlike `mount` (copy-once into the boot rootfs), these stay
4007
+ connected to the host: the guest reads on demand and nothing is
4008
+ copied at boot. `mode` defaults to `"rw"` — guest writes land on
4009
+ the host (#151, #156). Set `"ro"` for a one-way share (host
4010
+ caches, untrusted guests).
4022
4011
 
4023
4012
  Each guest path must live under `/mnt/` (same rule as `mount`).
4024
- Repeatable; each entry gets its own vsock port.
4013
+ Repeatable up to 4 entries per VM — each is served by its own
4014
+ in-VMM virtio-fs device (the VMM wires 4 virtio-fs slots). The
4015
+ FUSE opcode handlers run inside the VMM and the guest mounts each
4016
+ share directly with `mount -t virtiofs` — no agent process, no
4017
+ vsock hop. Requires a guest kernel with `CONFIG_VIRTIO_FS` — every
4018
+ machinen-built kernel has it. (The older FUSE-over-vsock transport
4019
+ and its `protocol` knob were removed in #338.)
4025
4020
 
4026
4021
  Snapshot / restore / fork (#273): liveMount has no guest-side
4027
4022
  state worth checkpointing — reads come from the host on demand,
4028
- writes (in `"rw"`) land on the host immediately. The runtime
4029
- unmounts each mount before CRIU dumps, then re-establishes a
4030
- fresh window on the other side: for `vm.snapshot({ leaveRunning:
4031
- true })` and `vm.fork()` the source's workload sees `/mnt/<guest>/`
4032
- disappear for the dump duration (typically seconds, scales with
4033
- memory size) before reappearing under fresh server state. Open
4034
- fds across that window see EBADF on next syscall — same shape
4035
- as "don't snapshot during a database write." Workloads that
4036
- quiesce before snapshot are unaffected.
4023
+ writes (in `"rw"`) land on the host immediately. The in-VMM
4024
+ virtio-fs device persists across the CRIU dump, so the workload's
4025
+ view of `/mnt/<guest>/` survives `vm.snapshot({ leaveRunning:
4026
+ true })` and `vm.fork()` without an unmount/remount window.
4037
4027
 
4038
4028
  Concurrent writes from multiple forks against the same host
4039
4029
  directory are no different from any other shared filesystem —
4040
- the runtime re-establishes the window per-VM but doesn't
4041
- coordinate writes between siblings. If two forks need
4042
- non-overlapping write surfaces, point each at a distinct
4043
- `host` path or use `mount` (copy-once, per-VM upper).
4030
+ each VM gets its own device but the runtime doesn't coordinate
4031
+ writes between siblings. If two forks need non-overlapping write
4032
+ surfaces, point each at a distinct `host` path or use `mount`
4033
+ (copy-once, per-VM upper).
4044
4034
 
4045
4035
  Restore on a host where the recorded `host` path doesn't exist:
4046
4036
  fails loudly via `BOOT_MOUNT_HOST_NOT_FOUND`. Pass
@@ -4359,34 +4349,35 @@ Must be a positive multiple of 4096. Default 4 GiB.
4359
4349
 
4360
4350
  > `optional` **liveMounts?**: `object`[]
4361
4351
 
4362
- Host directories exposed to the guest as live-share FUSE mounts
4363
- (#78). Unlike `mount` (copy-once into the boot rootfs), these stay
4364
- connected to the host: the guest reads on demand via a vsock FUSE
4365
- relay, and nothing is copied at boot. `mode` defaults to `"rw"` —
4366
- guest writes land on the host (#151, #156). Set `"ro"` for a
4367
- one-way share (host caches, untrusted guests).
4352
+ Host directories exposed to the guest as live-share mounts (#78,
4353
+ #332). Unlike `mount` (copy-once into the boot rootfs), these stay
4354
+ connected to the host: the guest reads on demand and nothing is
4355
+ copied at boot. `mode` defaults to `"rw"` — guest writes land on
4356
+ the host (#151, #156). Set `"ro"` for a one-way share (host
4357
+ caches, untrusted guests).
4368
4358
 
4369
4359
  Each guest path must live under `/mnt/` (same rule as `mount`).
4370
- Repeatable; each entry gets its own vsock port.
4360
+ Repeatable up to 4 entries per VM — each is served by its own
4361
+ in-VMM virtio-fs device (the VMM wires 4 virtio-fs slots). The
4362
+ FUSE opcode handlers run inside the VMM and the guest mounts each
4363
+ share directly with `mount -t virtiofs` — no agent process, no
4364
+ vsock hop. Requires a guest kernel with `CONFIG_VIRTIO_FS` — every
4365
+ machinen-built kernel has it. (The older FUSE-over-vsock transport
4366
+ and its `protocol` knob were removed in #338.)
4371
4367
 
4372
4368
  Snapshot / restore / fork (#273): liveMount has no guest-side
4373
4369
  state worth checkpointing — reads come from the host on demand,
4374
- writes (in `"rw"`) land on the host immediately. The runtime
4375
- unmounts each mount before CRIU dumps, then re-establishes a
4376
- fresh window on the other side: for `vm.snapshot({ leaveRunning:
4377
- true })` and `vm.fork()` the source's workload sees `/mnt/<guest>/`
4378
- disappear for the dump duration (typically seconds, scales with
4379
- memory size) before reappearing under fresh server state. Open
4380
- fds across that window see EBADF on next syscall — same shape
4381
- as "don't snapshot during a database write." Workloads that
4382
- quiesce before snapshot are unaffected.
4370
+ writes (in `"rw"`) land on the host immediately. The in-VMM
4371
+ virtio-fs device persists across the CRIU dump, so the workload's
4372
+ view of `/mnt/<guest>/` survives `vm.snapshot({ leaveRunning:
4373
+ true })` and `vm.fork()` without an unmount/remount window.
4383
4374
 
4384
4375
  Concurrent writes from multiple forks against the same host
4385
4376
  directory are no different from any other shared filesystem —
4386
- the runtime re-establishes the window per-VM but doesn't
4387
- coordinate writes between siblings. If two forks need
4388
- non-overlapping write surfaces, point each at a distinct
4389
- `host` path or use `mount` (copy-once, per-VM upper).
4377
+ each VM gets its own device but the runtime doesn't coordinate
4378
+ writes between siblings. If two forks need non-overlapping write
4379
+ surfaces, point each at a distinct `host` path or use `mount`
4380
+ (copy-once, per-VM upper).
4390
4381
 
4391
4382
  Restore on a host where the recorded `host` path doesn't exist:
4392
4383
  fails loudly via `BOOT_MOUNT_HOST_NOT_FOUND`. Pass
@@ -4716,6 +4707,12 @@ tarball-producing tool can pre-populate the lookup cache.
4716
4707
 
4717
4708
  > `optional` **cwd?**: `string`
4718
4709
 
4710
+ ***
4711
+
4712
+ ### SnapshotEngine
4713
+
4714
+ > **SnapshotEngine** = `"criu"` \| `"vmstate"`
4715
+
4719
4716
  ## Variables
4720
4717
 
4721
4718
  ### STATS\_FILE\_SIZE
@@ -4902,14 +4899,6 @@ tarball-producing tool can pre-populate the lookup cache.
4902
4899
 
4903
4900
  > `readonly` **MOUNT\_PATH\_ESCAPE**: `"MOUNT_PATH_ESCAPE"` = `"MOUNT_PATH_ESCAPE"`
4904
4901
 
4905
- ##### MOUNT\_SERVER\_BIN\_MISSING
4906
-
4907
- > `readonly` **MOUNT\_SERVER\_BIN\_MISSING**: `"MOUNT_SERVER_BIN_MISSING"` = `"MOUNT_SERVER_BIN_MISSING"`
4908
-
4909
- ##### MOUNT\_SERVER\_SPAWN\_FAILED
4910
-
4911
- > `readonly` **MOUNT\_SERVER\_SPAWN\_FAILED**: `"MOUNT_SERVER_SPAWN_FAILED"` = `"MOUNT_SERVER_SPAWN_FAILED"`
4912
-
4913
4902
  ##### SECRETS\_VALUE\_INVALID
4914
4903
 
4915
4904
  > `readonly` **SECRETS\_VALUE\_INVALID**: `"SECRETS_VALUE_INVALID"` = `"SECRETS_VALUE_INVALID"`
@@ -5476,7 +5465,6 @@ Layout:
5476
5465
  blk slots 5+6, not in the cpio.
5477
5466
  /dev/console char node 5,1 — kernel needs it
5478
5467
  before /init re-opens the console
5479
- /fuse-agent optional, only when liveMounts
5480
5468
  /tmp sticky 1777
5481
5469
 
5482
5470
  No /lib/modules tree, no kmod, no /modules/*.ko, no Debian userland.
@@ -6150,9 +6138,12 @@ much the snapshot path is (or isn't) buying us.
6150
6138
 
6151
6139
  Locate the VMM binary using the same lookup order as `@machinen/cli`:
6152
6140
  1. `MACHINEN_VMM` env var (dev-mode override)
6153
- 2. `require.resolve("@machinen/vmm-<arch>-<os>")` → `binary` export
6141
+ 2. `require.resolve("@machinen/native-<arch>-<os>")` → `binary` export
6154
6142
 
6155
- Callers can pass an explicit `binary` to `boot()` to bypass this.
6143
+ `@machinen/native-arm64-{darwin,linux}` is the consolidated host-tool
6144
+ package — it carries the VMM, gvproxy, guest ELFs, mke2fs,
6145
+ mksquashfs, and the mount server. Callers can pass an explicit
6146
+ `binary` to `boot()` to bypass this.
6156
6147
 
6157
6148
  #### Returns
6158
6149