@braedonsaunders/appkit-desk 0.10.0 → 0.11.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/README.md +117 -0
- package/backend.d.ts +13 -1
- package/backend.d.ts.map +1 -1
- package/backend.js +23 -9
- package/backend.js.map +1 -1
- package/host.d.ts +33 -5
- package/host.d.ts.map +1 -1
- package/host.js +216 -18
- package/host.js.map +1 -1
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -1
- package/index.js +3 -0
- package/index.js.map +1 -1
- package/machine.d.ts +4 -2
- package/machine.d.ts.map +1 -1
- package/machine.js +5 -3
- package/machine.js.map +1 -1
- package/package.json +1 -1
- package/plan.d.ts +6 -1
- package/plan.d.ts.map +1 -1
- package/plan.js +7 -3
- package/plan.js.map +1 -1
- package/template-mapping.d.ts +5 -0
- package/template-mapping.d.ts.map +1 -0
- package/template-mapping.js +48 -0
- package/template-mapping.js.map +1 -0
- package/template-plan.d.ts +18 -0
- package/template-plan.d.ts.map +1 -0
- package/template-plan.js +52 -0
- package/template-plan.js.map +1 -0
- package/template-registry.d.ts +37 -0
- package/template-registry.d.ts.map +1 -0
- package/template-registry.js +190 -0
- package/template-registry.js.map +1 -0
- package/template-source.d.ts +15 -0
- package/template-source.d.ts.map +1 -0
- package/template-source.js +95 -0
- package/template-source.js.map +1 -0
package/plan.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from 'node:buffer'\nimport { createHash } from 'node:crypto'\nimport { existsSync, statSync } from 'node:fs'\nimport { isIP } from 'node:net'\nimport { arch as hostArch, release as hostRelease, tmpdir } from 'node:os'\nimport { delimiter, dirname, isAbsolute, join, resolve } from 'node:path'\n\nexport const DEFAULT_VMM_PATH = '/usr/bin/cloud-hypervisor'\nexport const DEFAULT_KVM_PATH = '/dev/kvm'\nexport const DEFAULT_QEMU_IMG_PATH = '/usr/bin/qemu-img'\nexport const DEFAULT_RUNTIME_DIR = '/run/appkit-desk'\nexport const DEFAULT_MEMORY_MB = 384\nexport const DEFAULT_VCPUS = 2\nexport const DEFAULT_KERNEL_CMDLINE = 'root=/dev/vda rw quiet'\n/**\n * Where a Cloud Hypervisor desk looks for `virtiofsd` when `virtiofsdPath` is\n * not set and it is not on `PATH`: Debian's and Fedora's package location.\n */\nexport const DEFAULT_VIRTIOFSD_PATH = '/usr/libexec/virtiofsd'\nexport const DEFAULT_VIRTIOFSD_SANDBOX: DeskVirtiofsdSandbox = 'namespace'\n/**\n * The guest network Virtualization.framework desks get from the helper's NAT:\n * the same addresses QEMU's user networking uses, so they are unsurprising in\n * a guest. Nothing outside the helper ever sees them.\n */\nexport const DEFAULT_VZ_GUEST_ADDRESS = '10.0.2.15'\nexport const DEFAULT_VZ_GATEWAY_ADDRESS = '10.0.2.2'\nexport const DEFAULT_VZ_PREFIX_LENGTH = 24\nexport const DEFAULT_VZ_DNS: readonly string[] = ['1.1.1.1', '9.9.9.9']\n/** Virtualization.framework always gives the guest vsock context ID 3. */\nexport const VZ_GUEST_CID = 3\n/** The first Darwin release with Virtualization.framework save and restore (macOS 14). */\nconst MIN_DARWIN_MAJOR = 23\n/** `sun_path` holds 104 bytes on Darwin, including the terminator. */\nconst DARWIN_SOCKET_PATH_LIMIT = 103\n/** `sun_path` holds 108 bytes on Linux, including the terminator. */\nconst LINUX_SOCKET_PATH_LIMIT = 107\n/** Linux's virtio-fs tag field is 36 bytes; Virtualization.framework enforces the same. */\nconst SHARE_TAG_PATTERN = /^[a-z0-9][a-z0-9-]{0,35}$/\n\nconst DESK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/\nconst TAP_DEVICE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,14}$/\nconst MAC_ADDRESS_PATTERN = /^[0-9a-f]{2}(?::[0-9a-f]{2}){5}$/i\n/** vsock CIDs 0–2 are reserved (hypervisor, loopback, host). */\nconst MIN_GUEST_CID = 3\nconst MAX_GUEST_CID = 0xffff_fffe\n\n/**\n * Every failure this package raises. The `name` is annotated as `string`\n * rather than left to infer its literal so a subclass can narrow the meaning\n * of a failure — see `DeskRequestFateUnknownError` — without callers losing\n * the one `instanceof DeskError` they already check.\n */\nexport class DeskError extends Error {\n override readonly name: string = 'DeskError'\n}\n\nexport interface DeskLauncherIdentity {\n /** Host/container UID used to invoke the VMM and image tooling. */\n uid: number\n /** Host/container GID used to invoke the VMM and image tooling. */\n gid: number\n}\n\n/**\n * Which hypervisor a plan is for: Cloud Hypervisor over KVM on Linux, or\n * Apple's Virtualization.framework through the `appkit-desk-vz` helper on an\n * Apple silicon Mac.\n */\nexport type DeskHypervisor = 'cloud-hypervisor' | 'virtualization'\n\n/**\n * The guest network of a Virtualization.framework desk. The helper runs a\n * userspace NAT: the guest reaches the public internet and nothing else unless\n * `allowLan` is set — no RFC 1918 space, link-local, CGNAT, or the Mac itself.\n */\nexport interface DeskGuestNetworkOptions {\n /** Let the guest reach the Mac's LAN, link-local and CGNAT space, and the Mac. */\n allowLan?: boolean\n /** The address the helper's DHCP server leases the guest. */\n guestAddress?: string\n /** The helper's own address on the link: the guest's router. */\n gatewayAddress?: string\n prefixLength?: number\n /** Resolvers the guest is handed; the first also answers DNS sent to the gateway. */\n dns?: readonly string[]\n}\n\n/**\n * A host directory shared into the guest over virtio-fs. The guest mounts it\n * with `mount -t virtiofs <tag> <dir>`.\n */\nexport interface DeskShare {\n /** 1–36 lowercase letters, digits, or hyphens, starting with a letter or digit; unique per desk. */\n tag: string\n /** Absolute path of an existing host directory. */\n path: string\n /**\n * Refuse every write from the guest. Enforced by the host's file server, so\n * a guest cannot lift it by remounting the share read-write.\n */\n readOnly: boolean\n}\n\n/**\n * How `virtiofsd` confines itself to the shared directory. `namespace` (its\n * own default) pivots into the directory inside new mount, PID and network\n * namespaces, which needs CAP_SYS_ADMIN when run as root and unprivileged user\n * namespaces otherwise. `chroot` only chroots, which needs root and suits a\n * container that may not create namespaces. Both stop symlinks from escaping\n * the share; running unconfined is not offered.\n */\nexport type DeskVirtiofsdSandbox = 'namespace' | 'chroot'\n\n/** An immutable host image. The caller must retain its bytes and path for every snapshot. */\nexport interface DeskReadOnlyAttachment {\n path: string\n /** SHA-256 of the immutable image, used as snapshot identity. */\n sha256: string\n kind: 'block' | 'pmem'\n}\n\nexport interface DeskLaunchPlanOptions {\n deskId: string\n /** Defaults to `cloud-hypervisor`. */\n hypervisor?: DeskHypervisor\n /** Cloud Hypervisor only: the VMM binary. */\n vmmPath?: string\n /** Cloud Hypervisor only: the KVM device. */\n kvmPath?: string\n qemuImgPath?: string\n /** Absolute path of the kernel image the microVM boots. */\n kernelPath: string\n /**\n * Absolute path of an initramfs. Required whenever the kernel loads its\n * root-filesystem or virtio drivers as modules — a stock distro cloud kernel\n * panics with \"VFS: Unable to mount root fs\" if it boots without one. Omit\n * only for a kernel with virtio_blk/ext4 built in.\n */\n initramfsPath?: string\n /** Absolute path of the golden raw base image every desk clones. */\n baseImagePath: string\n /** Absolute path of this desk's copy-on-write overlay (a raw reflink clone). */\n overlayPath: string\n memoryMb?: number\n /** Cloud Hypervisor virtio-balloon with free-page reporting, size zero. */\n freePageReporting?: boolean\n vcpus?: number\n /**\n * Guest vsock context ID; must be unique among resident desks. Cloud\n * Hypervisor only — a Virtualization.framework guest is always CID 3, and\n * its vsock is private to its helper.\n */\n vsockCid: number\n /**\n * Directory holding the desk's control sockets. Defaults to\n * `/run/appkit-desk` for Cloud Hypervisor and `<os tmpdir>/appkit-desk` for\n * Virtualization.framework.\n */\n runtimeDir?: string\n /** Cloud Hypervisor only: host TAP device name; derived from the CID when omitted. */\n tapDevice?: string\n /**\n * Guest MAC address. Derived from the CID for Cloud Hypervisor, and from the\n * desk ID for Virtualization.framework, whose restore needs the same NIC.\n */\n macAddress?: string\n /** Virtualization.framework only: the helper's NAT network and fence. */\n guestNetwork?: DeskGuestNetworkOptions\n /**\n * Host directories shared into the guest. Restoring a snapshot requires the\n * same shares the desk was parked with; see the backends.\n */\n shares?: readonly DeskShare[]\n /** Additional immutable Cloud Hypervisor devices, in guest enumeration order. */\n readOnlyAttachments?: readonly DeskReadOnlyAttachment[]\n /**\n * Cloud Hypervisor only: the `virtiofsd` binary (1.13 or later) that serves\n * each share. Defaults to the first `virtiofsd` on `PATH`, then\n * `/usr/libexec/virtiofsd`. Not needed, and not looked for, without shares.\n */\n virtiofsdPath?: string\n /** Cloud Hypervisor only: defaults to `namespace`. */\n virtiofsdSandbox?: DeskVirtiofsdSandbox\n kernelCmdline?: string\n launcherIdentity?: DeskLauncherIdentity\n /**\n * Where this desk's memory snapshot lives. Defaults beside the overlay, which\n * is the volume sized for desk state — a snapshot is roughly `memoryMb` of\n * guest RAM, so it does not belong on a runtime tmpfs.\n */\n snapshotDir?: string\n /**\n * Resume from that snapshot instead of booting the kernel.\n *\n * A restored VM continues with its process tree intact, which is the whole\n * point of parking with memory rather than shutting down: an agent's in-guest\n * daemon survives. Refused when the snapshot is not there, rather than falling\n * back to a cold boot — a silent cold boot is how a desk loses the state it\n * was promised to have kept.\n */\n restore?: boolean\n /** Cloud Hypervisor copy-on-write restore; the source must remain unchanged while resident. */\n memoryRestoreMode?: 'copyonwrite'\n}\n\n/** What every plan carries, whichever hypervisor runs it. */\nexport interface DeskLaunchPlanBase {\n deskId: string\n overlay: {\n path: string\n basePath: string\n /** The overlay-creation step, or `null` when the overlay already exists. */\n create: { command: string; args: string[] } | null\n }\n /**\n * The host Unix socket the guest's vsock is reached through, with Cloud\n * Hypervisor's hybrid-vsock preamble: write `CONNECT <port>\\n`, read\n * `OK <n>\\n`, then the stream is the guest connection.\n */\n vsock: { cid: number; socketPath: string }\n /** Where a memory snapshot of this desk is written, and read back from. */\n snapshot: { dir: string; restoring: boolean }\n /** Host directories shared into the guest, in the order they were given. */\n shares: readonly DeskShare[]\n memoryMb: number\n vcpus: number\n kernelPath: string\n /** Resolved initramfs path, or `null` when the kernel needs none. */\n initramfsPath: string | null\n kernelCmdline: string\n launcherIdentity?: DeskLauncherIdentity\n}\n\n/**\n * The complete Cloud Hypervisor invocation as inspectable data. Nothing here\n * has been spawned: tests, audit logs, and readiness screens can assert on the\n * exact argv a desk would boot with.\n */\nexport interface CloudHypervisorLaunchPlan extends DeskLaunchPlanBase {\n hypervisor: 'cloud-hypervisor'\n vmm: { command: string; args: string[] }\n api: { socketPath: string }\n tap: { device: string; mac: string }\n /**\n * Each share with the `virtiofsd` that serves it. Every daemon must be\n * listening on its socket before the VMM starts, for a boot and for a\n * restore alike.\n */\n shares: readonly CloudHypervisorShare[]\n}\n\nexport interface CloudHypervisorShare extends DeskShare {\n /** The vhost-user socket the daemon listens on and the VMM connects to. */\n socketPath: string\n daemon: { command: string; args: string[] }\n}\n\n/**\n * A Virtualization.framework desk as inspectable data: what the helper is\n * asked to boot. The helper path belongs to the backend, not the plan.\n */\nexport interface VirtualizationLaunchPlan extends DeskLaunchPlanBase {\n hypervisor: 'virtualization'\n /**\n * The host Unix socket guest TCP ports are reached through, with the same\n * preamble as `vsock`: `CONNECT <tcp port>\\n` answered by `OK <n>\\n` once\n * the guest accepts, or a close with nothing written when it refuses.\n */\n ports: { socketPath: string }\n network: {\n mac: string\n allowLan: boolean\n guestAddress: string\n gatewayAddress: string\n prefixLength: number\n dns: string[]\n }\n}\n\nexport type DeskLaunchPlan = CloudHypervisorLaunchPlan | VirtualizationLaunchPlan\n\n/**\n * Whether this host can run desks at all. On Linux: a usable `/dev/kvm` and\n * the Cloud Hypervisor binary. On macOS: Apple silicon, macOS 14 or later (the\n * first with Virtualization.framework save and restore), and an existing\n * `appkit-desk-vz` helper at `helperPath`. Signature and entitlement are checked\n * by macOS on execution, not by this probe. Every check is injectable and the\n * answer is fail-closed — a host missing any of them gets no desk capability\n * rather than a degraded one.\n */\nexport function isDeskSupported(options: {\n platform?: NodeJS.Platform\n kvmPath?: string\n vmmPath?: string\n /** macOS: the signed `appkit-desk-vz` helper. Required there. */\n helperPath?: string\n arch?: string\n /** The kernel release, as `os.release()` reports it. */\n osRelease?: string\n pathExists?: (path: string) => boolean\n} = {}): boolean {\n const platform = options.platform ?? process.platform\n const pathExists = options.pathExists ?? existsSync\n if (platform === 'darwin') {\n const major = Number.parseInt((options.osRelease ?? hostRelease()).split('.')[0] ?? '', 10)\n return (\n (options.arch ?? hostArch()) === 'arm64'\n && Number.isInteger(major)\n && major >= MIN_DARWIN_MAJOR\n && options.helperPath !== undefined\n && isAbsolute(options.helperPath)\n && pathExists(options.helperPath)\n )\n }\n return (\n platform === 'linux'\n && pathExists(options.kvmPath ?? DEFAULT_KVM_PATH)\n && pathExists(options.vmmPath ?? DEFAULT_VMM_PATH)\n )\n}\n\n/** Validate a desk identifier, which is used in socket paths and device names. */\nexport function cleanDeskId(value: string): string {\n if (!DESK_ID_PATTERN.test(value)) {\n throw new DeskError(\n 'deskId must be 1–64 letters, numbers, dots, underscores, or hyphens.',\n )\n }\n return value\n}\n\nexport interface DeskLaunchPlanDependencies {\n pathExists?: (path: string) => boolean\n deviceExists?: (path: string) => boolean\n /** Whether a path is an existing directory; checked for every share. */\n isDirectory?: (path: string) => boolean\n /** Searched for `virtiofsd` when `virtiofsdPath` is not set; defaults to `process.env.PATH`. */\n searchPath?: string\n}\n\n/**\n * Build the deterministic launch plan without spawning anything. Fail closed:\n * a missing `/dev/kvm` or VMM binary (Cloud Hypervisor), kernel, base image,\n * or overlay directory throws instead of producing a plan that cannot boot.\n */\nexport function buildDeskLaunchPlan(\n options: DeskLaunchPlanOptions & { hypervisor: 'virtualization' },\n dependencies?: DeskLaunchPlanDependencies,\n): VirtualizationLaunchPlan\nexport function buildDeskLaunchPlan(\n options: DeskLaunchPlanOptions & { hypervisor?: 'cloud-hypervisor' },\n dependencies?: DeskLaunchPlanDependencies,\n): CloudHypervisorLaunchPlan\nexport function buildDeskLaunchPlan(\n options: DeskLaunchPlanOptions,\n dependencies?: DeskLaunchPlanDependencies,\n): DeskLaunchPlan\nexport function buildDeskLaunchPlan(\n options: DeskLaunchPlanOptions,\n dependencies: DeskLaunchPlanDependencies = {},\n): DeskLaunchPlan {\n const hypervisor = options.hypervisor ?? 'cloud-hypervisor'\n if (options.memoryRestoreMode !== undefined && (options.memoryRestoreMode !== 'copyonwrite' || hypervisor !== 'cloud-hypervisor')) {\n throw new DeskError('Copy-on-write restore requires Cloud Hypervisor.')\n }\n if (hypervisor === 'virtualization') return buildVirtualizationPlan(options, dependencies)\n if (hypervisor !== 'cloud-hypervisor') {\n throw new DeskError(`Unknown hypervisor: ${String(hypervisor)}`)\n }\n if (options.guestNetwork !== undefined) {\n throw new DeskError(\n 'guestNetwork applies to Virtualization.framework desks; a Cloud Hypervisor desk is networked with createDeskNetwork.',\n )\n }\n return buildCloudHypervisorPlan(options, dependencies)\n}\n\nfunction buildCloudHypervisorPlan(\n options: DeskLaunchPlanOptions,\n dependencies: DeskLaunchPlanDependencies,\n): CloudHypervisorLaunchPlan {\n const pathExists = dependencies.pathExists ?? existsSync\n const deviceExists = dependencies.deviceExists ?? existsSync\n const isDirectory = dependencies.isDirectory ?? directoryExists\n const deskId = cleanDeskId(options.deskId)\n const kvmPath = absolutePath(options.kvmPath ?? DEFAULT_KVM_PATH, 'kvmPath')\n if (!deviceExists(kvmPath)) {\n throw new DeskError(\n `KVM is not available at ${kvmPath}. Desks require hardware virtualization; `\n + 'there is no software-emulation fallback.',\n )\n }\n const vmmPath = absolutePath(options.vmmPath ?? DEFAULT_VMM_PATH, 'vmmPath')\n if (!pathExists(vmmPath)) {\n throw new DeskError(\n `Cloud Hypervisor was not found at ${vmmPath}. Install cloud-hypervisor or configure vmmPath.`,\n )\n }\n const kernelPath = absolutePath(options.kernelPath, 'kernelPath')\n if (!pathExists(kernelPath)) {\n throw new DeskError(`Guest kernel does not exist: ${kernelPath}`)\n }\n const initramfsPath =\n options.initramfsPath === undefined\n ? null\n : absolutePath(options.initramfsPath, 'initramfsPath')\n if (initramfsPath !== null && !pathExists(initramfsPath)) {\n throw new DeskError(`Initramfs does not exist: ${initramfsPath}`)\n }\n const baseImagePath = absolutePath(options.baseImagePath, 'baseImagePath')\n if (!pathExists(baseImagePath)) {\n throw new DeskError(`Base image does not exist: ${baseImagePath}`)\n }\n const overlayPath = absolutePath(options.overlayPath, 'overlayPath')\n const overlayDirectory = dirname(overlayPath)\n if (!pathExists(overlayDirectory)) {\n throw new DeskError(`Overlay directory does not exist: ${overlayDirectory}`)\n }\n const memoryMb = positiveInteger(options.memoryMb ?? DEFAULT_MEMORY_MB, 'memoryMb')\n const vcpus = positiveInteger(options.vcpus ?? DEFAULT_VCPUS, 'vcpus')\n const vsockCid = cleanCid(options.vsockCid)\n const runtimeDir = absolutePath(options.runtimeDir ?? DEFAULT_RUNTIME_DIR, 'runtimeDir')\n const tapDevice = cleanTapDevice(options.tapDevice ?? defaultTapDevice(vsockCid))\n const macAddress = cleanMacAddress(options.macAddress ?? defaultMacAddress(vsockCid))\n const kernelCmdline = options.kernelCmdline ?? DEFAULT_KERNEL_CMDLINE\n const launcherIdentity = cleanLauncherIdentity(options.launcherIdentity)\n const vsockSocketPath = join(runtimeDir, `desk-${deskId}.vsock`)\n const apiSocketPath = join(runtimeDir, `desk-${deskId}.api.sock`)\n // Beside the overlay by default: a snapshot is the guest's RAM, so it belongs\n // on the volume provisioned for desk state rather than on a runtime tmpfs.\n const snapshotDir = options.snapshotDir\n ? absolutePath(options.snapshotDir, 'snapshotDir')\n : join(overlayDirectory, `${deskId}.snapshot`)\n const restoring = options.restore === true\n if (restoring && !pathExists(snapshotDir)) {\n // Refused rather than silently cold-booted. A caller asking to restore has\n // been told this desk kept its memory; quietly booting a fresh kernel would\n // lose the process tree it was relying on and look like a crash inside the\n // guest instead of a missing snapshot out here.\n throw new DeskError(`Desk ${deskId} has no snapshot to restore at ${snapshotDir}`)\n }\n const attachments = cleanAttachments(options.readOnlyAttachments, pathExists)\n const shares = cloudHypervisorShares(cleanShares(options.shares, isDirectory), {\n deskId,\n runtimeDir,\n virtiofsdPath: options.virtiofsdPath,\n sandbox: options.virtiofsdSandbox ?? DEFAULT_VIRTIOFSD_SANDBOX,\n pathExists,\n searchPath: dependencies.searchPath ?? process.env.PATH ?? '',\n })\n\n const create = pathExists(overlayPath)\n ? null\n : {\n // Cloud Hypervisor cannot follow disk backing chains: a qcow2 overlay\n // over a qcow2/raw base is rejected with UnsupportedFeature /\n // MaxNestingDepthExceeded. So the overlay is a plain raw file, cloned\n // from the raw base with `cp --reflink=auto` — an instant, block-sharing\n // copy-on-write clone on XFS/Btrfs and a graceful full-copy fallback on\n // ext4. Either way CH boots the resulting raw file with no backing chain.\n command: 'cp',\n args: ['--reflink=auto', baseImagePath, overlayPath],\n }\n\n // A restoring VMM is handed the snapshot and nothing else.\n //\n // The snapshot carries the whole machine config — memory size, cpus, disk,\n // vsock, net — so passing `--kernel`/`--disk` alongside `--restore` is not\n // redundant, it is rejected.\n //\n // `resume=false` is deliberate, and it is the difference between this being\n // safe and being a data-loss bug. A memory snapshot is only valid against the\n // disk as it stood when the snapshot was taken. Resume the guest in this same\n // spawn and it starts writing immediately, so for as long as the snapshot\n // still exists there are two valid-looking pasts for one desk — and a host\n // that died in that window would, on the next start, replay this memory\n // against a disk that had moved on. That is corruption, not a stale cache.\n //\n // The guest comes back PAUSED; before it runs, the caller must make the\n // snapshot unavailable for replay. File-backed restores keep their retired\n // source unchanged until the VMM exits.\n //\n // The tap is reopened BY NAME from the snapshotted config. That is the detail\n // that makes this possible at all: Cloud Hypervisor's `net_fds` restore\n // parameter exists for VMs whose tap was passed as a file descriptor, and\n // producing one from Node would mean an ioctl(TUNSETIFF) it cannot make.\n // Because `deskPlan` configures `--net tap=<name>`, there is nothing to pass.\n //\n // Shares are in the snapshotted config too, by socket path, with each\n // daemon's state. What the snapshot cannot hold is a running `virtiofsd`, so\n // the backend starts the same daemons on the same sockets before this VMM\n // either way, and the VMM loads their state back into them.\n const args = restoring\n ? [\n '--api-socket', apiSocketPath,\n '--restore', `source_url=file://${snapshotDir},resume=false${options.memoryRestoreMode === 'copyonwrite' ? ',memory_restore_mode=copyonwrite' : ''}`,\n ]\n : [\n '--api-socket', apiSocketPath,\n '--kernel', kernelPath,\n // A modular distro kernel needs its initramfs to bring up virtio_blk and\n // mount root; omitted only for a kernel with those drivers built in.\n ...(initramfsPath === null ? [] : ['--initramfs', initramfsPath]),\n '--cmdline', kernelCmdline,\n // Be explicit that the disk is raw: CH deprecated image-format auto-detection\n // and warns without image_type; there is never a qcow2 overlay here.\n '--disk', `path=${overlayPath},image_type=raw`, ...attachments.filter(a => a.kind === 'block').map(a => `path=${a.path},readonly=on,image_type=raw`),\n ...attachments.filter(a => a.kind === 'pmem').flatMap(a => ['--pmem', `file=${a.path},discard_writes=on`]),\n // vhost-user devices map guest RAM into their daemon, so it must be\n // shared memory; a desk without shares keeps private memory.\n '--memory', `size=${memoryMb}M${shares.length === 0 ? '' : ',shared=on'}`,\n ...(options.freePageReporting ? ['--balloon', 'size=0,free_page_reporting=on'] : []),\n '--cpus', `boot=${vcpus}`,\n '--vsock', `cid=${vsockCid},socket=${vsockSocketPath}`,\n '--net', `tap=${tapDevice},mac=${macAddress}`,\n ...(shares.length === 0\n ? []\n : ['--fs', ...shares.map((share) => `tag=${share.tag},socket=${share.socketPath}`)]),\n '--serial', 'off',\n '--console', 'off',\n ]\n\n return {\n hypervisor: 'cloud-hypervisor',\n deskId,\n vmm: { command: vmmPath, args },\n overlay: { path: overlayPath, basePath: baseImagePath, create },\n vsock: { cid: vsockCid, socketPath: vsockSocketPath },\n api: { socketPath: apiSocketPath },\n tap: { device: tapDevice, mac: macAddress },\n snapshot: { dir: snapshotDir, restoring },\n shares,\n memoryMb,\n vcpus,\n kernelPath,\n initramfsPath,\n kernelCmdline,\n launcherIdentity,\n }\n}\n\n/**\n * The Virtualization.framework plan. The same fail-closed checks on the guest\n * assets; no KVM or VMM binary, because the backend owns the helper; and the\n * overlay is an APFS clone (`cp -c`), which is to APFS what `--reflink=auto`\n * is to XFS: instant and block-sharing. A volume that cannot clone must be\n * replaced with one that supports it.\n */\nfunction buildVirtualizationPlan(\n options: DeskLaunchPlanOptions,\n dependencies: DeskLaunchPlanDependencies,\n): VirtualizationLaunchPlan {\n const pathExists = dependencies.pathExists ?? existsSync\n if (options.readOnlyAttachments?.length) throw new DeskError('Read-only attachments require Cloud Hypervisor.')\n if (options.freePageReporting) throw new DeskError('Free-page reporting requires Cloud Hypervisor.')\n const deskId = cleanDeskId(options.deskId)\n if (options.tapDevice !== undefined) {\n throw new DeskError('tapDevice applies to Cloud Hypervisor desks; a Virtualization.framework desk has no TAP.')\n }\n if (options.virtiofsdPath !== undefined || options.virtiofsdSandbox !== undefined) {\n throw new DeskError(\n 'virtiofsdPath and virtiofsdSandbox apply to Cloud Hypervisor desks; Virtualization.framework serves shares itself.',\n )\n }\n const kernelPath = absolutePath(options.kernelPath, 'kernelPath')\n if (!pathExists(kernelPath)) {\n throw new DeskError(`Guest kernel does not exist: ${kernelPath}`)\n }\n const initramfsPath =\n options.initramfsPath === undefined ? null : absolutePath(options.initramfsPath, 'initramfsPath')\n if (initramfsPath !== null && !pathExists(initramfsPath)) {\n throw new DeskError(`Initramfs does not exist: ${initramfsPath}`)\n }\n const baseImagePath = absolutePath(options.baseImagePath, 'baseImagePath')\n if (!pathExists(baseImagePath)) {\n throw new DeskError(`Base image does not exist: ${baseImagePath}`)\n }\n const overlayPath = absolutePath(options.overlayPath, 'overlayPath')\n const overlayDirectory = dirname(overlayPath)\n if (!pathExists(overlayDirectory)) {\n throw new DeskError(`Overlay directory does not exist: ${overlayDirectory}`)\n }\n const memoryMb = positiveInteger(options.memoryMb ?? DEFAULT_MEMORY_MB, 'memoryMb')\n const vcpus = positiveInteger(options.vcpus ?? DEFAULT_VCPUS, 'vcpus')\n const runtimeDir = absolutePath(options.runtimeDir ?? join(tmpdir(), 'appkit-desk'), 'runtimeDir')\n // Keep Darwin's small sockaddr_un limit even for a maximum-length desk ID.\n const socketId = createHash('sha256').update(deskId).digest('hex').slice(0, 20)\n const vsockSocketPath = socketPath(join(runtimeDir, `d-${socketId}.vsock`))\n const portsSocketPath = socketPath(join(runtimeDir, `d-${socketId}.ports`))\n const snapshotDir = options.snapshotDir\n ? absolutePath(options.snapshotDir, 'snapshotDir')\n : join(overlayDirectory, `${deskId}.snapshot`)\n const restoring = options.restore === true\n if (restoring && !pathExists(snapshotDir)) {\n throw new DeskError(`Desk ${deskId} has no snapshot to restore at ${snapshotDir}`)\n }\n const network = cleanGuestNetwork(options.guestNetwork ?? {})\n const shares = cleanShares(options.shares, dependencies.isDirectory ?? directoryExists)\n return {\n hypervisor: 'virtualization',\n deskId,\n overlay: {\n path: overlayPath,\n basePath: baseImagePath,\n create: pathExists(overlayPath) ? null : { command: 'cp', args: ['-c', baseImagePath, overlayPath] },\n },\n vsock: { cid: VZ_GUEST_CID, socketPath: vsockSocketPath },\n ports: { socketPath: portsSocketPath },\n network: {\n mac: cleanMacAddress(options.macAddress ?? deskMacAddress(deskId)),\n ...network,\n },\n snapshot: { dir: snapshotDir, restoring },\n shares,\n memoryMb,\n vcpus,\n kernelPath,\n initramfsPath,\n kernelCmdline: options.kernelCmdline ?? DEFAULT_KERNEL_CMDLINE,\n launcherIdentity: cleanLauncherIdentity(options.launcherIdentity),\n }\n}\n\n/** Reject ambiguous Cloud Hypervisor key-value arguments and unpinned images. */\nexport function cleanAttachments(attachments: readonly DeskReadOnlyAttachment[] | undefined, pathExists: (path: string) => boolean): DeskReadOnlyAttachment[] {\n if (attachments === undefined) return []\n if (!Array.isArray(attachments)) throw new DeskError('readOnlyAttachments must be an array.')\n return attachments.map((attachment, index) => {\n if (!attachment || !['block', 'pmem'].includes(attachment.kind) || !/^[a-f0-9]{64}$/.test(attachment.sha256)) throw new DeskError(`Invalid readOnlyAttachments[${index}].`)\n const path = absolutePath(attachment.path, `readOnlyAttachments[${index}].path`)\n if (path.includes(',') || !pathExists(path)) throw new DeskError(`Invalid attachment path: ${path}`)\n return { path, kind: attachment.kind, sha256: attachment.sha256 }\n })\n}\n\n/** Validate shares as given: tags, absolute existing directories, explicit access. */\nfunction cleanShares(\n shares: readonly DeskShare[] | undefined,\n isDirectory: (path: string) => boolean,\n): DeskShare[] {\n if (shares === undefined) return []\n if (!Array.isArray(shares)) throw new DeskError('shares must be an array.')\n const tags = new Set<string>()\n return shares.map((share: DeskShare, index) => {\n const field = `shares[${index}]`\n if (typeof share !== 'object' || share === null) throw new DeskError(`${field} must be an object.`)\n if (typeof share.tag !== 'string' || !SHARE_TAG_PATTERN.test(share.tag)) {\n throw new DeskError(\n `${field}.tag must be 1–36 lowercase letters, digits, or hyphens, starting with a letter or digit.`,\n )\n }\n if (tags.has(share.tag)) throw new DeskError(`${field}.tag ${share.tag} is already used by another share.`)\n tags.add(share.tag)\n if (typeof share.path !== 'string') throw new DeskError(`${field}.path must be an absolute path.`)\n const path = absolutePath(share.path, `${field}.path`)\n if (!isDirectory(path)) throw new DeskError(`${field}.path is not an existing directory: ${path}`)\n if (typeof share.readOnly !== 'boolean') throw new DeskError(`${field}.readOnly must be a boolean.`)\n return { tag: share.tag, path, readOnly: share.readOnly }\n })\n}\n\n/**\n * One `virtiofsd` per share. `--cache=auto` lets the guest cache for a second\n * at a time, so changes the host makes under a share reach running guests;\n * `always` would let a guest keep serving a file the host has replaced.\n * `--readonly` is the enforcement for a read-only share — the guest is never\n * trusted to honour a read-only mount.\n *\n * The migration flags are for parking. Cloud Hypervisor carries the daemon's\n * state — the files and directories the guest holds — inside a snapshot, and\n * hands it to the fresh daemon on restore, which reopens each by path. A file\n * that has no path by then (unlinked on the host, or by the guest while held\n * open) becomes an error for the guest to see on that file alone, rather than\n * a restore that fails and costs the whole desk its memory.\n */\nfunction cloudHypervisorShares(\n shares: readonly DeskShare[],\n context: {\n deskId: string\n runtimeDir: string\n virtiofsdPath: string | undefined\n sandbox: DeskVirtiofsdSandbox\n pathExists: (path: string) => boolean\n searchPath: string\n },\n): CloudHypervisorShare[] {\n if (shares.length === 0) return []\n if (context.sandbox !== 'namespace' && context.sandbox !== 'chroot') {\n throw new DeskError(\"virtiofsdSandbox must be 'namespace' or 'chroot'.\")\n }\n const command = resolveVirtiofsd(context.virtiofsdPath, context.pathExists, context.searchPath)\n return shares.map((share) => {\n // A dot cannot appear in a tag, so `<desk>.<tag>` names one socket for\n // exactly one desk and share even though desk IDs may contain hyphens.\n const socketPath = join(context.runtimeDir, `desk-${context.deskId}.${share.tag}.fs.sock`)\n if (Buffer.byteLength(socketPath) > LINUX_SOCKET_PATH_LIMIT) {\n throw new DeskError(\n `Socket path ${socketPath} is longer than the ${LINUX_SOCKET_PATH_LIMIT} bytes Linux allows; `\n + 'use a shorter runtimeDir, deskId, or share tag.',\n )\n }\n return {\n ...share,\n socketPath,\n daemon: {\n command,\n args: [\n `--socket-path=${socketPath}`,\n `--shared-dir=${share.path}`,\n `--sandbox=${context.sandbox}`,\n '--cache=auto',\n '--migration-mode=find-paths',\n '--migration-on-error=guest-error',\n ...(share.readOnly ? ['--readonly'] : []),\n ],\n },\n }\n })\n}\n\nfunction resolveVirtiofsd(\n configured: string | undefined,\n pathExists: (path: string) => boolean,\n searchPath: string,\n): string {\n if (configured !== undefined) {\n const path = absolutePath(configured, 'virtiofsdPath')\n if (!pathExists(path)) {\n throw new DeskError(`virtiofsd was not found at ${path}. Install virtiofsd or configure virtiofsdPath.`)\n }\n return path\n }\n const candidates = [\n ...searchPath.split(delimiter).filter((entry) => isAbsolute(entry)).map((entry) => join(entry, 'virtiofsd')),\n DEFAULT_VIRTIOFSD_PATH,\n ]\n const found = candidates.find((candidate) => pathExists(candidate))\n if (found === undefined) {\n throw new DeskError(\n `virtiofsd was not found on PATH or at ${DEFAULT_VIRTIOFSD_PATH}. Install virtiofsd 1.13 or later, or configure virtiofsdPath.`,\n )\n }\n return found\n}\n\nfunction directoryExists(path: string): boolean {\n return statSync(path, { throwIfNoEntry: false })?.isDirectory() === true\n}\n\nfunction cleanGuestNetwork(\n options: DeskGuestNetworkOptions,\n): Omit<VirtualizationLaunchPlan['network'], 'mac'> {\n if (options.allowLan !== undefined && typeof options.allowLan !== 'boolean') {\n throw new DeskError('guestNetwork.allowLan must be a boolean.')\n }\n const guestAddress = ipv4(options.guestAddress ?? DEFAULT_VZ_GUEST_ADDRESS, 'guestNetwork.guestAddress')\n const gatewayAddress = ipv4(options.gatewayAddress ?? DEFAULT_VZ_GATEWAY_ADDRESS, 'guestNetwork.gatewayAddress')\n const prefixLength = options.prefixLength ?? DEFAULT_VZ_PREFIX_LENGTH\n if (!Number.isInteger(prefixLength) || prefixLength < 8 || prefixLength > 30) {\n throw new DeskError('guestNetwork.prefixLength must be an integer between 8 and 30.')\n }\n const mask = (0xffff_ffff << (32 - prefixLength)) >>> 0\n if (\n guestAddress === gatewayAddress\n || ((ipv4Number(guestAddress) & mask) >>> 0) !== ((ipv4Number(gatewayAddress) & mask) >>> 0)\n ) {\n throw new DeskError(\"guestNetwork.guestAddress must be a different address in the gateway's subnet.\")\n }\n const dns = [...(options.dns ?? DEFAULT_VZ_DNS)]\n if (dns.length < 1 || dns.length > 3) {\n throw new DeskError('guestNetwork.dns must list one to three IPv4 resolvers.')\n }\n return {\n allowLan: options.allowLan ?? false,\n guestAddress,\n gatewayAddress,\n prefixLength,\n dns: dns.map((entry) => ipv4(entry, 'guestNetwork.dns')),\n }\n}\n\n/** Stable per desk, so a restored guest finds the NIC it was saved with. */\nfunction deskMacAddress(deskId: string): string {\n const digest = createHash('sha256').update(`appkit-desk:${deskId}`).digest()\n const octets = [0x06, ...digest.subarray(0, 5)]\n return octets.map((octet) => octet.toString(16).padStart(2, '0')).join(':')\n}\n\nfunction socketPath(path: string): string {\n if (Buffer.byteLength(path) > DARWIN_SOCKET_PATH_LIMIT) {\n throw new DeskError(\n `Socket path ${path} is longer than the ${DARWIN_SOCKET_PATH_LIMIT} bytes macOS allows; `\n + 'use a shorter runtimeDir or deskId.',\n )\n }\n return path\n}\n\nfunction ipv4(value: string, field: string): string {\n if (typeof value !== 'string' || isIP(value) !== 4) {\n throw new DeskError(`${field} must be an IPv4 address.`)\n }\n return value\n}\n\nfunction ipv4Number(address: string): number {\n return address.split('.').reduce((value, octet) => ((value << 8) | Number(octet)) >>> 0, 0)\n}\n\nfunction defaultTapDevice(cid: number): string {\n return `dsk${cid}`\n}\n\nfunction defaultMacAddress(cid: number): string {\n // Locally administered, unicast prefix; the low four octets carry the CID so\n // the address is stable per desk and never collides across resident desks.\n const octets = [0x06, 0x00, (cid >>> 24) & 0xff, (cid >>> 16) & 0xff, (cid >>> 8) & 0xff, cid & 0xff]\n return octets.map((octet) => octet.toString(16).padStart(2, '0')).join(':')\n}\n\nfunction cleanCid(value: number): number {\n if (!Number.isInteger(value) || value < MIN_GUEST_CID || value > MAX_GUEST_CID) {\n throw new DeskError(\n `vsockCid must be an integer between ${MIN_GUEST_CID} and ${MAX_GUEST_CID}.`,\n )\n }\n return value\n}\n\nfunction cleanTapDevice(value: string): string {\n if (!TAP_DEVICE_PATTERN.test(value)) {\n throw new DeskError('tapDevice must be 1–15 characters of letters, numbers, underscores, or hyphens.')\n }\n return value\n}\n\nfunction cleanMacAddress(value: string): string {\n if (!MAC_ADDRESS_PATTERN.test(value)) {\n throw new DeskError(`macAddress is not a valid MAC address: ${value}`)\n }\n return value.toLowerCase()\n}\n\nexport function cleanLauncherIdentity(\n identity: DeskLauncherIdentity | undefined,\n): DeskLauncherIdentity | undefined {\n if (!identity) return undefined\n const max = 2_147_483_647\n if (!Number.isInteger(identity.uid) || identity.uid < 1 || identity.uid > max) {\n throw new DeskError(`launcherIdentity.uid must be an integer between 1 and ${max}.`)\n }\n if (!Number.isInteger(identity.gid) || identity.gid < 0 || identity.gid > max) {\n throw new DeskError(`launcherIdentity.gid must be an integer between 0 and ${max}.`)\n }\n return { uid: identity.uid, gid: identity.gid }\n}\n\nfunction absolutePath(value: string, field: string): string {\n if (!value || !isAbsolute(value)) {\n throw new DeskError(`${field} must be an absolute path.`)\n }\n return resolve(value)\n}\n\nfunction positiveInteger(value: number, field: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new DeskError(`${field} must be a positive integer.`)\n }\n return value\n}\n"]}
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from 'node:buffer'\nimport { createHash } from 'node:crypto'\nimport { existsSync, statSync } from 'node:fs'\nimport { isIP } from 'node:net'\nimport { arch as hostArch, release as hostRelease, tmpdir } from 'node:os'\nimport { delimiter, dirname, isAbsolute, join, resolve } from 'node:path'\n\nexport const DEFAULT_VMM_PATH = '/usr/bin/cloud-hypervisor'\nexport const DEFAULT_KVM_PATH = '/dev/kvm'\nexport const DEFAULT_QEMU_IMG_PATH = '/usr/bin/qemu-img'\nexport const DEFAULT_RUNTIME_DIR = '/run/appkit-desk'\nexport const DEFAULT_MEMORY_MB = 384\nexport const DEFAULT_VCPUS = 2\nexport const DEFAULT_KERNEL_CMDLINE = 'root=/dev/vda rw quiet'\n/**\n * Where a Cloud Hypervisor desk looks for `virtiofsd` when `virtiofsdPath` is\n * not set and it is not on `PATH`: Debian's and Fedora's package location.\n */\nexport const DEFAULT_VIRTIOFSD_PATH = '/usr/libexec/virtiofsd'\nexport const DEFAULT_VIRTIOFSD_SANDBOX: DeskVirtiofsdSandbox = 'namespace'\n/**\n * The guest network Virtualization.framework desks get from the helper's NAT:\n * the same addresses QEMU's user networking uses, so they are unsurprising in\n * a guest. Nothing outside the helper ever sees them.\n */\nexport const DEFAULT_VZ_GUEST_ADDRESS = '10.0.2.15'\nexport const DEFAULT_VZ_GATEWAY_ADDRESS = '10.0.2.2'\nexport const DEFAULT_VZ_PREFIX_LENGTH = 24\nexport const DEFAULT_VZ_DNS: readonly string[] = ['1.1.1.1', '9.9.9.9']\n/** Virtualization.framework always gives the guest vsock context ID 3. */\nexport const VZ_GUEST_CID = 3\n/** The first Darwin release with Virtualization.framework save and restore (macOS 14). */\nconst MIN_DARWIN_MAJOR = 23\n/** `sun_path` holds 104 bytes on Darwin, including the terminator. */\nconst DARWIN_SOCKET_PATH_LIMIT = 103\n/** `sun_path` holds 108 bytes on Linux, including the terminator. */\nconst LINUX_SOCKET_PATH_LIMIT = 107\n/** Linux's virtio-fs tag field is 36 bytes; Virtualization.framework enforces the same. */\nconst SHARE_TAG_PATTERN = /^[a-z0-9][a-z0-9-]{0,35}$/\n\nconst DESK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/\nconst TAP_DEVICE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,14}$/\nconst MAC_ADDRESS_PATTERN = /^[0-9a-f]{2}(?::[0-9a-f]{2}){5}$/i\n/** vsock CIDs 0–2 are reserved (hypervisor, loopback, host). */\nconst MIN_GUEST_CID = 3\nconst MAX_GUEST_CID = 0xffff_fffe\n\n/**\n * Every failure this package raises. The `name` is annotated as `string`\n * rather than left to infer its literal so a subclass can narrow the meaning\n * of a failure — see `DeskRequestFateUnknownError` — without callers losing\n * the one `instanceof DeskError` they already check.\n */\nexport class DeskError extends Error {\n override readonly name: string = 'DeskError'\n}\n\nexport interface DeskLauncherIdentity {\n /** Host/container UID used to invoke the VMM and image tooling. */\n uid: number\n /** Host/container GID used to invoke the VMM and image tooling. */\n gid: number\n}\n\n/**\n * Which hypervisor a plan is for: Cloud Hypervisor over KVM on Linux, or\n * Apple's Virtualization.framework through the `appkit-desk-vz` helper on an\n * Apple silicon Mac.\n */\nexport type DeskHypervisor = 'cloud-hypervisor' | 'virtualization'\n\n/**\n * The guest network of a Virtualization.framework desk. The helper runs a\n * userspace NAT: the guest reaches the public internet and nothing else unless\n * `allowLan` is set — no RFC 1918 space, link-local, CGNAT, or the Mac itself.\n */\nexport interface DeskGuestNetworkOptions {\n /** Let the guest reach the Mac's LAN, link-local and CGNAT space, and the Mac. */\n allowLan?: boolean\n /** The address the helper's DHCP server leases the guest. */\n guestAddress?: string\n /** The helper's own address on the link: the guest's router. */\n gatewayAddress?: string\n prefixLength?: number\n /** Resolvers the guest is handed; the first also answers DNS sent to the gateway. */\n dns?: readonly string[]\n}\n\n/**\n * A host directory shared into the guest over virtio-fs. The guest mounts it\n * with `mount -t virtiofs <tag> <dir>`.\n */\nexport interface DeskShare {\n /** 1–36 lowercase letters, digits, or hyphens, starting with a letter or digit; unique per desk. */\n tag: string\n /** Absolute path of an existing host directory. */\n path: string\n /**\n * Refuse every write from the guest. Enforced by the host's file server, so\n * a guest cannot lift it by remounting the share read-write.\n */\n readOnly: boolean\n}\n\n/**\n * How `virtiofsd` confines itself to the shared directory. `namespace` (its\n * own default) pivots into the directory inside new mount, PID and network\n * namespaces, which needs CAP_SYS_ADMIN when run as root and unprivileged user\n * namespaces otherwise. `chroot` only chroots, which needs root and suits a\n * container that may not create namespaces. Both stop symlinks from escaping\n * the share; running unconfined is not offered.\n */\nexport type DeskVirtiofsdSandbox = 'namespace' | 'chroot'\n\n/** An immutable host image. The caller must retain its bytes and path for every snapshot. */\nexport interface DeskReadOnlyAttachment {\n path: string\n /** SHA-256 of the immutable image, used as snapshot identity. */\n sha256: string\n kind: 'block' | 'pmem'\n}\n\nexport interface DeskLaunchPlanOptions {\n deskId: string\n /** Defaults to `cloud-hypervisor`. */\n hypervisor?: DeskHypervisor\n /** Cloud Hypervisor only: the VMM binary. */\n vmmPath?: string\n /** Cloud Hypervisor only: the KVM device. */\n kvmPath?: string\n qemuImgPath?: string\n /** Absolute path of the kernel image the microVM boots. */\n kernelPath: string\n /**\n * Absolute path of an initramfs. Required whenever the kernel loads its\n * root-filesystem or virtio drivers as modules — a stock distro cloud kernel\n * panics with \"VFS: Unable to mount root fs\" if it boots without one. Omit\n * only for a kernel with virtio_blk/ext4 built in.\n */\n initramfsPath?: string\n /** Absolute path of the golden raw base image every desk clones. */\n baseImagePath: string\n /** Absolute path of this desk's copy-on-write overlay (a raw reflink clone). */\n overlayPath: string\n memoryMb?: number\n /** Cloud Hypervisor virtio-balloon with free-page reporting, size zero. */\n freePageReporting?: boolean\n vcpus?: number\n /**\n * Guest vsock context ID; must be unique among resident desks. Cloud\n * Hypervisor only — a Virtualization.framework guest is always CID 3, and\n * its vsock is private to its helper.\n */\n vsockCid: number\n /**\n * Directory holding the desk's control sockets. Defaults to\n * `/run/appkit-desk` for Cloud Hypervisor and `<os tmpdir>/appkit-desk` for\n * Virtualization.framework.\n */\n runtimeDir?: string\n /** Cloud Hypervisor only: host TAP device name; derived from the CID when omitted. */\n tapDevice?: string\n /**\n * Guest MAC address. Derived from the CID for Cloud Hypervisor, and from the\n * desk ID for Virtualization.framework, whose restore needs the same NIC.\n */\n macAddress?: string\n /** Virtualization.framework only: the helper's NAT network and fence. */\n guestNetwork?: DeskGuestNetworkOptions\n /**\n * Host directories shared into the guest. Restoring a snapshot requires the\n * same shares the desk was parked with; see the backends.\n */\n shares?: readonly DeskShare[]\n /** Additional immutable Cloud Hypervisor devices, in guest enumeration order. */\n readOnlyAttachments?: readonly DeskReadOnlyAttachment[]\n /**\n * Cloud Hypervisor only: the `virtiofsd` binary (1.13 or later) that serves\n * each share. Defaults to the first `virtiofsd` on `PATH`, then\n * `/usr/libexec/virtiofsd`. Not needed, and not looked for, without shares.\n */\n virtiofsdPath?: string\n /** Cloud Hypervisor only: defaults to `namespace`. */\n virtiofsdSandbox?: DeskVirtiofsdSandbox\n kernelCmdline?: string\n launcherIdentity?: DeskLauncherIdentity\n /**\n * Where this desk's memory snapshot lives. Defaults beside the overlay, which\n * is the volume sized for desk state — a snapshot is roughly `memoryMb` of\n * guest RAM, so it does not belong on a runtime tmpfs.\n */\n snapshotDir?: string\n /**\n * Resume from that snapshot instead of booting the kernel.\n *\n * A restored VM continues with its process tree intact, which is the whole\n * point of parking with memory rather than shutting down: an agent's in-guest\n * daemon survives. Refused when the snapshot is not there, rather than falling\n * back to a cold boot — a silent cold boot is how a desk loses the state it\n * was promised to have kept.\n */\n restore?: boolean\n /** Cloud Hypervisor copy-on-write restore; the source must remain unchanged while resident. */\n memoryRestoreMode?: 'copyonwrite' | 'ondemand'\n}\n\n/** What every plan carries, whichever hypervisor runs it. */\nexport interface DeskLaunchPlanBase {\n deskId: string\n overlay: {\n path: string\n basePath: string\n /** The overlay-creation step, or `null` when the overlay already exists. */\n create: { command: string; args: string[] } | null\n }\n /**\n * The host Unix socket the guest's vsock is reached through, with Cloud\n * Hypervisor's hybrid-vsock preamble: write `CONNECT <port>\\n`, read\n * `OK <n>\\n`, then the stream is the guest connection.\n */\n vsock: { cid: number; socketPath: string }\n /** Where a memory snapshot of this desk is written, and read back from. */\n snapshot: { dir: string; restoring: boolean; requirePrivateMemoryMapping?: boolean }\n /** Host directories shared into the guest, in the order they were given. */\n shares: readonly DeskShare[]\n memoryMb: number\n vcpus: number\n kernelPath: string\n /** Resolved initramfs path, or `null` when the kernel needs none. */\n initramfsPath: string | null\n kernelCmdline: string\n launcherIdentity?: DeskLauncherIdentity\n}\n\n/**\n * The complete Cloud Hypervisor invocation as inspectable data. Nothing here\n * has been spawned: tests, audit logs, and readiness screens can assert on the\n * exact argv a desk would boot with.\n */\nexport interface CloudHypervisorLaunchPlan extends DeskLaunchPlanBase {\n hypervisor: 'cloud-hypervisor'\n vmm: { command: string; args: string[] }\n api: { socketPath: string }\n tap: { device: string; mac: string }\n /**\n * Each share with the `virtiofsd` that serves it. Every daemon must be\n * listening on its socket before the VMM starts, for a boot and for a\n * restore alike.\n */\n shares: readonly CloudHypervisorShare[]\n}\n\nexport interface CloudHypervisorShare extends DeskShare {\n /** The vhost-user socket the daemon listens on and the VMM connects to. */\n socketPath: string\n daemon: { command: string; args: string[] }\n}\n\n/**\n * A Virtualization.framework desk as inspectable data: what the helper is\n * asked to boot. The helper path belongs to the backend, not the plan.\n */\nexport interface VirtualizationLaunchPlan extends DeskLaunchPlanBase {\n hypervisor: 'virtualization'\n /**\n * The host Unix socket guest TCP ports are reached through, with the same\n * preamble as `vsock`: `CONNECT <tcp port>\\n` answered by `OK <n>\\n` once\n * the guest accepts, or a close with nothing written when it refuses.\n */\n ports: { socketPath: string }\n network: {\n mac: string\n allowLan: boolean\n guestAddress: string\n gatewayAddress: string\n prefixLength: number\n dns: string[]\n }\n}\n\nexport type DeskLaunchPlan = CloudHypervisorLaunchPlan | VirtualizationLaunchPlan\n\n/**\n * Whether this host can run desks at all. On Linux: a usable `/dev/kvm` and\n * the Cloud Hypervisor binary. On macOS: Apple silicon, macOS 14 or later (the\n * first with Virtualization.framework save and restore), and an existing\n * `appkit-desk-vz` helper at `helperPath`. Signature and entitlement are checked\n * by macOS on execution, not by this probe. Every check is injectable and the\n * answer is fail-closed — a host missing any of them gets no desk capability\n * rather than a degraded one.\n */\nexport function isDeskSupported(options: {\n platform?: NodeJS.Platform\n kvmPath?: string\n vmmPath?: string\n /** macOS: the signed `appkit-desk-vz` helper. Required there. */\n helperPath?: string\n arch?: string\n /** The kernel release, as `os.release()` reports it. */\n osRelease?: string\n pathExists?: (path: string) => boolean\n} = {}): boolean {\n const platform = options.platform ?? process.platform\n const pathExists = options.pathExists ?? existsSync\n if (platform === 'darwin') {\n const major = Number.parseInt((options.osRelease ?? hostRelease()).split('.')[0] ?? '', 10)\n return (\n (options.arch ?? hostArch()) === 'arm64'\n && Number.isInteger(major)\n && major >= MIN_DARWIN_MAJOR\n && options.helperPath !== undefined\n && isAbsolute(options.helperPath)\n && pathExists(options.helperPath)\n )\n }\n return (\n platform === 'linux'\n && pathExists(options.kvmPath ?? DEFAULT_KVM_PATH)\n && pathExists(options.vmmPath ?? DEFAULT_VMM_PATH)\n )\n}\n\n/** Validate a desk identifier, which is used in socket paths and device names. */\nexport function cleanDeskId(value: string): string {\n if (!DESK_ID_PATTERN.test(value)) {\n throw new DeskError(\n 'deskId must be 1–64 letters, numbers, dots, underscores, or hyphens.',\n )\n }\n return value\n}\n\nexport interface DeskLaunchPlanDependencies {\n pathExists?: (path: string) => boolean\n deviceExists?: (path: string) => boolean\n /** Whether a path is an existing directory; checked for every share. */\n isDirectory?: (path: string) => boolean\n /** Searched for `virtiofsd` when `virtiofsdPath` is not set; defaults to `process.env.PATH`. */\n searchPath?: string\n}\n\n/**\n * Build the deterministic launch plan without spawning anything. Fail closed:\n * a missing `/dev/kvm` or VMM binary (Cloud Hypervisor), kernel, base image,\n * or overlay directory throws instead of producing a plan that cannot boot.\n */\nexport function buildDeskLaunchPlan(\n options: DeskLaunchPlanOptions & { hypervisor: 'virtualization' },\n dependencies?: DeskLaunchPlanDependencies,\n): VirtualizationLaunchPlan\nexport function buildDeskLaunchPlan(\n options: DeskLaunchPlanOptions & { hypervisor?: 'cloud-hypervisor' },\n dependencies?: DeskLaunchPlanDependencies,\n): CloudHypervisorLaunchPlan\nexport function buildDeskLaunchPlan(\n options: DeskLaunchPlanOptions,\n dependencies?: DeskLaunchPlanDependencies,\n): DeskLaunchPlan\nexport function buildDeskLaunchPlan(\n options: DeskLaunchPlanOptions,\n dependencies: DeskLaunchPlanDependencies = {},\n): DeskLaunchPlan {\n const hypervisor = options.hypervisor ?? 'cloud-hypervisor'\n if (options.memoryRestoreMode !== undefined && (!['copyonwrite', 'ondemand'].includes(options.memoryRestoreMode) || hypervisor !== 'cloud-hypervisor')) {\n throw new DeskError('Memory restore mode requires Cloud Hypervisor and must be copyonwrite or ondemand.')\n }\n if (hypervisor === 'virtualization') return buildVirtualizationPlan(options, dependencies)\n if (hypervisor !== 'cloud-hypervisor') {\n throw new DeskError(`Unknown hypervisor: ${String(hypervisor)}`)\n }\n if (options.guestNetwork !== undefined) {\n throw new DeskError(\n 'guestNetwork applies to Virtualization.framework desks; a Cloud Hypervisor desk is networked with createDeskNetwork.',\n )\n }\n return buildCloudHypervisorPlan(options, dependencies)\n}\n\nfunction buildCloudHypervisorPlan(\n options: DeskLaunchPlanOptions,\n dependencies: DeskLaunchPlanDependencies,\n): CloudHypervisorLaunchPlan {\n const pathExists = dependencies.pathExists ?? existsSync\n const deviceExists = dependencies.deviceExists ?? existsSync\n const isDirectory = dependencies.isDirectory ?? directoryExists\n const deskId = cleanDeskId(options.deskId)\n const kvmPath = absolutePath(options.kvmPath ?? DEFAULT_KVM_PATH, 'kvmPath')\n if (!deviceExists(kvmPath)) {\n throw new DeskError(\n `KVM is not available at ${kvmPath}. Desks require hardware virtualization; `\n + 'there is no software-emulation fallback.',\n )\n }\n const vmmPath = absolutePath(options.vmmPath ?? DEFAULT_VMM_PATH, 'vmmPath')\n if (!pathExists(vmmPath)) {\n throw new DeskError(\n `Cloud Hypervisor was not found at ${vmmPath}. Install cloud-hypervisor or configure vmmPath.`,\n )\n }\n const kernelPath = absolutePath(options.kernelPath, 'kernelPath')\n if (!pathExists(kernelPath)) {\n throw new DeskError(`Guest kernel does not exist: ${kernelPath}`)\n }\n const initramfsPath =\n options.initramfsPath === undefined\n ? null\n : absolutePath(options.initramfsPath, 'initramfsPath')\n if (initramfsPath !== null && !pathExists(initramfsPath)) {\n throw new DeskError(`Initramfs does not exist: ${initramfsPath}`)\n }\n const baseImagePath = absolutePath(options.baseImagePath, 'baseImagePath')\n if (!pathExists(baseImagePath)) {\n throw new DeskError(`Base image does not exist: ${baseImagePath}`)\n }\n const overlayPath = absolutePath(options.overlayPath, 'overlayPath')\n const overlayDirectory = dirname(overlayPath)\n if (!pathExists(overlayDirectory)) {\n throw new DeskError(`Overlay directory does not exist: ${overlayDirectory}`)\n }\n const memoryMb = positiveInteger(options.memoryMb ?? DEFAULT_MEMORY_MB, 'memoryMb')\n const vcpus = positiveInteger(options.vcpus ?? DEFAULT_VCPUS, 'vcpus')\n const vsockCid = cleanCid(options.vsockCid)\n const runtimeDir = absolutePath(options.runtimeDir ?? DEFAULT_RUNTIME_DIR, 'runtimeDir')\n const tapDevice = cleanTapDevice(options.tapDevice ?? defaultTapDevice(vsockCid))\n const macAddress = cleanMacAddress(options.macAddress ?? defaultMacAddress(vsockCid))\n const kernelCmdline = options.kernelCmdline ?? DEFAULT_KERNEL_CMDLINE\n const launcherIdentity = cleanLauncherIdentity(options.launcherIdentity)\n const vsockSocketPath = join(runtimeDir, `desk-${deskId}.vsock`)\n const apiSocketPath = join(runtimeDir, `desk-${deskId}.api.sock`)\n // Beside the overlay by default: a snapshot is the guest's RAM, so it belongs\n // on the volume provisioned for desk state rather than on a runtime tmpfs.\n const snapshotDir = options.snapshotDir\n ? absolutePath(options.snapshotDir, 'snapshotDir')\n : join(overlayDirectory, `${deskId}.snapshot`)\n const restoring = options.restore === true\n if (restoring && !pathExists(snapshotDir)) {\n // Refused rather than silently cold-booted. A caller asking to restore has\n // been told this desk kept its memory; quietly booting a fresh kernel would\n // lose the process tree it was relying on and look like a crash inside the\n // guest instead of a missing snapshot out here.\n throw new DeskError(`Desk ${deskId} has no snapshot to restore at ${snapshotDir}`)\n }\n const attachments = cleanAttachments(options.readOnlyAttachments, pathExists)\n const shares = cloudHypervisorShares(cleanShares(options.shares, isDirectory), {\n deskId,\n runtimeDir,\n virtiofsdPath: options.virtiofsdPath,\n sandbox: options.virtiofsdSandbox ?? DEFAULT_VIRTIOFSD_SANDBOX,\n pathExists,\n searchPath: dependencies.searchPath ?? process.env.PATH ?? '',\n })\n\n const create = pathExists(overlayPath)\n ? null\n : {\n // Cloud Hypervisor cannot follow disk backing chains: a qcow2 overlay\n // over a qcow2/raw base is rejected with UnsupportedFeature /\n // MaxNestingDepthExceeded. So the overlay is a plain raw file, cloned\n // from the raw base with `cp --reflink=auto` — an instant, block-sharing\n // copy-on-write clone on XFS/Btrfs and a graceful full-copy fallback on\n // ext4. Either way CH boots the resulting raw file with no backing chain.\n command: 'cp',\n args: ['--reflink=auto', baseImagePath, overlayPath],\n }\n\n // A restoring VMM is handed the snapshot and nothing else.\n //\n // The snapshot carries the whole machine config — memory size, cpus, disk,\n // vsock, net — so passing `--kernel`/`--disk` alongside `--restore` is not\n // redundant, it is rejected.\n //\n // `resume=false` is deliberate, and it is the difference between this being\n // safe and being a data-loss bug. A memory snapshot is only valid against the\n // disk as it stood when the snapshot was taken. Resume the guest in this same\n // spawn and it starts writing immediately, so for as long as the snapshot\n // still exists there are two valid-looking pasts for one desk — and a host\n // that died in that window would, on the next start, replay this memory\n // against a disk that had moved on. That is corruption, not a stale cache.\n //\n // The guest comes back PAUSED; before it runs, the caller must make the\n // snapshot unavailable for replay. File-backed restores keep their retired\n // source unchanged until the VMM exits.\n //\n // The tap is reopened BY NAME from the snapshotted config. That is the detail\n // that makes this possible at all: Cloud Hypervisor's `net_fds` restore\n // parameter exists for VMs whose tap was passed as a file descriptor, and\n // producing one from Node would mean an ioctl(TUNSETIFF) it cannot make.\n // Because `deskPlan` configures `--net tap=<name>`, there is nothing to pass.\n //\n // Shares are in the snapshotted config too, by socket path, with each\n // daemon's state. What the snapshot cannot hold is a running `virtiofsd`, so\n // the backend starts the same daemons on the same sockets before this VMM\n // either way, and the VMM loads their state back into them.\n const args = restoring\n ? [\n '--api-socket', apiSocketPath,\n '--restore', `source_url=file://${snapshotDir},resume=false${options.memoryRestoreMode ? `,memory_restore_mode=${options.memoryRestoreMode}` : ''}`,\n ]\n : [\n '--api-socket', apiSocketPath,\n '--kernel', kernelPath,\n // A modular distro kernel needs its initramfs to bring up virtio_blk and\n // mount root; omitted only for a kernel with those drivers built in.\n ...(initramfsPath === null ? [] : ['--initramfs', initramfsPath]),\n '--cmdline', kernelCmdline,\n // Be explicit that the disk is raw: CH deprecated image-format auto-detection\n // and warns without image_type; there is never a qcow2 overlay here.\n '--disk', `path=${overlayPath},image_type=raw`, ...attachments.filter(a => a.kind === 'block').map(a => `path=${a.path},readonly=on,image_type=raw`),\n ...attachments.filter(a => a.kind === 'pmem').flatMap(a => ['--pmem', `file=${a.path},discard_writes=on`]),\n // vhost-user devices map guest RAM into their daemon, so it must be\n // shared memory; a desk without shares keeps private memory.\n '--memory', `size=${memoryMb}M${shares.length === 0 ? '' : ',shared=on'}`,\n ...(options.freePageReporting ? ['--balloon', 'size=0,free_page_reporting=on'] : []),\n '--cpus', `boot=${vcpus}`,\n '--vsock', `cid=${vsockCid},socket=${vsockSocketPath}`,\n '--net', `tap=${tapDevice},mac=${macAddress}`,\n ...(shares.length === 0\n ? []\n : ['--fs', ...shares.map((share) => `tag=${share.tag},socket=${share.socketPath}`)]),\n '--serial', 'off',\n '--console', 'off',\n ]\n\n return {\n hypervisor: 'cloud-hypervisor',\n deskId,\n vmm: { command: vmmPath, args },\n overlay: { path: overlayPath, basePath: baseImagePath, create },\n vsock: { cid: vsockCid, socketPath: vsockSocketPath },\n api: { socketPath: apiSocketPath },\n tap: { device: tapDevice, mac: macAddress },\n snapshot: { dir: snapshotDir, restoring },\n shares,\n memoryMb,\n vcpus,\n kernelPath,\n initramfsPath,\n kernelCmdline,\n launcherIdentity,\n }\n}\n\n/**\n * The Virtualization.framework plan. The same fail-closed checks on the guest\n * assets; no KVM or VMM binary, because the backend owns the helper; and the\n * overlay is an APFS clone (`cp -c`), which is to APFS what `--reflink=auto`\n * is to XFS: instant and block-sharing. A volume that cannot clone must be\n * replaced with one that supports it.\n */\nfunction buildVirtualizationPlan(\n options: DeskLaunchPlanOptions,\n dependencies: DeskLaunchPlanDependencies,\n): VirtualizationLaunchPlan {\n const pathExists = dependencies.pathExists ?? existsSync\n if (options.readOnlyAttachments?.length) throw new DeskError('Read-only attachments require Cloud Hypervisor.')\n if (options.freePageReporting) throw new DeskError('Free-page reporting requires Cloud Hypervisor.')\n const deskId = cleanDeskId(options.deskId)\n if (options.tapDevice !== undefined) {\n throw new DeskError('tapDevice applies to Cloud Hypervisor desks; a Virtualization.framework desk has no TAP.')\n }\n if (options.virtiofsdPath !== undefined || options.virtiofsdSandbox !== undefined) {\n throw new DeskError(\n 'virtiofsdPath and virtiofsdSandbox apply to Cloud Hypervisor desks; Virtualization.framework serves shares itself.',\n )\n }\n const kernelPath = absolutePath(options.kernelPath, 'kernelPath')\n if (!pathExists(kernelPath)) {\n throw new DeskError(`Guest kernel does not exist: ${kernelPath}`)\n }\n const initramfsPath =\n options.initramfsPath === undefined ? null : absolutePath(options.initramfsPath, 'initramfsPath')\n if (initramfsPath !== null && !pathExists(initramfsPath)) {\n throw new DeskError(`Initramfs does not exist: ${initramfsPath}`)\n }\n const baseImagePath = absolutePath(options.baseImagePath, 'baseImagePath')\n if (!pathExists(baseImagePath)) {\n throw new DeskError(`Base image does not exist: ${baseImagePath}`)\n }\n const overlayPath = absolutePath(options.overlayPath, 'overlayPath')\n const overlayDirectory = dirname(overlayPath)\n if (!pathExists(overlayDirectory)) {\n throw new DeskError(`Overlay directory does not exist: ${overlayDirectory}`)\n }\n const memoryMb = positiveInteger(options.memoryMb ?? DEFAULT_MEMORY_MB, 'memoryMb')\n const vcpus = positiveInteger(options.vcpus ?? DEFAULT_VCPUS, 'vcpus')\n const runtimeDir = absolutePath(options.runtimeDir ?? join(tmpdir(), 'appkit-desk'), 'runtimeDir')\n // Keep Darwin's small sockaddr_un limit even for a maximum-length desk ID.\n const socketId = createHash('sha256').update(deskId).digest('hex').slice(0, 20)\n const vsockSocketPath = socketPath(join(runtimeDir, `d-${socketId}.vsock`))\n const portsSocketPath = socketPath(join(runtimeDir, `d-${socketId}.ports`))\n const snapshotDir = options.snapshotDir\n ? absolutePath(options.snapshotDir, 'snapshotDir')\n : join(overlayDirectory, `${deskId}.snapshot`)\n const restoring = options.restore === true\n if (restoring && !pathExists(snapshotDir)) {\n throw new DeskError(`Desk ${deskId} has no snapshot to restore at ${snapshotDir}`)\n }\n const network = cleanGuestNetwork(options.guestNetwork ?? {})\n const shares = cleanShares(options.shares, dependencies.isDirectory ?? directoryExists)\n return {\n hypervisor: 'virtualization',\n deskId,\n overlay: {\n path: overlayPath,\n basePath: baseImagePath,\n create: pathExists(overlayPath) ? null : { command: 'cp', args: ['-c', baseImagePath, overlayPath] },\n },\n vsock: { cid: VZ_GUEST_CID, socketPath: vsockSocketPath },\n ports: { socketPath: portsSocketPath },\n network: {\n mac: cleanMacAddress(options.macAddress ?? deskMacAddress(deskId)),\n ...network,\n },\n snapshot: { dir: snapshotDir, restoring },\n shares,\n memoryMb,\n vcpus,\n kernelPath,\n initramfsPath,\n kernelCmdline: options.kernelCmdline ?? DEFAULT_KERNEL_CMDLINE,\n launcherIdentity: cleanLauncherIdentity(options.launcherIdentity),\n }\n}\n\n/** Reject ambiguous Cloud Hypervisor key-value arguments and unpinned images. */\nexport function cleanAttachments(attachments: readonly DeskReadOnlyAttachment[] | undefined, pathExists: (path: string) => boolean): DeskReadOnlyAttachment[] {\n if (attachments === undefined) return []\n if (!Array.isArray(attachments)) throw new DeskError('readOnlyAttachments must be an array.')\n return attachments.map((attachment, index) => {\n if (!attachment || !['block', 'pmem'].includes(attachment.kind) || !/^[a-f0-9]{64}$/.test(attachment.sha256)) throw new DeskError(`Invalid readOnlyAttachments[${index}].`)\n const path = absolutePath(attachment.path, `readOnlyAttachments[${index}].path`)\n if (path.includes(',') || !pathExists(path)) throw new DeskError(`Invalid attachment path: ${path}`)\n return { path, kind: attachment.kind, sha256: attachment.sha256 }\n })\n}\n\n/** Validate shares as given: tags, absolute existing directories, explicit access. */\nfunction cleanShares(\n shares: readonly DeskShare[] | undefined,\n isDirectory: (path: string) => boolean,\n): DeskShare[] {\n if (shares === undefined) return []\n if (!Array.isArray(shares)) throw new DeskError('shares must be an array.')\n const tags = new Set<string>()\n return shares.map((share: DeskShare, index) => {\n const field = `shares[${index}]`\n if (typeof share !== 'object' || share === null) throw new DeskError(`${field} must be an object.`)\n if (typeof share.tag !== 'string' || !SHARE_TAG_PATTERN.test(share.tag)) {\n throw new DeskError(\n `${field}.tag must be 1–36 lowercase letters, digits, or hyphens, starting with a letter or digit.`,\n )\n }\n if (tags.has(share.tag)) throw new DeskError(`${field}.tag ${share.tag} is already used by another share.`)\n tags.add(share.tag)\n if (typeof share.path !== 'string') throw new DeskError(`${field}.path must be an absolute path.`)\n const path = absolutePath(share.path, `${field}.path`)\n if (!isDirectory(path)) throw new DeskError(`${field}.path is not an existing directory: ${path}`)\n if (typeof share.readOnly !== 'boolean') throw new DeskError(`${field}.readOnly must be a boolean.`)\n return { tag: share.tag, path, readOnly: share.readOnly }\n })\n}\n\n/**\n * One `virtiofsd` per share. `--cache=auto` lets the guest cache for a second\n * at a time, so changes the host makes under a share reach running guests;\n * `always` would let a guest keep serving a file the host has replaced.\n * `--readonly` is the enforcement for a read-only share — the guest is never\n * trusted to honour a read-only mount.\n *\n * The migration flags are for parking. Cloud Hypervisor carries the daemon's\n * state — the files and directories the guest holds — inside a snapshot, and\n * hands it to the fresh daemon on restore, which reopens each by path. A file\n * that has no path by then (unlinked on the host, or by the guest while held\n * open) becomes an error for the guest to see on that file alone, rather than\n * a restore that fails and costs the whole desk its memory.\n */\nfunction cloudHypervisorShares(\n shares: readonly DeskShare[],\n context: {\n deskId: string\n runtimeDir: string\n virtiofsdPath: string | undefined\n sandbox: DeskVirtiofsdSandbox\n pathExists: (path: string) => boolean\n searchPath: string\n },\n): CloudHypervisorShare[] {\n if (shares.length === 0) return []\n if (context.sandbox !== 'namespace' && context.sandbox !== 'chroot') {\n throw new DeskError(\"virtiofsdSandbox must be 'namespace' or 'chroot'.\")\n }\n const command = resolveVirtiofsd(context.virtiofsdPath, context.pathExists, context.searchPath)\n return shares.map((share) => {\n // A dot cannot appear in a tag, so `<desk>.<tag>` names one socket for\n // exactly one desk and share even though desk IDs may contain hyphens.\n const socketPath = join(context.runtimeDir, `desk-${context.deskId}.${share.tag}.fs.sock`)\n if (Buffer.byteLength(socketPath) > LINUX_SOCKET_PATH_LIMIT) {\n throw new DeskError(\n `Socket path ${socketPath} is longer than the ${LINUX_SOCKET_PATH_LIMIT} bytes Linux allows; `\n + 'use a shorter runtimeDir, deskId, or share tag.',\n )\n }\n return {\n ...share,\n socketPath,\n daemon: {\n command,\n args: [\n `--socket-path=${socketPath}`,\n `--shared-dir=${share.path}`,\n `--sandbox=${context.sandbox}`,\n '--cache=auto',\n '--migration-mode=find-paths',\n '--migration-on-error=guest-error',\n ...(share.readOnly ? ['--readonly'] : []),\n ],\n },\n }\n })\n}\n\nfunction resolveVirtiofsd(\n configured: string | undefined,\n pathExists: (path: string) => boolean,\n searchPath: string,\n): string {\n if (configured !== undefined) {\n const path = absolutePath(configured, 'virtiofsdPath')\n if (!pathExists(path)) {\n throw new DeskError(`virtiofsd was not found at ${path}. Install virtiofsd or configure virtiofsdPath.`)\n }\n return path\n }\n const candidates = [\n ...searchPath.split(delimiter).filter((entry) => isAbsolute(entry)).map((entry) => join(entry, 'virtiofsd')),\n DEFAULT_VIRTIOFSD_PATH,\n ]\n const found = candidates.find((candidate) => pathExists(candidate))\n if (found === undefined) {\n throw new DeskError(\n `virtiofsd was not found on PATH or at ${DEFAULT_VIRTIOFSD_PATH}. Install virtiofsd 1.13 or later, or configure virtiofsdPath.`,\n )\n }\n return found\n}\n\nfunction directoryExists(path: string): boolean {\n return statSync(path, { throwIfNoEntry: false })?.isDirectory() === true\n}\n\nfunction cleanGuestNetwork(\n options: DeskGuestNetworkOptions,\n): Omit<VirtualizationLaunchPlan['network'], 'mac'> {\n if (options.allowLan !== undefined && typeof options.allowLan !== 'boolean') {\n throw new DeskError('guestNetwork.allowLan must be a boolean.')\n }\n const guestAddress = ipv4(options.guestAddress ?? DEFAULT_VZ_GUEST_ADDRESS, 'guestNetwork.guestAddress')\n const gatewayAddress = ipv4(options.gatewayAddress ?? DEFAULT_VZ_GATEWAY_ADDRESS, 'guestNetwork.gatewayAddress')\n const prefixLength = options.prefixLength ?? DEFAULT_VZ_PREFIX_LENGTH\n if (!Number.isInteger(prefixLength) || prefixLength < 8 || prefixLength > 30) {\n throw new DeskError('guestNetwork.prefixLength must be an integer between 8 and 30.')\n }\n const mask = (0xffff_ffff << (32 - prefixLength)) >>> 0\n if (\n guestAddress === gatewayAddress\n || ((ipv4Number(guestAddress) & mask) >>> 0) !== ((ipv4Number(gatewayAddress) & mask) >>> 0)\n ) {\n throw new DeskError(\"guestNetwork.guestAddress must be a different address in the gateway's subnet.\")\n }\n const dns = [...(options.dns ?? DEFAULT_VZ_DNS)]\n if (dns.length < 1 || dns.length > 3) {\n throw new DeskError('guestNetwork.dns must list one to three IPv4 resolvers.')\n }\n return {\n allowLan: options.allowLan ?? false,\n guestAddress,\n gatewayAddress,\n prefixLength,\n dns: dns.map((entry) => ipv4(entry, 'guestNetwork.dns')),\n }\n}\n\n/** Stable per desk, so a restored guest finds the NIC it was saved with. */\nfunction deskMacAddress(deskId: string): string {\n const digest = createHash('sha256').update(`appkit-desk:${deskId}`).digest()\n const octets = [0x06, ...digest.subarray(0, 5)]\n return octets.map((octet) => octet.toString(16).padStart(2, '0')).join(':')\n}\n\nfunction socketPath(path: string): string {\n if (Buffer.byteLength(path) > DARWIN_SOCKET_PATH_LIMIT) {\n throw new DeskError(\n `Socket path ${path} is longer than the ${DARWIN_SOCKET_PATH_LIMIT} bytes macOS allows; `\n + 'use a shorter runtimeDir or deskId.',\n )\n }\n return path\n}\n\nfunction ipv4(value: string, field: string): string {\n if (typeof value !== 'string' || isIP(value) !== 4) {\n throw new DeskError(`${field} must be an IPv4 address.`)\n }\n return value\n}\n\nfunction ipv4Number(address: string): number {\n return address.split('.').reduce((value, octet) => ((value << 8) | Number(octet)) >>> 0, 0)\n}\n\nfunction defaultTapDevice(cid: number): string {\n return `dsk${cid}`\n}\n\nfunction defaultMacAddress(cid: number): string {\n // Locally administered, unicast prefix; the low four octets carry the CID so\n // the address is stable per desk and never collides across resident desks.\n const octets = [0x06, 0x00, (cid >>> 24) & 0xff, (cid >>> 16) & 0xff, (cid >>> 8) & 0xff, cid & 0xff]\n return octets.map((octet) => octet.toString(16).padStart(2, '0')).join(':')\n}\n\nfunction cleanCid(value: number): number {\n if (!Number.isInteger(value) || value < MIN_GUEST_CID || value > MAX_GUEST_CID) {\n throw new DeskError(\n `vsockCid must be an integer between ${MIN_GUEST_CID} and ${MAX_GUEST_CID}.`,\n )\n }\n return value\n}\n\nfunction cleanTapDevice(value: string): string {\n if (!TAP_DEVICE_PATTERN.test(value)) {\n throw new DeskError('tapDevice must be 1–15 characters of letters, numbers, underscores, or hyphens.')\n }\n return value\n}\n\nfunction cleanMacAddress(value: string): string {\n if (!MAC_ADDRESS_PATTERN.test(value)) {\n throw new DeskError(`macAddress is not a valid MAC address: ${value}`)\n }\n return value.toLowerCase()\n}\n\nexport function cleanLauncherIdentity(\n identity: DeskLauncherIdentity | undefined,\n): DeskLauncherIdentity | undefined {\n if (!identity) return undefined\n const max = 2_147_483_647\n if (!Number.isInteger(identity.uid) || identity.uid < 1 || identity.uid > max) {\n throw new DeskError(`launcherIdentity.uid must be an integer between 1 and ${max}.`)\n }\n if (!Number.isInteger(identity.gid) || identity.gid < 0 || identity.gid > max) {\n throw new DeskError(`launcherIdentity.gid must be an integer between 0 and ${max}.`)\n }\n return { uid: identity.uid, gid: identity.gid }\n}\n\nfunction absolutePath(value: string, field: string): string {\n if (!value || !isAbsolute(value)) {\n throw new DeskError(`${field} must be an absolute path.`)\n }\n return resolve(value)\n}\n\nfunction positiveInteger(value: number, field: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw new DeskError(`${field} must be a positive integer.`)\n }\n return value\n}\n\n/** Snapshot restore modes implemented with source lifetime and failure fencing. */\nexport const DESK_MEMORY_RESTORE_MODES = ['copyonwrite', 'ondemand'] as const\n/** Suspend hooks include positive completion after snapshot durability. */\nexport const DESK_PARK_HOOK_VERSION = 1 as const\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** True only when writable private mappings cover every byte of the source. */
|
|
2
|
+
export declare function hasPrivateMemoryMapping(maps: string, device: bigint, inode: bigint, size: bigint): boolean;
|
|
3
|
+
/** Check the real VMM mapping before guest resume; inability to inspect fails closed. */
|
|
4
|
+
export declare function assertPrivateMemoryMapping(pid: number | undefined, snapshot: string): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=template-mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-mapping.d.ts","sourceRoot":"","sources":["../src/template-mapping.ts"],"names":[],"mappings":"AAGA,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CA0B1G;AAED,yFAAyF;AACzF,wBAAsB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKzG"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/** True only when writable private mappings cover every byte of the source. */
|
|
4
|
+
export function hasPrivateMemoryMapping(maps, device, inode, size) {
|
|
5
|
+
if (size <= 0n)
|
|
6
|
+
return false;
|
|
7
|
+
const major = ((device >> 8n) & 0xfffn) | ((device >> 32n) & ~0xfffn);
|
|
8
|
+
const minor = (device & 0xffn) | ((device >> 12n) & ~0xffn);
|
|
9
|
+
const ranges = [];
|
|
10
|
+
for (const line of maps.split('\n')) {
|
|
11
|
+
const fields = line.trim().split(/\s+/);
|
|
12
|
+
if (!fields[0] || !fields[1]?.startsWith('rw-') || !fields[1].endsWith('p') || !fields[2] || !fields[3] || !fields[4])
|
|
13
|
+
continue;
|
|
14
|
+
const address = fields[0].split('-'), parts = fields[3].split(':');
|
|
15
|
+
if (address.length !== 2 || parts.length !== 2 || !address[0] || !address[1] || !parts[0] || !parts[1]
|
|
16
|
+
|| !address.every(part => /^[a-f0-9]+$/i.test(part)) || !parts.every(part => /^[a-f0-9]+$/i.test(part))
|
|
17
|
+
|| !/^[a-f0-9]+$/i.test(fields[2]) || !/^\d+$/.test(fields[4]))
|
|
18
|
+
continue;
|
|
19
|
+
if (BigInt(`0x${parts[0]}`) !== major || BigInt(`0x${parts[1]}`) !== minor || BigInt(fields[4]) !== inode)
|
|
20
|
+
continue;
|
|
21
|
+
const length = BigInt(`0x${address[1]}`) - BigInt(`0x${address[0]}`);
|
|
22
|
+
if (length <= 0n)
|
|
23
|
+
continue;
|
|
24
|
+
const offset = BigInt(`0x${fields[2]}`);
|
|
25
|
+
ranges.push({ start: offset, end: offset + length });
|
|
26
|
+
}
|
|
27
|
+
ranges.sort((a, b) => a.start < b.start ? -1 : a.start > b.start ? 1 : 0);
|
|
28
|
+
let covered = 0n;
|
|
29
|
+
for (const range of ranges) {
|
|
30
|
+
if (range.start > covered)
|
|
31
|
+
break;
|
|
32
|
+
if (range.end > covered)
|
|
33
|
+
covered = range.end;
|
|
34
|
+
if (covered >= size)
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
/** Check the real VMM mapping before guest resume; inability to inspect fails closed. */
|
|
40
|
+
export async function assertPrivateMemoryMapping(pid, snapshot) {
|
|
41
|
+
if (!pid || !Number.isSafeInteger(pid) || pid < 1)
|
|
42
|
+
throw new Error('Cannot identify the template VMM process');
|
|
43
|
+
const source = await stat(join(snapshot, 'memory-ranges'), { bigint: true });
|
|
44
|
+
const maps = await readFile(`/proc/${pid}/maps`, 'utf8');
|
|
45
|
+
if (!source.isFile() || !hasPrivateMemoryMapping(maps, source.dev, source.ino, source.size))
|
|
46
|
+
throw new Error('VMM did not privately map the template memory inode');
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=template-mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-mapping.js","sourceRoot":"","sources":["../src/template-mapping.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile, stat } from 'node:fs/promises'\nimport { join } from 'node:path'\n\n/** True only when writable private mappings cover every byte of the source. */\nexport function hasPrivateMemoryMapping(maps: string, device: bigint, inode: bigint, size: bigint): boolean {\n if (size <= 0n) return false\n const major = ((device >> 8n) & 0xfffn) | ((device >> 32n) & ~0xfffn)\n const minor = (device & 0xffn) | ((device >> 12n) & ~0xffn)\n const ranges: { start: bigint; end: bigint }[] = []\n for (const line of maps.split('\\n')) {\n const fields = line.trim().split(/\\s+/)\n if (!fields[0] || !fields[1]?.startsWith('rw-') || !fields[1].endsWith('p') || !fields[2] || !fields[3] || !fields[4]) continue\n const address = fields[0].split('-'), parts = fields[3].split(':')\n if (address.length !== 2 || parts.length !== 2 || !address[0] || !address[1] || !parts[0] || !parts[1]\n || !address.every(part => /^[a-f0-9]+$/i.test(part)) || !parts.every(part => /^[a-f0-9]+$/i.test(part))\n || !/^[a-f0-9]+$/i.test(fields[2]) || !/^\\d+$/.test(fields[4])) continue\n if (BigInt(`0x${parts[0]}`) !== major || BigInt(`0x${parts[1]}`) !== minor || BigInt(fields[4]) !== inode) continue\n const length = BigInt(`0x${address[1]}`) - BigInt(`0x${address[0]}`)\n if (length <= 0n) continue\n const offset = BigInt(`0x${fields[2]}`)\n ranges.push({ start: offset, end: offset + length })\n }\n ranges.sort((a, b) => a.start < b.start ? -1 : a.start > b.start ? 1 : 0)\n let covered = 0n\n for (const range of ranges) {\n if (range.start > covered) break\n if (range.end > covered) covered = range.end\n if (covered >= size) return true\n }\n return false\n}\n\n/** Check the real VMM mapping before guest resume; inability to inspect fails closed. */\nexport async function assertPrivateMemoryMapping(pid: number | undefined, snapshot: string): Promise<void> {\n if (!pid || !Number.isSafeInteger(pid) || pid < 1) throw new Error('Cannot identify the template VMM process')\n const source = await stat(join(snapshot, 'memory-ranges'), { bigint: true })\n const maps = await readFile(`/proc/${pid}/maps`, 'utf8')\n if (!source.isFile() || !hasPrivateMemoryMapping(maps, source.dev, source.ino, source.size)) throw new Error('VMM did not privately map the template memory inode')\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Host-side identities required to restore a child of a sealed snapshot. */
|
|
2
|
+
export interface DeskTemplateChildIdentity {
|
|
3
|
+
diskPath: string;
|
|
4
|
+
tap: string;
|
|
5
|
+
mac: string;
|
|
6
|
+
vsockCid: number;
|
|
7
|
+
vsockSocket: string;
|
|
8
|
+
}
|
|
9
|
+
type ObjectValue = Record<string, unknown>;
|
|
10
|
+
/**
|
|
11
|
+
* Construct a child-specific Cloud Hypervisor config without modifying the
|
|
12
|
+
* sealed input. This only plans host-side device paths. The guest's restored
|
|
13
|
+
* NIC state still contains the old MAC; the guest must set its new MAC and
|
|
14
|
+
* reset its identity while externally fenced before the child can be exposed.
|
|
15
|
+
*/
|
|
16
|
+
export declare function planTemplateConfig(input: unknown, child: DeskTemplateChildIdentity): ObjectValue;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=template-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-plan.d.ts","sourceRoot":"","sources":["../src/template-plan.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAO1C;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,yBAAyB,GAAG,WAAW,CAgChG"}
|
package/template-plan.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { isAbsolute } from 'node:path';
|
|
2
|
+
function object(value, name) {
|
|
3
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
4
|
+
throw new Error(`Invalid template ${name}`);
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
function path(value) { return isAbsolute(value) && !value.includes('\0'); }
|
|
8
|
+
/**
|
|
9
|
+
* Construct a child-specific Cloud Hypervisor config without modifying the
|
|
10
|
+
* sealed input. This only plans host-side device paths. The guest's restored
|
|
11
|
+
* NIC state still contains the old MAC; the guest must set its new MAC and
|
|
12
|
+
* reset its identity while externally fenced before the child can be exposed.
|
|
13
|
+
*/
|
|
14
|
+
export function planTemplateConfig(input, child) {
|
|
15
|
+
if (!path(child.diskPath) || !path(child.vsockSocket) || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,14}$/.test(child.tap)
|
|
16
|
+
|| !/^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$/i.test(child.mac)
|
|
17
|
+
|| (parseInt(child.mac.slice(0, 2), 16) & 1) !== 0
|
|
18
|
+
|| !Number.isSafeInteger(child.vsockCid) || child.vsockCid < 3 || child.vsockCid > 0xfffffffe) {
|
|
19
|
+
throw new Error('Invalid template child identity');
|
|
20
|
+
}
|
|
21
|
+
const config = structuredClone(object(input, 'config'));
|
|
22
|
+
const memory = object(config.memory, 'memory');
|
|
23
|
+
if (memory.shared || memory.hugepages || memory.mergeable || memory.hotplug_size || memory.hotplugged_size || memory.prefault || memory.zones || memory.file || memory.host_numa_node != null || memory.reserve || memory.hotplug_method === 'VirtioMem') {
|
|
24
|
+
throw new Error('Template memory cannot use a copy-on-write restore');
|
|
25
|
+
}
|
|
26
|
+
for (const name of ['fs', 'pmem', 'devices', 'user_devices', 'vdpa', 'gpu', 'tpm', 'pvmemcontrol']) {
|
|
27
|
+
const value = config[name];
|
|
28
|
+
if (Array.isArray(value) ? value.length !== 0 : value != null && value !== false)
|
|
29
|
+
throw new Error(`Unsupported template device: ${name}`);
|
|
30
|
+
}
|
|
31
|
+
if (!Array.isArray(config.disks) || config.disks.length < 1 || !Array.isArray(config.net) || config.net.length !== 1)
|
|
32
|
+
throw new Error('Template needs a writable disk and one network device');
|
|
33
|
+
for (const attachment of config.disks.slice(1)) {
|
|
34
|
+
const item = object(attachment, 'attachment');
|
|
35
|
+
if (item.readonly !== true || item.vhost_user || typeof item.path !== 'string' || !path(item.path) || item.path === child.diskPath)
|
|
36
|
+
throw new Error('Unsupported template attachment');
|
|
37
|
+
}
|
|
38
|
+
const disk = object(config.disks[0], 'disk');
|
|
39
|
+
const net = object(config.net[0], 'network');
|
|
40
|
+
const vsock = object(config.vsock, 'vsock');
|
|
41
|
+
if (disk.readonly || disk.vhost_user || net.vhost_user || net.fds || typeof disk.path !== 'string' || typeof net.tap !== 'string' || typeof vsock.socket !== 'string')
|
|
42
|
+
throw new Error('Unsupported template disk, network or vsock');
|
|
43
|
+
if (disk.path === child.diskPath || net.tap === child.tap || vsock.socket === child.vsockSocket || String(net.mac).toLowerCase() === child.mac.toLowerCase() || vsock.cid === child.vsockCid)
|
|
44
|
+
throw new Error('Child identity must differ from the template');
|
|
45
|
+
disk.path = child.diskPath;
|
|
46
|
+
net.tap = child.tap;
|
|
47
|
+
net.mac = child.mac;
|
|
48
|
+
vsock.cid = child.vsockCid;
|
|
49
|
+
vsock.socket = child.vsockSocket;
|
|
50
|
+
return config;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=template-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-plan.js","sourceRoot":"","sources":["../src/template-plan.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute } from 'node:path'\n\n/** Host-side identities required to restore a child of a sealed snapshot. */\nexport interface DeskTemplateChildIdentity {\n diskPath: string\n tap: string\n mac: string\n vsockCid: number\n vsockSocket: string\n}\n\ntype ObjectValue = Record<string, unknown>\nfunction object(value: unknown, name: string): ObjectValue {\n if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`Invalid template ${name}`)\n return value as ObjectValue\n}\nfunction path(value: string): boolean { return isAbsolute(value) && !value.includes('\\0') }\n\n/**\n * Construct a child-specific Cloud Hypervisor config without modifying the\n * sealed input. This only plans host-side device paths. The guest's restored\n * NIC state still contains the old MAC; the guest must set its new MAC and\n * reset its identity while externally fenced before the child can be exposed.\n */\nexport function planTemplateConfig(input: unknown, child: DeskTemplateChildIdentity): ObjectValue {\n if (!path(child.diskPath) || !path(child.vsockSocket) || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,14}$/.test(child.tap)\n || !/^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$/i.test(child.mac)\n || (parseInt(child.mac.slice(0, 2), 16) & 1) !== 0\n || !Number.isSafeInteger(child.vsockCid) || child.vsockCid < 3 || child.vsockCid > 0xfffffffe) {\n throw new Error('Invalid template child identity')\n }\n const config = structuredClone(object(input, 'config'))\n const memory = object(config.memory, 'memory')\n if (memory.shared || memory.hugepages || memory.mergeable || memory.hotplug_size || memory.hotplugged_size || memory.prefault || memory.zones || memory.file || memory.host_numa_node != null || memory.reserve || memory.hotplug_method === 'VirtioMem') {\n throw new Error('Template memory cannot use a copy-on-write restore')\n }\n for (const name of ['fs', 'pmem', 'devices', 'user_devices', 'vdpa', 'gpu', 'tpm', 'pvmemcontrol']) {\n const value = config[name]\n if (Array.isArray(value) ? value.length !== 0 : value != null && value !== false) throw new Error(`Unsupported template device: ${name}`)\n }\n if (!Array.isArray(config.disks) || config.disks.length < 1 || !Array.isArray(config.net) || config.net.length !== 1) throw new Error('Template needs a writable disk and one network device')\n for (const attachment of config.disks.slice(1)) {\n const item = object(attachment, 'attachment')\n if (item.readonly !== true || item.vhost_user || typeof item.path !== 'string' || !path(item.path) || item.path === child.diskPath) throw new Error('Unsupported template attachment')\n }\n const disk = object(config.disks[0], 'disk')\n const net = object(config.net[0], 'network')\n const vsock = object(config.vsock, 'vsock')\n if (disk.readonly || disk.vhost_user || net.vhost_user || net.fds || typeof disk.path !== 'string' || typeof net.tap !== 'string' || typeof vsock.socket !== 'string') throw new Error('Unsupported template disk, network or vsock')\n if (disk.path === child.diskPath || net.tap === child.tap || vsock.socket === child.vsockSocket || String(net.mac).toLowerCase() === child.mac.toLowerCase() || vsock.cid === child.vsockCid) throw new Error('Child identity must differ from the template')\n disk.path = child.diskPath\n net.tap = child.tap\n net.mac = child.mac\n vsock.cid = child.vsockCid\n vsock.socket = child.vsockSocket\n return config\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type DeskTemplateChildIdentity } from './template-plan.js';
|
|
2
|
+
/** Compatibility inputs must describe the actual VMM and host CPU, not labels. */
|
|
3
|
+
export interface DeskTemplateCompatibility {
|
|
4
|
+
vmmVersion: string;
|
|
5
|
+
vmmPath: string;
|
|
6
|
+
cpuSignature: string;
|
|
7
|
+
componentVersion: string;
|
|
8
|
+
baseImagePath: string;
|
|
9
|
+
kernelPath: string;
|
|
10
|
+
initramfsPath: string;
|
|
11
|
+
attachmentPaths: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Register a stopped, specially prepared template. The caller must ensure the
|
|
15
|
+
* guest was fenced and contains no user data or credentials; hashing cannot
|
|
16
|
+
* prove provenance. Every source file is kept read-only and protected against
|
|
17
|
+
* writers for as long as any child can map its memory.
|
|
18
|
+
*/
|
|
19
|
+
export declare function registerDeskTemplate(snapshot: string, diskPath: string, compatibility: DeskTemplateCompatibility): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Prepare a child in a new private directory, without starting it. The caller
|
|
22
|
+
* must hold a network/workload fence until guest reinitialization succeeds.
|
|
23
|
+
* A failed or interrupted child directory is deliberately left for recovery.
|
|
24
|
+
*/
|
|
25
|
+
export declare function forkDeskTemplate(snapshot: string, childDirectory: string, child: Omit<DeskTemplateChildIdentity, 'diskPath'> & {
|
|
26
|
+
deskId: string;
|
|
27
|
+
}, compatibility: DeskTemplateCompatibility): Promise<{
|
|
28
|
+
diskPath: string;
|
|
29
|
+
snapshotPath: string;
|
|
30
|
+
}>;
|
|
31
|
+
/** Revalidate the registered source and lifetime pin for a parked descendant. */
|
|
32
|
+
export declare function verifyDeskTemplateCompatibility(childDirectory: string, compatibility: DeskTemplateCompatibility): Promise<void>;
|
|
33
|
+
/** Revalidate an unconsumed prepared child after an interrupted host start. */
|
|
34
|
+
export declare function verifyDeskTemplateFork(childDirectory: string, child: Omit<DeskTemplateChildIdentity, 'diskPath'> & {
|
|
35
|
+
deskId: string;
|
|
36
|
+
}, compatibility: DeskTemplateCompatibility): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=template-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-registry.d.ts","sourceRoot":"","sources":["../src/template-registry.ts"],"names":[],"mappings":"AAKA,OAAO,EAAsB,KAAK,yBAAyB,EAAE,MAAM,iBAAiB,CAAA;AAGpF,kFAAkF;AAClF,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;CACnC;AA+BD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAStI;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,aAAa,EAAE,yBAAyB,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAqCvP;AAaD,iFAAiF;AACjF,wBAAsB,+BAA+B,CAAC,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAErI;AAED,+EAA+E;AAC/E,wBAAsB,sBAAsB,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,aAAa,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAcrM"}
|