@ghalex/kvbox-server 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/kvbox-server.js +109 -27
- package/dist/kvbox-server.js.map +1 -1
- package/package.json +3 -3
package/dist/kvbox-server.js
CHANGED
|
@@ -46,12 +46,17 @@ function paths(home = kvboxHome()) {
|
|
|
46
46
|
firecracker: join(home, "bin", "firecracker"),
|
|
47
47
|
assets: join(home, "assets"),
|
|
48
48
|
kernel: join(home, "assets", "vmlinux"),
|
|
49
|
+
/** Symlink to the current base image, assets/rootfs-<version>.ext4; boxes keep using the version they were made from. */
|
|
49
50
|
rootfs: join(home, "assets", "rootfs.ext4"),
|
|
51
|
+
baseImage: (version) => join(home, "assets", `rootfs-${version}.ext4`),
|
|
50
52
|
machines: join(home, "machines"),
|
|
51
53
|
snapshots: join(home, "snapshots"),
|
|
52
54
|
machine: (name) => ({
|
|
53
55
|
dir: join(home, "machines", name),
|
|
54
56
|
meta: join(home, "machines", name, "meta.json"),
|
|
57
|
+
/** The box's writable layer, stacked on the read-only base image inside the guest. */
|
|
58
|
+
overlay: join(home, "machines", name, "overlay.ext4"),
|
|
59
|
+
/** Boxes made before overlays: a full copy of the base image. Kept bootable, never created anymore. */
|
|
55
60
|
disk: join(home, "machines", name, "disk.ext4"),
|
|
56
61
|
sock: join(home, "machines", name, "fc.sock"),
|
|
57
62
|
log: join(home, "machines", name, "fc.log")
|
|
@@ -72,9 +77,9 @@ import { z } from "zod";
|
|
|
72
77
|
|
|
73
78
|
// ../shared/src/sizes.ts
|
|
74
79
|
var SIZES = {
|
|
75
|
-
s: { vcpu: 2, memMib: 4096 },
|
|
76
|
-
m: { vcpu: 4, memMib: 8192 },
|
|
77
|
-
l: { vcpu: 8, memMib: 16384 }
|
|
80
|
+
s: { vcpu: 2, memMib: 4096, diskGib: 8 },
|
|
81
|
+
m: { vcpu: 4, memMib: 8192, diskGib: 16 },
|
|
82
|
+
l: { vcpu: 8, memMib: 16384, diskGib: 32 }
|
|
78
83
|
};
|
|
79
84
|
var SIZE_NAMES = Object.keys(SIZES);
|
|
80
85
|
var DEFAULT_SIZE = "s";
|
|
@@ -203,7 +208,7 @@ function macFor(ip) {
|
|
|
203
208
|
}
|
|
204
209
|
|
|
205
210
|
// src/listen.ts
|
|
206
|
-
import { rm as rm2, writeFile as
|
|
211
|
+
import { rm as rm2, writeFile as writeFile4 } from "fs/promises";
|
|
207
212
|
import { createAdaptorServer } from "@hono/node-server";
|
|
208
213
|
|
|
209
214
|
// src/core/state.ts
|
|
@@ -218,7 +223,9 @@ var MachineMetaSchema = z2.object({
|
|
|
218
223
|
vcpu: z2.number().int(),
|
|
219
224
|
memMib: z2.number().int(),
|
|
220
225
|
createdAt: z2.string(),
|
|
221
|
-
source: z2.string()
|
|
226
|
+
source: z2.string(),
|
|
227
|
+
/** Base image version (assets/rootfs-<version>.ext4) the box overlays. Absent: a pre-overlay box with its own disk.ext4. */
|
|
228
|
+
base: z2.string().optional()
|
|
222
229
|
});
|
|
223
230
|
var SnapshotMetaSchema = z2.object({
|
|
224
231
|
name: z2.string(),
|
|
@@ -281,7 +288,7 @@ function versionOf(srcFileUrl, injected) {
|
|
|
281
288
|
}
|
|
282
289
|
|
|
283
290
|
// src/version.ts
|
|
284
|
-
var VERSION = versionOf(import.meta.url, true ? "0.0.
|
|
291
|
+
var VERSION = versionOf(import.meta.url, true ? "0.0.6" : void 0);
|
|
285
292
|
|
|
286
293
|
// src/routes/capacity.ts
|
|
287
294
|
import { Hono } from "hono";
|
|
@@ -311,6 +318,12 @@ var MachineSchema = z3.object({
|
|
|
311
318
|
pid: z3.number().int().nullable(),
|
|
312
319
|
vcpu: z3.number().int(),
|
|
313
320
|
memMib: z3.number().int(),
|
|
321
|
+
diskBytes: z3.number().int(),
|
|
322
|
+
// the box's own writable disk, as the guest sees it
|
|
323
|
+
diskUsedBytes: z3.number().int(),
|
|
324
|
+
// what that sparse file really occupies on the host
|
|
325
|
+
base: z3.string().nullable(),
|
|
326
|
+
// base image version the box runs on (null: pre-overlay box with its own copy)
|
|
314
327
|
createdAt: z3.string(),
|
|
315
328
|
source: z3.string(),
|
|
316
329
|
// "base" or "snapshot:<name>"
|
|
@@ -351,8 +364,9 @@ var ApiErrorSchema = z3.object({
|
|
|
351
364
|
|
|
352
365
|
// src/core/machines.ts
|
|
353
366
|
import { spawn } from "child_process";
|
|
354
|
-
import { closeSync, existsSync as existsSync2, openSync } from "fs";
|
|
355
|
-
import { mkdir as mkdir2, readFile as readFile3, rm } from "fs/promises";
|
|
367
|
+
import { closeSync, existsSync as existsSync2, openSync, statSync } from "fs";
|
|
368
|
+
import { mkdir as mkdir2, readFile as readFile3, realpath, rm, truncate, writeFile as writeFile3 } from "fs/promises";
|
|
369
|
+
import { basename } from "path";
|
|
356
370
|
import { setTimeout as sleep } from "timers/promises";
|
|
357
371
|
|
|
358
372
|
// src/core/firecracker.ts
|
|
@@ -380,15 +394,26 @@ var FirecrackerClient = class {
|
|
|
380
394
|
bootSource(kernelImagePath, bootArgs2) {
|
|
381
395
|
return this.call("PUT", "/boot-source", { kernel_image_path: kernelImagePath, boot_args: bootArgs2 });
|
|
382
396
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
397
|
+
/**
|
|
398
|
+
* /dev/vda. Firecracker adds `root=/dev/vda` plus `ro` or `rw` to the boot args to match.
|
|
399
|
+
* Read-only for the shared base image; read-write only for pre-overlay boxes with their own copy.
|
|
400
|
+
*/
|
|
401
|
+
rootDrive(pathOnHost, readOnly) {
|
|
402
|
+
return this.drive("rootfs", pathOnHost, { root: true, readOnly });
|
|
403
|
+
}
|
|
404
|
+
/** The box's own writable disk, /dev/vdb: the guest stacks it on the base as an overlay. */
|
|
405
|
+
overlayDrive(pathOnHost) {
|
|
406
|
+
return this.drive("overlay", pathOnHost, { root: false, readOnly: false });
|
|
407
|
+
}
|
|
408
|
+
drive(id, pathOnHost, opts) {
|
|
409
|
+
return this.call("PUT", `/drives/${id}`, {
|
|
410
|
+
drive_id: id,
|
|
386
411
|
path_on_host: pathOnHost,
|
|
387
|
-
is_root_device:
|
|
388
|
-
is_read_only:
|
|
412
|
+
is_root_device: opts.root,
|
|
413
|
+
is_read_only: opts.readOnly,
|
|
389
414
|
// "Unsafe" (the default) acks guest flushes without doing anything, so the last journal
|
|
390
415
|
// commit of a clean shutdown can be lost when the guest resets and Firecracker exits.
|
|
391
|
-
// Writeback makes a guest flush a real fsync;
|
|
416
|
+
// Writeback makes a guest flush a real fsync; a box's writes must survive `kvbox stop`.
|
|
392
417
|
cache_type: "Writeback"
|
|
393
418
|
});
|
|
394
419
|
}
|
|
@@ -440,7 +465,19 @@ var FirecrackerClient = class {
|
|
|
440
465
|
// src/core/machines.ts
|
|
441
466
|
var NAME_RE = /^[a-z0-9][a-z0-9-]{0,11}$/;
|
|
442
467
|
var AUTO_PREFIX = "box";
|
|
443
|
-
function
|
|
468
|
+
function diskStats(disk) {
|
|
469
|
+
try {
|
|
470
|
+
const st = statSync(disk);
|
|
471
|
+
return { diskBytes: st.size, diskUsedBytes: st.blocks * 512 };
|
|
472
|
+
} catch {
|
|
473
|
+
return { diskBytes: 0, diskUsedBytes: 0 };
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function ownDisk(meta, p) {
|
|
477
|
+
const m = p.machine(meta.name);
|
|
478
|
+
return meta.base === void 0 ? m.disk : m.overlay;
|
|
479
|
+
}
|
|
480
|
+
function toMachine(meta, p = paths()) {
|
|
444
481
|
const alive = pidAlive(meta.pid);
|
|
445
482
|
return {
|
|
446
483
|
name: meta.name,
|
|
@@ -449,6 +486,8 @@ function toMachine(meta) {
|
|
|
449
486
|
pid: alive ? meta.pid : null,
|
|
450
487
|
vcpu: meta.vcpu,
|
|
451
488
|
memMib: meta.memMib,
|
|
489
|
+
...diskStats(ownDisk(meta, p)),
|
|
490
|
+
base: meta.base ?? null,
|
|
452
491
|
createdAt: meta.createdAt,
|
|
453
492
|
source: meta.source,
|
|
454
493
|
status: alive ? "running" : "stopped"
|
|
@@ -466,7 +505,7 @@ async function readAllMeta(p) {
|
|
|
466
505
|
return metas;
|
|
467
506
|
}
|
|
468
507
|
async function listMachines(p = paths()) {
|
|
469
|
-
return (await readAllMeta(p)).map(toMachine);
|
|
508
|
+
return (await readAllMeta(p)).map((m) => toMachine(m, p));
|
|
470
509
|
}
|
|
471
510
|
function pickName(requested, taken) {
|
|
472
511
|
if (requested !== void 0) {
|
|
@@ -481,10 +520,44 @@ function pickName(requested, taken) {
|
|
|
481
520
|
if (!taken.has(candidate)) return candidate;
|
|
482
521
|
}
|
|
483
522
|
}
|
|
484
|
-
function bootArgs(name, ip, cfg, pubkeyB64) {
|
|
485
|
-
const
|
|
523
|
+
function bootArgs(name, ip, cfg, pubkeyB64, mode) {
|
|
524
|
+
const init = mode.overlay ? " init=/sbin/overlay-init kvbox.overlay=/dev/vdb" : "";
|
|
525
|
+
const args = `console=ttyS0 reboot=k panic=1${init} ip=${ip}::${cfg.gateway}:255.255.255.0:${name}:eth0:off kvbox.name=${name} kvbox.sshkey=${pubkeyB64}`;
|
|
486
526
|
return process.arch === "arm64" ? `keep_bootcon ${args}` : args;
|
|
487
527
|
}
|
|
528
|
+
function baseVersionOf(file) {
|
|
529
|
+
const m = /^rootfs-(.+)\.ext4$/.exec(basename(file));
|
|
530
|
+
if (!m) throw new KvboxError(`base image ${file} is not versioned (expected assets/rootfs-<version>.ext4); run install-server.sh`);
|
|
531
|
+
return m[1];
|
|
532
|
+
}
|
|
533
|
+
async function currentBase(cfg, p) {
|
|
534
|
+
const file = await realpath(cfg.rootfs);
|
|
535
|
+
const version = baseVersionOf(file);
|
|
536
|
+
const expected = p.baseImage(version);
|
|
537
|
+
if (file !== expected) throw new KvboxError(`base image ${cfg.rootfs} points outside ${p.assets} (${file})`);
|
|
538
|
+
return { version, file };
|
|
539
|
+
}
|
|
540
|
+
function baseFileFor(meta, p) {
|
|
541
|
+
if (meta.base === void 0) return void 0;
|
|
542
|
+
const file = p.baseImage(meta.base);
|
|
543
|
+
if (!existsSync2(file)) throw new KvboxError(`box ${meta.name} runs on base image ${meta.base}, which is gone from ${p.assets}`);
|
|
544
|
+
return file;
|
|
545
|
+
}
|
|
546
|
+
var overlayCapable = /* @__PURE__ */ new Map();
|
|
547
|
+
async function assertOverlayCapable(baseFile, version) {
|
|
548
|
+
let ok = overlayCapable.get(baseFile);
|
|
549
|
+
if (ok === void 0) {
|
|
550
|
+
const { stdout } = await run(["debugfs", "-R", "stat /sbin/overlay-init", baseFile]);
|
|
551
|
+
ok = /Type:\s+regular/.test(stdout);
|
|
552
|
+
overlayCapable.set(baseFile, ok);
|
|
553
|
+
}
|
|
554
|
+
if (!ok) throw new KvboxError(`base image ${version} is too old for this server (no /sbin/overlay-init); rerun install-server.sh to get a newer one`);
|
|
555
|
+
}
|
|
556
|
+
async function createOverlay(file, gib) {
|
|
557
|
+
await writeFile3(file, "", { mode: 384 });
|
|
558
|
+
await truncate(file, gib * 1024 ** 3);
|
|
559
|
+
await run(["mkfs.ext4", "-q", "-F", "-m", "0", "-L", "kvbox-box", "-E", "lazy_itable_init=1,lazy_journal_init=1", file]);
|
|
560
|
+
}
|
|
488
561
|
async function waitFor(pred, timeoutMs, stepMs = 50) {
|
|
489
562
|
const deadline = Date.now() + timeoutMs;
|
|
490
563
|
while (Date.now() < deadline) {
|
|
@@ -502,6 +575,7 @@ async function tailOf(file, lines = 5) {
|
|
|
502
575
|
}
|
|
503
576
|
async function launch(spec, tap, cfg, pubkeyB64, p) {
|
|
504
577
|
const m = p.machine(spec.name);
|
|
578
|
+
const baseFile = baseFileFor(spec, p);
|
|
505
579
|
await rm(m.sock, { force: true });
|
|
506
580
|
const logFd = openSync(m.log, "a");
|
|
507
581
|
let child;
|
|
@@ -523,8 +597,13 @@ ${await tailOf(m.log)}`);
|
|
|
523
597
|
}
|
|
524
598
|
const fc = new FirecrackerClient(m.sock);
|
|
525
599
|
try {
|
|
526
|
-
await fc.bootSource(cfg.kernel, bootArgs(spec.name, spec.ip, cfg, pubkeyB64));
|
|
527
|
-
|
|
600
|
+
await fc.bootSource(cfg.kernel, bootArgs(spec.name, spec.ip, cfg, pubkeyB64, { overlay: baseFile !== void 0 }));
|
|
601
|
+
if (baseFile !== void 0) {
|
|
602
|
+
await fc.rootDrive(baseFile, true);
|
|
603
|
+
await fc.overlayDrive(m.overlay);
|
|
604
|
+
} else {
|
|
605
|
+
await fc.rootDrive(m.disk, false);
|
|
606
|
+
}
|
|
528
607
|
await fc.machineConfig(spec.vcpu, spec.memMib);
|
|
529
608
|
await fc.networkInterface("eth0", tap, spec.mac);
|
|
530
609
|
await fc.start();
|
|
@@ -565,17 +644,20 @@ async function createMachine(req, p = paths()) {
|
|
|
565
644
|
const ip = nextFreeIp(cfg.subnet, [...existing.map((m2) => m2.ip), cfg.gateway]);
|
|
566
645
|
const mac = macFor(ip);
|
|
567
646
|
const m = p.machine(name);
|
|
647
|
+
const base = await currentBase(cfg, p);
|
|
648
|
+
await assertOverlayCapable(base.file, base.version);
|
|
568
649
|
await ensureHostNetwork(cfg);
|
|
569
650
|
await mkdir2(m.dir, { recursive: true, mode: 448 });
|
|
570
651
|
let tapCreated = false;
|
|
571
652
|
try {
|
|
572
|
-
await
|
|
653
|
+
await createOverlay(m.overlay, size.diskGib);
|
|
573
654
|
const tap = await createTap(name, cfg);
|
|
574
655
|
tapCreated = true;
|
|
575
|
-
const
|
|
576
|
-
const
|
|
656
|
+
const spec = { name, ip, mac, vcpu: size.vcpu, memMib: size.memMib, base: base.version };
|
|
657
|
+
const pid = await launch(spec, tap, cfg, pubkeyB64, p);
|
|
658
|
+
const meta = { ...spec, pid, createdAt: (/* @__PURE__ */ new Date()).toISOString(), source: "base" };
|
|
577
659
|
await writeMachineMeta(meta, p);
|
|
578
|
-
return toMachine(meta);
|
|
660
|
+
return toMachine(meta, p);
|
|
579
661
|
} catch (err) {
|
|
580
662
|
if (tapCreated) await deleteTap(name, cfg).catch(() => void 0);
|
|
581
663
|
await rm(m.dir, { recursive: true, force: true });
|
|
@@ -611,7 +693,7 @@ async function stopMachine(name, p = paths()) {
|
|
|
611
693
|
await shutdown(meta, cfg, p, 1e4);
|
|
612
694
|
const stopped = { ...meta, pid: null };
|
|
613
695
|
await writeMachineMeta(stopped, p);
|
|
614
|
-
return toMachine(stopped);
|
|
696
|
+
return toMachine(stopped, p);
|
|
615
697
|
}
|
|
616
698
|
async function startMachine(name, p = paths()) {
|
|
617
699
|
const meta = await readMeta(name, p);
|
|
@@ -625,7 +707,7 @@ async function startMachine(name, p = paths()) {
|
|
|
625
707
|
const pid = await launch(meta, tap, cfg, pubkeyB64, p);
|
|
626
708
|
const started = { ...meta, pid };
|
|
627
709
|
await writeMachineMeta(started, p);
|
|
628
|
-
return toMachine(started);
|
|
710
|
+
return toMachine(started, p);
|
|
629
711
|
} catch (err) {
|
|
630
712
|
await deleteTap(name, cfg).catch(() => void 0);
|
|
631
713
|
throw err;
|
|
@@ -725,7 +807,7 @@ async function listen(opts = {}) {
|
|
|
725
807
|
server.once("error", reject);
|
|
726
808
|
server.listen(socketPath, () => resolve());
|
|
727
809
|
});
|
|
728
|
-
await
|
|
810
|
+
await writeFile4(p.serverPid, `${process.pid}
|
|
729
811
|
`);
|
|
730
812
|
const shutdown2 = async () => {
|
|
731
813
|
server.close();
|
package/dist/kvbox-server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../../shared/src/errors.ts","../../shared/src/paths.ts","../src/core/config.ts","../../shared/src/sizes.ts","../src/core/network.ts","../src/core/exec.ts","../src/listen.ts","../src/core/state.ts","../src/app.ts","../../shared/src/version.ts","../src/version.ts","../src/routes/capacity.ts","../src/core/capacity.ts","../src/routes/machines.ts","../../shared/src/api.ts","../src/core/machines.ts","../src/core/firecracker.ts","../src/routes/snapshots.ts","../src/core/snapshots.ts"],"sourcesContent":["/**\n * kvbox-server: the host daemon. `kvbox-server` (or `kvbox-server start`) serves\n * the kvbox API on ~/.kvbox/server.sock in the foreground; the CLI's\n * `kvbox server start` spawns it detached. M2 adds `register`.\n */\nimport { spawnSync } from \"node:child_process\";\nimport { Command } from \"commander\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\nimport { paths } from \"@kvbox/shared/paths\";\nimport { loadConfig } from \"./core/config.js\";\nimport { ensureHostNetwork, netSetupScript } from \"./core/network.js\";\nimport { listen } from \"./listen.js\";\nimport { VERSION } from \"./version.js\";\n\nconst program = new Command(\"kvbox-server\")\n .description(\"kvbox host daemon: owns Firecracker and ~/.kvbox, serves the kvbox API\")\n .version(VERSION, \"-v, --version\")\n .showHelpAfterError();\n\nprogram\n .command(\"start\", { isDefault: true })\n .description(\"serve the API on the local Unix socket (foreground)\")\n .option(\"--socket <path>\", \"socket to listen on (default: $KVBOX_HOME/server.sock)\")\n .action(async (opts: { socket?: string }) => {\n const { socketPath } = await listen(opts.socket ? { socketPath: opts.socket } : {});\n console.log(`kvbox-server ${VERSION} listening on ${socketPath} (pid ${process.pid})`);\n // The bridge vanishes on host reboot; recreate it. Not fatal: ls/status must still work.\n try {\n const cfg = await loadConfig(paths());\n const state = await ensureHostNetwork(cfg);\n console.log(`host network: bridge ${cfg.bridge} ${state}`);\n } catch (err) {\n console.error(`warning: host network not ready (${(err as Error).message}); run: kvbox-server net up`);\n }\n });\n\nprogram\n .command(\"net <action>\")\n .description(\"host network: up | down | status (bridge, forwarding, NAT; uses sudo)\")\n .action(async (action: string) => {\n if (![\"up\", \"down\", \"status\"].includes(action)) throw new KvboxError(\"net: expected up, down or status\");\n const cfg = await loadConfig(paths());\n const res = spawnSync(netSetupScript(), [action], {\n stdio: \"inherit\",\n env: { ...process.env, KVBOX_BRIDGE: cfg.bridge, KVBOX_SUBNET: cfg.subnet, KVBOX_GATEWAY: cfg.gateway },\n });\n process.exit(res.status ?? 1);\n });\n\ntry {\n await program.parseAsync(process.argv);\n} catch (err) {\n if (err instanceof KvboxError) {\n console.error(`error: ${err.message}`);\n process.exit(err.exitCode);\n }\n throw err;\n}\n","/** Errors with an exit code; the entry point prints `message` and exits with `exitCode`. */\nexport class KvboxError extends Error {\n constructor(\n message: string,\n public readonly exitCode: number = 1,\n ) {\n super(message);\n this.name = \"KvboxError\";\n }\n}\n\nexport class NotImplementedError extends KvboxError {\n constructor(what: string) {\n super(`${what}: not implemented yet`, 2);\n this.name = \"NotImplementedError\";\n }\n}\n\n/** The named box/snapshot/host does not exist: HTTP 404, exit 1. */\nexport class NotFoundError extends KvboxError {\n constructor(what: string) {\n super(`${what}: not found`, 1);\n this.name = \"NotFoundError\";\n }\n}\n","/**\n * Host state layout. The filesystem is the database and `~/.kvbox` is its root.\n * This module is dependency-free so both the CLI (socket path, hosts.json)\n * and core (everything else) can use it without crossing the layer boundary.\n *\n * `KVBOX_HOME` overrides the root, mainly for tests.\n */\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport function kvboxHome(): string {\n return process.env[\"KVBOX_HOME\"] ?? join(homedir(), \".kvbox\");\n}\n\nexport function paths(home: string = kvboxHome()) {\n return {\n home,\n config: join(home, \"config.json\"),\n serverSock: join(home, \"server.sock\"),\n serverPid: join(home, \"server.pid\"),\n serverLog: join(home, \"server.log\"),\n hosts: join(home, \"hosts.json\"),\n ssh: join(home, \"ssh\"),\n sshKey: join(home, \"ssh\", \"id_ed25519\"),\n bin: join(home, \"bin\"),\n firecracker: join(home, \"bin\", \"firecracker\"),\n assets: join(home, \"assets\"),\n kernel: join(home, \"assets\", \"vmlinux\"),\n rootfs: join(home, \"assets\", \"rootfs.ext4\"),\n machines: join(home, \"machines\"),\n snapshots: join(home, \"snapshots\"),\n machine: (name: string) => ({\n dir: join(home, \"machines\", name),\n meta: join(home, \"machines\", name, \"meta.json\"),\n disk: join(home, \"machines\", name, \"disk.ext4\"),\n sock: join(home, \"machines\", name, \"fc.sock\"),\n log: join(home, \"machines\", name, \"fc.log\"),\n }),\n snapshot: (name: string) => ({\n dir: join(home, \"snapshots\", name),\n meta: join(home, \"snapshots\", name, \"meta.json\"),\n vmstate: join(home, \"snapshots\", name, \"vmstate\"),\n mem: join(home, \"snapshots\", name, \"mem\"),\n disk: join(home, \"snapshots\", name, \"disk.ext4\"),\n }),\n } as const;\n}\n\nexport type Paths = ReturnType<typeof paths>;\n","/** `~/.kvbox/config.json`: asset paths, subnet, default size. Written with defaults on first run. */\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport { paths, type Paths } from \"@kvbox/shared/paths\";\nimport { DEFAULT_SIZE, SIZE_NAMES } from \"@kvbox/shared/sizes\";\n\nexport const ConfigSchema = z.object({\n kernel: z.string(),\n rootfs: z.string(),\n firecracker: z.string(),\n bridge: z.string().default(\"kvbox0\"),\n subnet: z.string().default(\"172.30.0.0/24\"),\n gateway: z.string().default(\"172.30.0.1\"),\n defaultSize: z.enum(SIZE_NAMES as [string, ...string[]]).default(DEFAULT_SIZE),\n});\nexport type Config = z.infer<typeof ConfigSchema>;\n\nexport function defaultConfig(p: Paths = paths()): Config {\n return ConfigSchema.parse({\n kernel: p.kernel,\n rootfs: p.rootfs,\n firecracker: p.firecracker,\n });\n}\n\nexport async function loadConfig(p: Paths = paths()): Promise<Config> {\n try {\n const raw = await readFile(p.config, \"utf8\");\n return ConfigSchema.parse(JSON.parse(raw));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n const cfg = defaultConfig(p);\n await writeFile(p.config, JSON.stringify(cfg, null, 2) + \"\\n\");\n return cfg;\n }\n throw err;\n }\n}\n","/** Fixed box sizes. Firecracker backs guest memory lazily, so a box only costs what the guest touches. */\nexport const SIZES = {\n s: { vcpu: 2, memMib: 4096 },\n m: { vcpu: 4, memMib: 8192 },\n l: { vcpu: 8, memMib: 16384 },\n} as const;\n\nexport type SizeName = keyof typeof SIZES;\nexport const SIZE_NAMES = Object.keys(SIZES) as SizeName[];\nexport const DEFAULT_SIZE: SizeName = \"s\";\n\nexport function isSizeName(value: string): value is SizeName {\n return value in SIZES;\n}\n","/**\n * Host networking: bridge `kvbox0`, one tap `kv-<box>` per box, IP allocation\n * inside the subnet. Everything that needs root goes through one script,\n * `net-setup.sh`, which re-execs itself with `sudo -n` when run from here.\n * `install-server.sh` installs a root-owned copy at PRIVILEGED_SCRIPT plus a\n * sudoers rule for exactly that path; the package copy is the dev fallback.\n */\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\nimport type { Config } from \"./config.js\";\nimport { run } from \"./exec.js\";\n\nexport const TAP_PREFIX = \"kv-\";\nexport const PRIVILEGED_SCRIPT = \"/usr/local/lib/kvbox/net-setup.sh\";\n\ntype NetConfig = Pick<Config, \"bridge\" | \"subnet\" | \"gateway\">;\n\nexport function tapName(box: string): string {\n // Linux interface names are limited to 15 characters.\n return `${TAP_PREFIX}${box}`.slice(0, 15);\n}\n\n/** The script to run: KVBOX_NET_SETUP override, the root-owned install, else the package copy (dist/ or src/core/). */\nexport function netSetupScript(): string {\n const override = process.env[\"KVBOX_NET_SETUP\"];\n if (override) return override;\n if (existsSync(PRIVILEGED_SCRIPT)) return PRIVILEGED_SCRIPT;\n for (const rel of [\"../scripts/net-setup.sh\", \"../../scripts/net-setup.sh\"]) {\n const candidate = fileURLToPath(new URL(rel, import.meta.url));\n if (existsSync(candidate)) return candidate;\n }\n throw new KvboxError(\"net-setup.sh not found next to kvbox-server\");\n}\n\nexport function bridgeExists(bridge: string): boolean {\n return existsSync(`/sys/class/net/${bridge}`);\n}\n\nfunction netRun(cfg: NetConfig, args: string[]) {\n return run([netSetupScript(), ...args], {\n env: { ...process.env, KVBOX_BRIDGE: cfg.bridge, KVBOX_SUBNET: cfg.subnet, KVBOX_GATEWAY: cfg.gateway },\n }).catch((err: Error) => {\n throw new KvboxError(\n `${err.message}\\n (root is needed for host networking; install-server.sh sets up a sudoers rule for ${PRIVILEGED_SCRIPT})`,\n );\n });\n}\n\n/** Run `net-setup.sh up` when the bridge is missing, which is the case after every host reboot. */\nexport async function ensureHostNetwork(cfg: NetConfig): Promise<\"present\" | \"created\"> {\n if (bridgeExists(cfg.bridge)) return \"present\";\n await netRun(cfg, [\"up\"]);\n return \"created\";\n}\n\n/** Create the box's tap (owned by this user, plugged into the bridge) and return its name. */\nexport async function createTap(box: string, cfg: NetConfig): Promise<string> {\n const tap = tapName(box);\n await netRun(cfg, [\"tap\", \"add\", tap]);\n return tap;\n}\n\nexport async function deleteTap(box: string, cfg: NetConfig): Promise<void> {\n await netRun(cfg, [\"tap\", \"del\", tapName(box)]);\n}\n\n// ── addresses ────────────────────────────────────────────────────────────────\nconst ipToInt = (ip: string) => ip.split(\".\").reduce((acc, o) => (acc << 8) + Number(o), 0) >>> 0;\nconst intToIp = (n: number) => [24, 16, 8, 0].map((s) => (n >>> s) & 255).join(\".\");\n\n/** Lowest free host address in `subnet` (a.b.c.d/len), skipping network, gateway (.1) and broadcast. */\nexport function nextFreeIp(subnet: string, inUse: readonly string[]): string {\n const [base, lenStr] = subnet.split(\"/\");\n const len = Number(lenStr);\n if (!base || !Number.isInteger(len) || len < 8 || len > 30) throw new KvboxError(`bad subnet: ${subnet}`);\n const mask = len === 0 ? 0 : (~0 << (32 - len)) >>> 0;\n const network = (ipToInt(base) & mask) >>> 0;\n const broadcast = (network | (~mask >>> 0)) >>> 0;\n const used = new Set(inUse.map(ipToInt));\n for (let n = network + 2; n < broadcast; n++) {\n if (!used.has(n)) return intToIp(n);\n }\n throw new KvboxError(`no free IP left in ${subnet}`);\n}\n\n/** Locally administered MAC derived from the IP: 06:00:<a>:<b>:<c>:<d>, unique per box and readable. */\nexport function macFor(ip: string): string {\n const hex = ip.split(\".\").map((o) => Number(o).toString(16).padStart(2, \"0\").toUpperCase());\n return [\"06\", \"00\", ...hex].join(\":\");\n}\n","/**\n * The one shell-out helper. Every `firecracker`, `ip`, `ssh`, `cp` call goes\n * through here: argv arrays only (never a shell string), the command is logged,\n * and stderr ends up in the thrown error.\n */\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\n\nconst execFileAsync = promisify(execFile);\n\nexport interface RunOptions {\n sudo?: boolean;\n cwd?: string;\n timeoutMs?: number;\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface RunResult {\n stdout: string;\n stderr: string;\n}\n\nexport async function run(argv: readonly string[], opts: RunOptions = {}): Promise<RunResult> {\n const [cmd, ...args] = opts.sudo ? [\"sudo\", \"-n\", ...argv] : argv;\n if (!cmd) throw new KvboxError(\"run: empty argv\");\n if (process.env[\"KVBOX_DEBUG\"]) console.error(`[exec] ${[cmd, ...args].join(\" \")}`);\n try {\n const { stdout, stderr } = await execFileAsync(cmd, args, {\n cwd: opts.cwd,\n env: opts.env ?? process.env,\n timeout: opts.timeoutMs ?? 30_000,\n maxBuffer: 8 * 1024 * 1024,\n });\n return { stdout, stderr };\n } catch (err) {\n const e = err as NodeJS.ErrnoException & { stderr?: string; code?: number | string };\n const detail = (e.stderr ?? e.message ?? \"\").trim();\n throw new KvboxError(`${[cmd, ...args].join(\" \")} failed${detail ? `: ${detail}` : \"\"}`);\n }\n}\n","/** Bind the app to the local Unix socket and write the pid file. */\nimport { rm, writeFile } from \"node:fs/promises\";\nimport { createAdaptorServer } from \"@hono/node-server\";\nimport { ensureLayout } from \"./core/state.js\";\nimport { loadConfig } from \"./core/config.js\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\nimport { paths } from \"@kvbox/shared/paths\";\nimport { createApp } from \"./app.js\";\n\nexport interface ListenOptions {\n socketPath?: string;\n}\n\nexport async function listen(opts: ListenOptions = {}) {\n const p = await ensureLayout(paths());\n await loadConfig(p);\n const socketPath = opts.socketPath ?? p.serverSock;\n // Linux limits a Unix socket path (sun_path) to 108 bytes; bind() fails with EINVAL past it.\n if (Buffer.byteLength(socketPath) > 100) {\n throw new KvboxError(`socket path too long for a Unix socket (${socketPath}); set KVBOX_HOME to a shorter path`);\n }\n\n // A stale socket file from a crashed server blocks bind(); the pid file tells\n // `server status` whether anything is actually alive.\n await rm(socketPath, { force: true });\n\n const server = createAdaptorServer({ fetch: createApp().fetch });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(socketPath, () => resolve());\n });\n await writeFile(p.serverPid, `${process.pid}\\n`);\n\n const shutdown = async () => {\n server.close();\n await rm(socketPath, { force: true });\n await rm(p.serverPid, { force: true });\n process.exit(0);\n };\n process.once(\"SIGINT\", () => void shutdown());\n process.once(\"SIGTERM\", () => void shutdown());\n\n return { socketPath, server };\n}\n","/**\n * The `~/.kvbox` layout and the per-box / per-snapshot `meta.json` files.\n * The server is the only writer; every read is validated with zod.\n */\nimport { mkdir, readFile, readdir, writeFile } from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport { paths, type Paths } from \"@kvbox/shared/paths\";\n\nexport const MachineMetaSchema = z.object({\n name: z.string(),\n ip: z.string(),\n mac: z.string(),\n pid: z.number().int().nullable(), // null: stopped on purpose, disk kept\n vcpu: z.number().int(),\n memMib: z.number().int(),\n createdAt: z.string(),\n source: z.string(),\n});\nexport type MachineMeta = z.infer<typeof MachineMetaSchema>;\n\nexport const SnapshotMetaSchema = z.object({\n name: z.string(),\n source: z.string(),\n vcpu: z.number().int(),\n memMib: z.number().int(),\n createdAt: z.string(),\n});\nexport type SnapshotMeta = z.infer<typeof SnapshotMetaSchema>;\n\n/** Create every directory of the layout. Idempotent; called by `kvbox server start`. */\nexport async function ensureLayout(p: Paths = paths()): Promise<Paths> {\n for (const dir of [p.home, p.ssh, p.bin, p.assets, p.machines, p.snapshots]) {\n await mkdir(dir, { recursive: true, mode: 0o700 });\n }\n return p;\n}\n\nasync function readJson<T>(file: string, schema: z.ZodType<T>): Promise<T> {\n return schema.parse(JSON.parse(await readFile(file, \"utf8\")));\n}\n\nasync function listDirs(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw err;\n }\n}\n\nexport async function readMachineMeta(name: string, p: Paths = paths()) {\n return readJson(p.machine(name).meta, MachineMetaSchema);\n}\n\nexport async function writeMachineMeta(meta: MachineMeta, p: Paths = paths()) {\n await mkdir(p.machine(meta.name).dir, { recursive: true });\n await writeFile(p.machine(meta.name).meta, JSON.stringify(meta, null, 2) + \"\\n\");\n}\n\nexport async function listMachineNames(p: Paths = paths()) {\n return listDirs(p.machines);\n}\n\nexport async function readSnapshotMeta(name: string, p: Paths = paths()) {\n return readJson(p.snapshot(name).meta, SnapshotMetaSchema);\n}\n\nexport async function writeSnapshotMeta(meta: SnapshotMeta, p: Paths = paths()) {\n await mkdir(p.snapshot(meta.name).dir, { recursive: true });\n await writeFile(p.snapshot(meta.name).meta, JSON.stringify(meta, null, 2) + \"\\n\");\n}\n\nexport async function listSnapshotNames(p: Paths = paths()) {\n return listDirs(p.snapshots);\n}\n\n/** True when a pid is alive (signal 0). */\nexport function pidAlive(pid: number | null): boolean {\n if (pid === null || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n","/**\n * The hono app behind `kvbox-server`. Served over `~/.kvbox/server.sock`\n * in M1; the same app goes behind HTTPS in M2.\n */\nimport { Hono } from \"hono\";\nimport { KvboxError, NotFoundError } from \"@kvbox/shared/errors\";\nimport { VERSION } from \"./version.js\";\nimport { capacityRoutes } from \"./routes/capacity.js\";\nimport { machineRoutes } from \"./routes/machines.js\";\nimport { snapshotRoutes } from \"./routes/snapshots.js\";\n\nexport function createApp() {\n const app = new Hono();\n\n app.get(\"/health\", (c) => c.json({ ok: true as const, version: VERSION, pid: process.pid }));\n app.route(\"/machines\", machineRoutes());\n app.route(\"/snapshots\", snapshotRoutes());\n app.route(\"/capacity\", capacityRoutes());\n\n app.notFound((c) => c.json({ error: `no route: ${c.req.method} ${c.req.path}` }, 404));\n app.onError((err, c) => {\n if (err instanceof NotFoundError) return c.json({ error: err.message }, 404);\n if (err instanceof KvboxError) {\n return c.json({ error: err.message }, err.exitCode === 2 ? 501 : 400);\n }\n console.error(err);\n return c.json({ error: err.message }, 500);\n });\n\n return app;\n}\n\nexport type App = ReturnType<typeof createApp>;\n","/**\n * Each binary's version comes from its own package.json: tsup injects\n * `__KVBOX_VERSION__` at build time; the dev path (tsx) reads the file.\n * Usage in a package: `versionOf(import.meta.url)` from its `src/version.ts`.\n */\nimport { createRequire } from \"node:module\";\n\nexport function versionOf(srcFileUrl: string, injected?: string): string {\n if (typeof injected === \"string\") return injected;\n try {\n const require = createRequire(srcFileUrl);\n return (require(\"../package.json\") as { version: string }).version;\n } catch {\n return \"0.0.0-dev\";\n }\n}\n","import { versionOf } from \"@kvbox/shared/version\";\n\ndeclare const __KVBOX_VERSION__: string | undefined;\n\nexport const VERSION: string = versionOf(import.meta.url, typeof __KVBOX_VERSION__ === \"string\" ? __KVBOX_VERSION__ : undefined);\n","import { Hono } from \"hono\";\nimport { capacity } from \"../core/capacity.js\";\n\nexport function capacityRoutes() {\n const r = new Hono();\n r.get(\"/\", async (c) => c.json(await capacity()));\n return r;\n}\n","/**\n * The capacity guard: `kvbox new` refuses when reserved RAM of running boxes\n * plus the new one exceeds host RAM minus 2 GB (hard), or reserved vCPUs exceed\n * 2x host cores (soft). `--force` skips it.\n */\nimport { NotImplementedError } from \"@kvbox/shared/errors\";\nimport type { Capacity } from \"@kvbox/shared/api\";\n\nexport const MEM_HEADROOM_MIB = 2048;\nexport const VCPU_OVERSUBSCRIPTION = 2;\n\nexport async function capacity(): Promise<Capacity> {\n throw new NotImplementedError(\"capacity.capacity\");\n}\n\nexport async function assertRoomFor(_vcpu: number, _memMib: number, _force: boolean): Promise<void> {\n throw new NotImplementedError(\"capacity.assertRoomFor\");\n}\n","import { Hono } from \"hono\";\nimport { CreateMachineRequest } from \"@kvbox/shared/api\";\nimport { createMachine, destroyMachine, listMachines, startMachine, stopMachine } from \"../core/machines.js\";\n\nexport function machineRoutes() {\n const r = new Hono();\n r.get(\"/\", async (c) => c.json(await listMachines()));\n r.post(\"/\", async (c) => {\n const body = CreateMachineRequest.parse(await c.req.json().catch(() => ({})));\n return c.json(await createMachine(body), 201);\n });\n r.post(\"/:name/stop\", async (c) => c.json(await stopMachine(c.req.param(\"name\"))));\n r.post(\"/:name/start\", async (c) => c.json(await startMachine(c.req.param(\"name\"))));\n r.delete(\"/:name\", async (c) => {\n await destroyMachine(c.req.param(\"name\"));\n return c.body(null, 204);\n });\n return r;\n}\n","/**\n * Wire shapes of the kvbox HTTP API, shared by the client (src/cli) and the\n * server (src/server). Validated with zod on both sides because the client\n * may be talking to an older or newer server.\n */\nimport { z } from \"zod\";\nimport { SIZE_NAMES } from \"./sizes.js\";\n\nexport const SizeSchema = z.enum(SIZE_NAMES as [string, ...string[]]);\n\nexport const MachineSchema = z.object({\n name: z.string(),\n ip: z.string(),\n mac: z.string(),\n pid: z.number().int().nullable(),\n vcpu: z.number().int(),\n memMib: z.number().int(),\n createdAt: z.string(),\n source: z.string(), // \"base\" or \"snapshot:<name>\"\n status: z.enum([\"running\", \"stopped\"]),\n});\nexport type Machine = z.infer<typeof MachineSchema>;\n\nexport const CreateMachineRequest = z.object({\n name: z.string().optional(),\n size: SizeSchema.optional(),\n from: z.string().optional(),\n force: z.boolean().optional(),\n});\nexport type CreateMachineRequest = z.infer<typeof CreateMachineRequest>;\n\nexport const SnapshotSchema = z.object({\n name: z.string(),\n source: z.string(),\n vcpu: z.number().int(),\n memMib: z.number().int(),\n createdAt: z.string(),\n});\nexport type Snapshot = z.infer<typeof SnapshotSchema>;\n\nexport const CreateSnapshotRequest = z.object({\n box: z.string(),\n name: z.string(),\n});\nexport type CreateSnapshotRequest = z.infer<typeof CreateSnapshotRequest>;\n\nexport const CapacitySchema = z.object({\n hostMemMib: z.number().int(),\n hostCores: z.number().int(),\n reservedMemMib: z.number().int(),\n reservedVcpu: z.number().int(),\n running: z.number().int(),\n});\nexport type Capacity = z.infer<typeof CapacitySchema>;\n\nexport const HealthSchema = z.object({\n ok: z.literal(true),\n version: z.string(),\n pid: z.number().int(),\n});\nexport type Health = z.infer<typeof HealthSchema>;\n\nexport const ApiErrorSchema = z.object({\n error: z.string(),\n});\n","/**\n * Create, list and destroy boxes: what spikes #4 and #6 did by hand.\n * See architecture \"Process model\". The server is the only writer of ~/.kvbox/machines.\n */\nimport { spawn } from \"node:child_process\";\nimport { closeSync, existsSync, openSync } from \"node:fs\";\nimport { mkdir, readFile, rm } from \"node:fs/promises\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { KvboxError, NotFoundError, NotImplementedError } from \"@kvbox/shared/errors\";\nimport type { CreateMachineRequest, Machine } from \"@kvbox/shared/api\";\nimport { paths, type Paths } from \"@kvbox/shared/paths\";\nimport { SIZES, isSizeName } from \"@kvbox/shared/sizes\";\nimport { loadConfig, type Config } from \"./config.js\";\nimport { run } from \"./exec.js\";\nimport { FirecrackerClient } from \"./firecracker.js\";\nimport { createTap, deleteTap, ensureHostNetwork, macFor, nextFreeIp } from \"./network.js\";\nimport { listMachineNames, pidAlive, readMachineMeta, writeMachineMeta, type MachineMeta } from \"./state.js\";\n\n/** Box names double as hostnames and tap suffixes (kv- + name ≤ 15 chars). */\nconst NAME_RE = /^[a-z0-9][a-z0-9-]{0,11}$/;\nconst AUTO_PREFIX = \"box\";\n\nfunction toMachine(meta: MachineMeta): Machine {\n const alive = pidAlive(meta.pid);\n return {\n name: meta.name,\n ip: meta.ip,\n mac: meta.mac,\n pid: alive ? meta.pid : null,\n vcpu: meta.vcpu,\n memMib: meta.memMib,\n createdAt: meta.createdAt,\n source: meta.source,\n status: alive ? \"running\" : \"stopped\",\n };\n}\n\nasync function readAllMeta(p: Paths): Promise<MachineMeta[]> {\n const metas: MachineMeta[] = [];\n for (const name of await listMachineNames(p)) {\n try {\n metas.push(await readMachineMeta(name, p));\n } catch (err) {\n console.error(`warning: skipping machines/${name}: ${(err as Error).message}`);\n }\n }\n return metas;\n}\n\nexport async function listMachines(p: Paths = paths()): Promise<Machine[]> {\n return (await readAllMeta(p)).map(toMachine);\n}\n\nexport async function getMachine(name: string, p: Paths = paths()): Promise<Machine> {\n return toMachine(await readMeta(name, p));\n}\n\nfunction pickName(requested: string | undefined, taken: Set<string>): string {\n if (requested !== undefined) {\n if (!NAME_RE.test(requested)) {\n throw new KvboxError(`invalid box name \"${requested}\": 1-12 chars, lowercase letters, digits and '-', starting with a letter or digit`);\n }\n if (taken.has(requested)) throw new KvboxError(`box ${requested} already exists`);\n return requested;\n }\n for (let i = 1; ; i++) {\n const candidate = `${AUTO_PREFIX}${i}`;\n if (!taken.has(candidate)) return candidate;\n }\n}\n\nfunction bootArgs(name: string, ip: string, cfg: Config, pubkeyB64: string): string {\n // Firecracker appends `pci=off root=/dev/vda rw` itself. ip= is read by the guest kernel,\n // kvbox.name= / kvbox.sshkey= by kvbox-init on first boot (the kvbox-images contract).\n const args = `console=ttyS0 reboot=k panic=1 ip=${ip}::${cfg.gateway}:255.255.255.0:${name}:eth0:off kvbox.name=${name} kvbox.sshkey=${pubkeyB64}`;\n return process.arch === \"arm64\" ? `keep_bootcon ${args}` : args;\n}\n\nasync function waitFor(pred: () => boolean, timeoutMs: number, stepMs = 50): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (pred()) return true;\n await sleep(stepMs);\n }\n return pred();\n}\n\nasync function tailOf(file: string, lines = 5): Promise<string> {\n try {\n return (await readFile(file, \"utf8\")).trim().split(\"\\n\").slice(-lines).join(\"\\n\");\n } catch {\n return \"\";\n }\n}\n\ninterface LaunchSpec {\n name: string;\n ip: string;\n mac: string;\n vcpu: number;\n memMib: number;\n}\n\n/** Spawn Firecracker detached, configure it over its socket, InstanceStart. Returns the pid. Caller rolls back on throw. */\nasync function launch(spec: LaunchSpec, tap: string, cfg: Config, pubkeyB64: string, p: Paths): Promise<number> {\n const m = p.machine(spec.name);\n await rm(m.sock, { force: true });\n const logFd = openSync(m.log, \"a\");\n let child;\n try {\n child = spawn(cfg.firecracker, [\"--api-sock\", m.sock, \"--id\", spec.name, \"--level\", \"Error\"], {\n detached: true,\n stdio: [\"ignore\", logFd, logFd],\n });\n } finally {\n closeSync(logFd);\n }\n child.unref();\n const pid = child.pid;\n if (pid === undefined) throw new KvboxError(\"could not start firecracker\");\n try {\n if (!(await waitFor(() => existsSync(m.sock), 3000))) {\n throw new KvboxError(`firecracker did not open its API socket; last log lines:\\n${await tailOf(m.log)}`);\n }\n const fc = new FirecrackerClient(m.sock);\n try {\n await fc.bootSource(cfg.kernel, bootArgs(spec.name, spec.ip, cfg, pubkeyB64));\n await fc.rootDrive(m.disk);\n await fc.machineConfig(spec.vcpu, spec.memMib);\n await fc.networkInterface(\"eth0\", tap, spec.mac);\n await fc.start();\n } finally {\n await fc.close();\n }\n return pid;\n } catch (err) {\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n /* already gone */\n }\n throw err;\n }\n}\n\nasync function readPubkeyB64(p: Paths): Promise<string> {\n try {\n return Buffer.from((await readFile(`${p.sshKey}.pub`, \"utf8\")).trim()).toString(\"base64\");\n } catch {\n throw new KvboxError(`SSH key missing at ${p.sshKey}.pub; run install-server.sh`);\n }\n}\n\nfunction assertAssets(cfg: Config): void {\n for (const [what, file] of [[\"kernel\", cfg.kernel], [\"base image\", cfg.rootfs], [\"firecracker\", cfg.firecracker]] as const) {\n if (!existsSync(file)) throw new KvboxError(`${what} missing at ${file}; run install-server.sh`);\n }\n}\n\nexport async function createMachine(req: CreateMachineRequest, p: Paths = paths()): Promise<Machine> {\n if (req.from !== undefined) throw new NotImplementedError(\"new --from <snapshot> (#13)\");\n const cfg = await loadConfig(p);\n const sizeName = req.size ?? cfg.defaultSize;\n if (!isSizeName(sizeName)) throw new KvboxError(`unknown size \"${sizeName}\"; use one of ${Object.keys(SIZES).join(\", \")}`);\n const size = SIZES[sizeName];\n assertAssets(cfg);\n const pubkeyB64 = await readPubkeyB64(p);\n\n const existing = await readAllMeta(p);\n const name = pickName(req.name, new Set(existing.map((m) => m.name)));\n const ip = nextFreeIp(cfg.subnet, [...existing.map((m) => m.ip), cfg.gateway]);\n const mac = macFor(ip);\n const m = p.machine(name);\n\n await ensureHostNetwork(cfg);\n await mkdir(m.dir, { recursive: true, mode: 0o700 });\n let tapCreated = false;\n try {\n // the box's own disk: instant on btrfs/xfs (reflink), a sparse copy elsewhere\n await run([\"cp\", \"--reflink=auto\", \"--sparse=always\", cfg.rootfs, m.disk], { timeoutMs: 120_000 });\n const tap = await createTap(name, cfg);\n tapCreated = true;\n const pid = await launch({ name, ip, mac, vcpu: size.vcpu, memMib: size.memMib }, tap, cfg, pubkeyB64, p);\n const meta: MachineMeta = { name, ip, mac, pid, vcpu: size.vcpu, memMib: size.memMib, createdAt: new Date().toISOString(), source: \"base\" };\n await writeMachineMeta(meta, p);\n return toMachine(meta);\n } catch (err) {\n // leave nothing half-made behind\n if (tapCreated) await deleteTap(name, cfg).catch(() => undefined);\n await rm(m.dir, { recursive: true, force: true });\n throw err;\n }\n}\n\n/** Clean guest shutdown (Ctrl+Alt+Del), SIGKILL after `graceMs`; tap removed; disk and meta kept. */\nasync function shutdown(meta: MachineMeta, cfg: Config, p: Paths, graceMs: number): Promise<void> {\n const pid = meta.pid;\n if (pidAlive(pid)) {\n const fc = new FirecrackerClient(p.machine(meta.name).sock);\n try {\n await fc.sendCtrlAltDel();\n } catch {\n process.kill(pid!, \"SIGTERM\");\n } finally {\n await fc.close();\n }\n if (!(await waitFor(() => !pidAlive(pid), graceMs, 100))) {\n try {\n process.kill(pid!, \"SIGKILL\");\n } catch {\n /* exited meanwhile */\n }\n await waitFor(() => !pidAlive(pid), 1000, 50);\n }\n }\n await deleteTap(meta.name, cfg);\n await rm(p.machine(meta.name).sock, { force: true });\n}\n\nexport async function stopMachine(name: string, p: Paths = paths()): Promise<Machine> {\n const meta = await readMeta(name, p);\n const cfg = await loadConfig(p);\n if (!pidAlive(meta.pid)) throw new KvboxError(`box ${name} is already stopped`);\n await shutdown(meta, cfg, p, 10_000);\n const stopped: MachineMeta = { ...meta, pid: null };\n await writeMachineMeta(stopped, p);\n return toMachine(stopped);\n}\n\nexport async function startMachine(name: string, p: Paths = paths()): Promise<Machine> {\n const meta = await readMeta(name, p);\n const cfg = await loadConfig(p);\n if (pidAlive(meta.pid)) throw new KvboxError(`box ${name} is already running`);\n assertAssets(cfg);\n const pubkeyB64 = await readPubkeyB64(p);\n await ensureHostNetwork(cfg);\n const tap = await createTap(name, cfg);\n try {\n const pid = await launch(meta, tap, cfg, pubkeyB64, p);\n const started: MachineMeta = { ...meta, pid };\n await writeMachineMeta(started, p);\n return toMachine(started);\n } catch (err) {\n await deleteTap(name, cfg).catch(() => undefined);\n throw err;\n }\n}\n\nasync function readMeta(name: string, p: Paths): Promise<MachineMeta> {\n try {\n return await readMachineMeta(name, p);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") throw new NotFoundError(`box ${name}`);\n throw err;\n }\n}\n\nexport async function destroyMachine(name: string, p: Paths = paths()): Promise<void> {\n const meta = await readMeta(name, p);\n const cfg = await loadConfig(p);\n await shutdown(meta, cfg, p, 2000);\n await rm(p.machine(name).dir, { recursive: true, force: true });\n}\n","/**\n * Client for one Firecracker process's API socket. Ten-odd endpoints, all\n * PUT/PATCH with JSON bodies, so a thin wrapper over undici beats an SDK.\n */\nimport { Agent, request } from \"undici\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\n\nexport class FirecrackerClient {\n private readonly agent: Agent;\n\n constructor(readonly socketPath: string) {\n this.agent = new Agent({ connect: { socketPath } });\n }\n\n private async call(method: \"GET\" | \"PUT\" | \"PATCH\", path: string, body?: unknown): Promise<unknown> {\n const res = await request(`http://localhost${path}`, {\n dispatcher: this.agent,\n method,\n headers: body === undefined ? {} : { \"content-type\": \"application/json\" },\n body: body === undefined ? null : JSON.stringify(body),\n });\n const text = await res.body.text();\n if (res.statusCode >= 300) {\n throw new KvboxError(`firecracker ${method} ${path} -> ${res.statusCode}: ${text}`);\n }\n return text ? JSON.parse(text) : undefined;\n }\n\n bootSource(kernelImagePath: string, bootArgs: string) {\n return this.call(\"PUT\", \"/boot-source\", { kernel_image_path: kernelImagePath, boot_args: bootArgs });\n }\n\n rootDrive(pathOnHost: string) {\n return this.call(\"PUT\", \"/drives/rootfs\", {\n drive_id: \"rootfs\",\n path_on_host: pathOnHost,\n is_root_device: true,\n is_read_only: false,\n // \"Unsafe\" (the default) acks guest flushes without doing anything, so the last journal\n // commit of a clean shutdown can be lost when the guest resets and Firecracker exits.\n // Writeback makes a guest flush a real fsync; the box's disk must survive `kvbox stop`.\n cache_type: \"Writeback\",\n });\n }\n\n machineConfig(vcpuCount: number, memSizeMib: number) {\n return this.call(\"PUT\", \"/machine-config\", { vcpu_count: vcpuCount, mem_size_mib: memSizeMib });\n }\n\n networkInterface(ifaceId: string, hostDevName: string, guestMac: string) {\n return this.call(\"PUT\", `/network-interfaces/${ifaceId}`, {\n iface_id: ifaceId,\n host_dev_name: hostDevName,\n guest_mac: guestMac,\n });\n }\n\n start() {\n return this.call(\"PUT\", \"/actions\", { action_type: \"InstanceStart\" });\n }\n\n /** Ask the guest to shut down cleanly (systemd handles Ctrl+Alt+Del as poweroff on Firecracker). */\n sendCtrlAltDel() {\n return this.call(\"PUT\", \"/actions\", { action_type: \"SendCtrlAltDel\" });\n }\n\n pause() {\n return this.call(\"PATCH\", \"/vm\", { state: \"Paused\" });\n }\n\n resume() {\n return this.call(\"PATCH\", \"/vm\", { state: \"Resumed\" });\n }\n\n snapshotCreate(snapshotPath: string, memFilePath: string) {\n return this.call(\"PUT\", \"/snapshot/create\", {\n snapshot_type: \"Full\",\n snapshot_path: snapshotPath,\n mem_file_path: memFilePath,\n });\n }\n\n snapshotLoad(snapshotPath: string, memFilePath: string, resume = true) {\n return this.call(\"PUT\", \"/snapshot/load\", {\n snapshot_path: snapshotPath,\n mem_backend: { backend_type: \"File\", backend_path: memFilePath },\n resume_vm: resume,\n });\n }\n\n info() {\n return this.call(\"GET\", \"/\");\n }\n\n async close() {\n await this.agent.close();\n }\n}\n","import { Hono } from \"hono\";\nimport { CreateSnapshotRequest } from \"@kvbox/shared/api\";\nimport { deleteSnapshot, listSnapshots, saveSnapshot } from \"../core/snapshots.js\";\n\nexport function snapshotRoutes() {\n const r = new Hono();\n r.get(\"/\", async (c) => c.json(await listSnapshots()));\n r.post(\"/\", async (c) => {\n const body = CreateSnapshotRequest.parse(await c.req.json());\n return c.json(await saveSnapshot(body), 201);\n });\n r.delete(\"/:name\", async (c) => {\n await deleteSnapshot(c.req.param(\"name\"));\n return c.body(null, 204);\n });\n return r;\n}\n","/** Full snapshots (vmstate + mem + disk copy) and their listing. */\nimport { NotImplementedError } from \"@kvbox/shared/errors\";\nimport type { CreateSnapshotRequest, Snapshot } from \"@kvbox/shared/api\";\n\nexport async function listSnapshots(): Promise<Snapshot[]> {\n throw new NotImplementedError(\"snapshots.listSnapshots\");\n}\n\nexport async function saveSnapshot(_req: CreateSnapshotRequest): Promise<Snapshot> {\n throw new NotImplementedError(\"snapshots.saveSnapshot\");\n}\n\nexport async function deleteSnapshot(_name: string): Promise<void> {\n throw new NotImplementedError(\"snapshots.deleteSnapshot\");\n}\n"],"mappings":"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","names":["rm","writeFile","readFile","writeFile","z","z","readFile","writeFile","Hono","require","Hono","z","z","existsSync","mkdir","readFile","bootArgs","readFile","existsSync","m","mkdir","Hono","Hono","Hono","Hono","rm","writeFile","shutdown"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../shared/src/errors.ts","../../shared/src/paths.ts","../src/core/config.ts","../../shared/src/sizes.ts","../src/core/network.ts","../src/core/exec.ts","../src/listen.ts","../src/core/state.ts","../src/app.ts","../../shared/src/version.ts","../src/version.ts","../src/routes/capacity.ts","../src/core/capacity.ts","../src/routes/machines.ts","../../shared/src/api.ts","../src/core/machines.ts","../src/core/firecracker.ts","../src/routes/snapshots.ts","../src/core/snapshots.ts"],"sourcesContent":["/**\n * kvbox-server: the host daemon. `kvbox-server` (or `kvbox-server start`) serves\n * the kvbox API on ~/.kvbox/server.sock in the foreground; the CLI's\n * `kvbox server start` spawns it detached. M2 adds `register`.\n */\nimport { spawnSync } from \"node:child_process\";\nimport { Command } from \"commander\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\nimport { paths } from \"@kvbox/shared/paths\";\nimport { loadConfig } from \"./core/config.js\";\nimport { ensureHostNetwork, netSetupScript } from \"./core/network.js\";\nimport { listen } from \"./listen.js\";\nimport { VERSION } from \"./version.js\";\n\nconst program = new Command(\"kvbox-server\")\n .description(\"kvbox host daemon: owns Firecracker and ~/.kvbox, serves the kvbox API\")\n .version(VERSION, \"-v, --version\")\n .showHelpAfterError();\n\nprogram\n .command(\"start\", { isDefault: true })\n .description(\"serve the API on the local Unix socket (foreground)\")\n .option(\"--socket <path>\", \"socket to listen on (default: $KVBOX_HOME/server.sock)\")\n .action(async (opts: { socket?: string }) => {\n const { socketPath } = await listen(opts.socket ? { socketPath: opts.socket } : {});\n console.log(`kvbox-server ${VERSION} listening on ${socketPath} (pid ${process.pid})`);\n // The bridge vanishes on host reboot; recreate it. Not fatal: ls/status must still work.\n try {\n const cfg = await loadConfig(paths());\n const state = await ensureHostNetwork(cfg);\n console.log(`host network: bridge ${cfg.bridge} ${state}`);\n } catch (err) {\n console.error(`warning: host network not ready (${(err as Error).message}); run: kvbox-server net up`);\n }\n });\n\nprogram\n .command(\"net <action>\")\n .description(\"host network: up | down | status (bridge, forwarding, NAT; uses sudo)\")\n .action(async (action: string) => {\n if (![\"up\", \"down\", \"status\"].includes(action)) throw new KvboxError(\"net: expected up, down or status\");\n const cfg = await loadConfig(paths());\n const res = spawnSync(netSetupScript(), [action], {\n stdio: \"inherit\",\n env: { ...process.env, KVBOX_BRIDGE: cfg.bridge, KVBOX_SUBNET: cfg.subnet, KVBOX_GATEWAY: cfg.gateway },\n });\n process.exit(res.status ?? 1);\n });\n\ntry {\n await program.parseAsync(process.argv);\n} catch (err) {\n if (err instanceof KvboxError) {\n console.error(`error: ${err.message}`);\n process.exit(err.exitCode);\n }\n throw err;\n}\n","/** Errors with an exit code; the entry point prints `message` and exits with `exitCode`. */\nexport class KvboxError extends Error {\n constructor(\n message: string,\n public readonly exitCode: number = 1,\n ) {\n super(message);\n this.name = \"KvboxError\";\n }\n}\n\nexport class NotImplementedError extends KvboxError {\n constructor(what: string) {\n super(`${what}: not implemented yet`, 2);\n this.name = \"NotImplementedError\";\n }\n}\n\n/** The named box/snapshot/host does not exist: HTTP 404, exit 1. */\nexport class NotFoundError extends KvboxError {\n constructor(what: string) {\n super(`${what}: not found`, 1);\n this.name = \"NotFoundError\";\n }\n}\n","/**\n * Host state layout. The filesystem is the database and `~/.kvbox` is its root.\n * This module is dependency-free so both the CLI (socket path, hosts.json)\n * and core (everything else) can use it without crossing the layer boundary.\n *\n * `KVBOX_HOME` overrides the root, mainly for tests.\n */\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport function kvboxHome(): string {\n return process.env[\"KVBOX_HOME\"] ?? join(homedir(), \".kvbox\");\n}\n\nexport function paths(home: string = kvboxHome()) {\n return {\n home,\n config: join(home, \"config.json\"),\n serverSock: join(home, \"server.sock\"),\n serverPid: join(home, \"server.pid\"),\n serverLog: join(home, \"server.log\"),\n hosts: join(home, \"hosts.json\"),\n ssh: join(home, \"ssh\"),\n sshKey: join(home, \"ssh\", \"id_ed25519\"),\n bin: join(home, \"bin\"),\n firecracker: join(home, \"bin\", \"firecracker\"),\n assets: join(home, \"assets\"),\n kernel: join(home, \"assets\", \"vmlinux\"),\n /** Symlink to the current base image, assets/rootfs-<version>.ext4; boxes keep using the version they were made from. */\n rootfs: join(home, \"assets\", \"rootfs.ext4\"),\n baseImage: (version: string) => join(home, \"assets\", `rootfs-${version}.ext4`),\n machines: join(home, \"machines\"),\n snapshots: join(home, \"snapshots\"),\n machine: (name: string) => ({\n dir: join(home, \"machines\", name),\n meta: join(home, \"machines\", name, \"meta.json\"),\n /** The box's writable layer, stacked on the read-only base image inside the guest. */\n overlay: join(home, \"machines\", name, \"overlay.ext4\"),\n /** Boxes made before overlays: a full copy of the base image. Kept bootable, never created anymore. */\n disk: join(home, \"machines\", name, \"disk.ext4\"),\n sock: join(home, \"machines\", name, \"fc.sock\"),\n log: join(home, \"machines\", name, \"fc.log\"),\n }),\n snapshot: (name: string) => ({\n dir: join(home, \"snapshots\", name),\n meta: join(home, \"snapshots\", name, \"meta.json\"),\n vmstate: join(home, \"snapshots\", name, \"vmstate\"),\n mem: join(home, \"snapshots\", name, \"mem\"),\n disk: join(home, \"snapshots\", name, \"disk.ext4\"),\n }),\n } as const;\n}\n\nexport type Paths = ReturnType<typeof paths>;\n","/** `~/.kvbox/config.json`: asset paths, subnet, default size. Written with defaults on first run. */\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport { paths, type Paths } from \"@kvbox/shared/paths\";\nimport { DEFAULT_SIZE, SIZE_NAMES } from \"@kvbox/shared/sizes\";\n\nexport const ConfigSchema = z.object({\n kernel: z.string(),\n rootfs: z.string(),\n firecracker: z.string(),\n bridge: z.string().default(\"kvbox0\"),\n subnet: z.string().default(\"172.30.0.0/24\"),\n gateway: z.string().default(\"172.30.0.1\"),\n defaultSize: z.enum(SIZE_NAMES as [string, ...string[]]).default(DEFAULT_SIZE),\n});\nexport type Config = z.infer<typeof ConfigSchema>;\n\nexport function defaultConfig(p: Paths = paths()): Config {\n return ConfigSchema.parse({\n kernel: p.kernel,\n rootfs: p.rootfs,\n firecracker: p.firecracker,\n });\n}\n\nexport async function loadConfig(p: Paths = paths()): Promise<Config> {\n try {\n const raw = await readFile(p.config, \"utf8\");\n return ConfigSchema.parse(JSON.parse(raw));\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n const cfg = defaultConfig(p);\n await writeFile(p.config, JSON.stringify(cfg, null, 2) + \"\\n\");\n return cfg;\n }\n throw err;\n }\n}\n","/**\n * Fixed box sizes. Firecracker backs guest memory lazily and the disk is a sparse overlay on the\n * shared base image, so a box only costs what the guest actually touches.\n */\nexport const SIZES = {\n s: { vcpu: 2, memMib: 4096, diskGib: 8 },\n m: { vcpu: 4, memMib: 8192, diskGib: 16 },\n l: { vcpu: 8, memMib: 16384, diskGib: 32 },\n} as const;\n\nexport type SizeName = keyof typeof SIZES;\nexport const SIZE_NAMES = Object.keys(SIZES) as SizeName[];\nexport const DEFAULT_SIZE: SizeName = \"s\";\n\nexport function isSizeName(value: string): value is SizeName {\n return value in SIZES;\n}\n","/**\n * Host networking: bridge `kvbox0`, one tap `kv-<box>` per box, IP allocation\n * inside the subnet. Everything that needs root goes through one script,\n * `net-setup.sh`, which re-execs itself with `sudo -n` when run from here.\n * `install-server.sh` installs a root-owned copy at PRIVILEGED_SCRIPT plus a\n * sudoers rule for exactly that path; the package copy is the dev fallback.\n */\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\nimport type { Config } from \"./config.js\";\nimport { run } from \"./exec.js\";\n\nexport const TAP_PREFIX = \"kv-\";\nexport const PRIVILEGED_SCRIPT = \"/usr/local/lib/kvbox/net-setup.sh\";\n\ntype NetConfig = Pick<Config, \"bridge\" | \"subnet\" | \"gateway\">;\n\nexport function tapName(box: string): string {\n // Linux interface names are limited to 15 characters.\n return `${TAP_PREFIX}${box}`.slice(0, 15);\n}\n\n/** The script to run: KVBOX_NET_SETUP override, the root-owned install, else the package copy (dist/ or src/core/). */\nexport function netSetupScript(): string {\n const override = process.env[\"KVBOX_NET_SETUP\"];\n if (override) return override;\n if (existsSync(PRIVILEGED_SCRIPT)) return PRIVILEGED_SCRIPT;\n for (const rel of [\"../scripts/net-setup.sh\", \"../../scripts/net-setup.sh\"]) {\n const candidate = fileURLToPath(new URL(rel, import.meta.url));\n if (existsSync(candidate)) return candidate;\n }\n throw new KvboxError(\"net-setup.sh not found next to kvbox-server\");\n}\n\nexport function bridgeExists(bridge: string): boolean {\n return existsSync(`/sys/class/net/${bridge}`);\n}\n\nfunction netRun(cfg: NetConfig, args: string[]) {\n return run([netSetupScript(), ...args], {\n env: { ...process.env, KVBOX_BRIDGE: cfg.bridge, KVBOX_SUBNET: cfg.subnet, KVBOX_GATEWAY: cfg.gateway },\n }).catch((err: Error) => {\n throw new KvboxError(\n `${err.message}\\n (root is needed for host networking; install-server.sh sets up a sudoers rule for ${PRIVILEGED_SCRIPT})`,\n );\n });\n}\n\n/** Run `net-setup.sh up` when the bridge is missing, which is the case after every host reboot. */\nexport async function ensureHostNetwork(cfg: NetConfig): Promise<\"present\" | \"created\"> {\n if (bridgeExists(cfg.bridge)) return \"present\";\n await netRun(cfg, [\"up\"]);\n return \"created\";\n}\n\n/** Create the box's tap (owned by this user, plugged into the bridge) and return its name. */\nexport async function createTap(box: string, cfg: NetConfig): Promise<string> {\n const tap = tapName(box);\n await netRun(cfg, [\"tap\", \"add\", tap]);\n return tap;\n}\n\nexport async function deleteTap(box: string, cfg: NetConfig): Promise<void> {\n await netRun(cfg, [\"tap\", \"del\", tapName(box)]);\n}\n\n// ── addresses ────────────────────────────────────────────────────────────────\nconst ipToInt = (ip: string) => ip.split(\".\").reduce((acc, o) => (acc << 8) + Number(o), 0) >>> 0;\nconst intToIp = (n: number) => [24, 16, 8, 0].map((s) => (n >>> s) & 255).join(\".\");\n\n/** Lowest free host address in `subnet` (a.b.c.d/len), skipping network, gateway (.1) and broadcast. */\nexport function nextFreeIp(subnet: string, inUse: readonly string[]): string {\n const [base, lenStr] = subnet.split(\"/\");\n const len = Number(lenStr);\n if (!base || !Number.isInteger(len) || len < 8 || len > 30) throw new KvboxError(`bad subnet: ${subnet}`);\n const mask = len === 0 ? 0 : (~0 << (32 - len)) >>> 0;\n const network = (ipToInt(base) & mask) >>> 0;\n const broadcast = (network | (~mask >>> 0)) >>> 0;\n const used = new Set(inUse.map(ipToInt));\n for (let n = network + 2; n < broadcast; n++) {\n if (!used.has(n)) return intToIp(n);\n }\n throw new KvboxError(`no free IP left in ${subnet}`);\n}\n\n/** Locally administered MAC derived from the IP: 06:00:<a>:<b>:<c>:<d>, unique per box and readable. */\nexport function macFor(ip: string): string {\n const hex = ip.split(\".\").map((o) => Number(o).toString(16).padStart(2, \"0\").toUpperCase());\n return [\"06\", \"00\", ...hex].join(\":\");\n}\n","/**\n * The one shell-out helper. Every `firecracker`, `ip`, `ssh`, `cp` call goes\n * through here: argv arrays only (never a shell string), the command is logged,\n * and stderr ends up in the thrown error.\n */\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\n\nconst execFileAsync = promisify(execFile);\n\nexport interface RunOptions {\n sudo?: boolean;\n cwd?: string;\n timeoutMs?: number;\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface RunResult {\n stdout: string;\n stderr: string;\n}\n\nexport async function run(argv: readonly string[], opts: RunOptions = {}): Promise<RunResult> {\n const [cmd, ...args] = opts.sudo ? [\"sudo\", \"-n\", ...argv] : argv;\n if (!cmd) throw new KvboxError(\"run: empty argv\");\n if (process.env[\"KVBOX_DEBUG\"]) console.error(`[exec] ${[cmd, ...args].join(\" \")}`);\n try {\n const { stdout, stderr } = await execFileAsync(cmd, args, {\n cwd: opts.cwd,\n env: opts.env ?? process.env,\n timeout: opts.timeoutMs ?? 30_000,\n maxBuffer: 8 * 1024 * 1024,\n });\n return { stdout, stderr };\n } catch (err) {\n const e = err as NodeJS.ErrnoException & { stderr?: string; code?: number | string };\n const detail = (e.stderr ?? e.message ?? \"\").trim();\n throw new KvboxError(`${[cmd, ...args].join(\" \")} failed${detail ? `: ${detail}` : \"\"}`);\n }\n}\n","/** Bind the app to the local Unix socket and write the pid file. */\nimport { rm, writeFile } from \"node:fs/promises\";\nimport { createAdaptorServer } from \"@hono/node-server\";\nimport { ensureLayout } from \"./core/state.js\";\nimport { loadConfig } from \"./core/config.js\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\nimport { paths } from \"@kvbox/shared/paths\";\nimport { createApp } from \"./app.js\";\n\nexport interface ListenOptions {\n socketPath?: string;\n}\n\nexport async function listen(opts: ListenOptions = {}) {\n const p = await ensureLayout(paths());\n await loadConfig(p);\n const socketPath = opts.socketPath ?? p.serverSock;\n // Linux limits a Unix socket path (sun_path) to 108 bytes; bind() fails with EINVAL past it.\n if (Buffer.byteLength(socketPath) > 100) {\n throw new KvboxError(`socket path too long for a Unix socket (${socketPath}); set KVBOX_HOME to a shorter path`);\n }\n\n // A stale socket file from a crashed server blocks bind(); the pid file tells\n // `server status` whether anything is actually alive.\n await rm(socketPath, { force: true });\n\n const server = createAdaptorServer({ fetch: createApp().fetch });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(socketPath, () => resolve());\n });\n await writeFile(p.serverPid, `${process.pid}\\n`);\n\n const shutdown = async () => {\n server.close();\n await rm(socketPath, { force: true });\n await rm(p.serverPid, { force: true });\n process.exit(0);\n };\n process.once(\"SIGINT\", () => void shutdown());\n process.once(\"SIGTERM\", () => void shutdown());\n\n return { socketPath, server };\n}\n","/**\n * The `~/.kvbox` layout and the per-box / per-snapshot `meta.json` files.\n * The server is the only writer; every read is validated with zod.\n */\nimport { mkdir, readFile, readdir, writeFile } from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport { paths, type Paths } from \"@kvbox/shared/paths\";\n\nexport const MachineMetaSchema = z.object({\n name: z.string(),\n ip: z.string(),\n mac: z.string(),\n pid: z.number().int().nullable(), // null: stopped on purpose, disk kept\n vcpu: z.number().int(),\n memMib: z.number().int(),\n createdAt: z.string(),\n source: z.string(),\n /** Base image version (assets/rootfs-<version>.ext4) the box overlays. Absent: a pre-overlay box with its own disk.ext4. */\n base: z.string().optional(),\n});\nexport type MachineMeta = z.infer<typeof MachineMetaSchema>;\n\nexport const SnapshotMetaSchema = z.object({\n name: z.string(),\n source: z.string(),\n vcpu: z.number().int(),\n memMib: z.number().int(),\n createdAt: z.string(),\n});\nexport type SnapshotMeta = z.infer<typeof SnapshotMetaSchema>;\n\n/** Create every directory of the layout. Idempotent; called by `kvbox server start`. */\nexport async function ensureLayout(p: Paths = paths()): Promise<Paths> {\n for (const dir of [p.home, p.ssh, p.bin, p.assets, p.machines, p.snapshots]) {\n await mkdir(dir, { recursive: true, mode: 0o700 });\n }\n return p;\n}\n\nasync function readJson<T>(file: string, schema: z.ZodType<T>): Promise<T> {\n return schema.parse(JSON.parse(await readFile(file, \"utf8\")));\n}\n\nasync function listDirs(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw err;\n }\n}\n\nexport async function readMachineMeta(name: string, p: Paths = paths()) {\n return readJson(p.machine(name).meta, MachineMetaSchema);\n}\n\nexport async function writeMachineMeta(meta: MachineMeta, p: Paths = paths()) {\n await mkdir(p.machine(meta.name).dir, { recursive: true });\n await writeFile(p.machine(meta.name).meta, JSON.stringify(meta, null, 2) + \"\\n\");\n}\n\nexport async function listMachineNames(p: Paths = paths()) {\n return listDirs(p.machines);\n}\n\nexport async function readSnapshotMeta(name: string, p: Paths = paths()) {\n return readJson(p.snapshot(name).meta, SnapshotMetaSchema);\n}\n\nexport async function writeSnapshotMeta(meta: SnapshotMeta, p: Paths = paths()) {\n await mkdir(p.snapshot(meta.name).dir, { recursive: true });\n await writeFile(p.snapshot(meta.name).meta, JSON.stringify(meta, null, 2) + \"\\n\");\n}\n\nexport async function listSnapshotNames(p: Paths = paths()) {\n return listDirs(p.snapshots);\n}\n\n/** True when a pid is alive (signal 0). */\nexport function pidAlive(pid: number | null): boolean {\n if (pid === null || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n","/**\n * The hono app behind `kvbox-server`. Served over `~/.kvbox/server.sock`\n * in M1; the same app goes behind HTTPS in M2.\n */\nimport { Hono } from \"hono\";\nimport { KvboxError, NotFoundError } from \"@kvbox/shared/errors\";\nimport { VERSION } from \"./version.js\";\nimport { capacityRoutes } from \"./routes/capacity.js\";\nimport { machineRoutes } from \"./routes/machines.js\";\nimport { snapshotRoutes } from \"./routes/snapshots.js\";\n\nexport function createApp() {\n const app = new Hono();\n\n app.get(\"/health\", (c) => c.json({ ok: true as const, version: VERSION, pid: process.pid }));\n app.route(\"/machines\", machineRoutes());\n app.route(\"/snapshots\", snapshotRoutes());\n app.route(\"/capacity\", capacityRoutes());\n\n app.notFound((c) => c.json({ error: `no route: ${c.req.method} ${c.req.path}` }, 404));\n app.onError((err, c) => {\n if (err instanceof NotFoundError) return c.json({ error: err.message }, 404);\n if (err instanceof KvboxError) {\n return c.json({ error: err.message }, err.exitCode === 2 ? 501 : 400);\n }\n console.error(err);\n return c.json({ error: err.message }, 500);\n });\n\n return app;\n}\n\nexport type App = ReturnType<typeof createApp>;\n","/**\n * Each binary's version comes from its own package.json: tsup injects\n * `__KVBOX_VERSION__` at build time; the dev path (tsx) reads the file.\n * Usage in a package: `versionOf(import.meta.url)` from its `src/version.ts`.\n */\nimport { createRequire } from \"node:module\";\n\nexport function versionOf(srcFileUrl: string, injected?: string): string {\n if (typeof injected === \"string\") return injected;\n try {\n const require = createRequire(srcFileUrl);\n return (require(\"../package.json\") as { version: string }).version;\n } catch {\n return \"0.0.0-dev\";\n }\n}\n","import { versionOf } from \"@kvbox/shared/version\";\n\ndeclare const __KVBOX_VERSION__: string | undefined;\n\nexport const VERSION: string = versionOf(import.meta.url, typeof __KVBOX_VERSION__ === \"string\" ? __KVBOX_VERSION__ : undefined);\n","import { Hono } from \"hono\";\nimport { capacity } from \"../core/capacity.js\";\n\nexport function capacityRoutes() {\n const r = new Hono();\n r.get(\"/\", async (c) => c.json(await capacity()));\n return r;\n}\n","/**\n * The capacity guard: `kvbox new` refuses when reserved RAM of running boxes\n * plus the new one exceeds host RAM minus 2 GB (hard), or reserved vCPUs exceed\n * 2x host cores (soft). `--force` skips it.\n */\nimport { NotImplementedError } from \"@kvbox/shared/errors\";\nimport type { Capacity } from \"@kvbox/shared/api\";\n\nexport const MEM_HEADROOM_MIB = 2048;\nexport const VCPU_OVERSUBSCRIPTION = 2;\n\nexport async function capacity(): Promise<Capacity> {\n throw new NotImplementedError(\"capacity.capacity\");\n}\n\nexport async function assertRoomFor(_vcpu: number, _memMib: number, _force: boolean): Promise<void> {\n throw new NotImplementedError(\"capacity.assertRoomFor\");\n}\n","import { Hono } from \"hono\";\nimport { CreateMachineRequest } from \"@kvbox/shared/api\";\nimport { createMachine, destroyMachine, listMachines, startMachine, stopMachine } from \"../core/machines.js\";\n\nexport function machineRoutes() {\n const r = new Hono();\n r.get(\"/\", async (c) => c.json(await listMachines()));\n r.post(\"/\", async (c) => {\n const body = CreateMachineRequest.parse(await c.req.json().catch(() => ({})));\n return c.json(await createMachine(body), 201);\n });\n r.post(\"/:name/stop\", async (c) => c.json(await stopMachine(c.req.param(\"name\"))));\n r.post(\"/:name/start\", async (c) => c.json(await startMachine(c.req.param(\"name\"))));\n r.delete(\"/:name\", async (c) => {\n await destroyMachine(c.req.param(\"name\"));\n return c.body(null, 204);\n });\n return r;\n}\n","/**\n * Wire shapes of the kvbox HTTP API, shared by the client (src/cli) and the\n * server (src/server). Validated with zod on both sides because the client\n * may be talking to an older or newer server.\n */\nimport { z } from \"zod\";\nimport { SIZE_NAMES } from \"./sizes.js\";\n\nexport const SizeSchema = z.enum(SIZE_NAMES as [string, ...string[]]);\n\nexport const MachineSchema = z.object({\n name: z.string(),\n ip: z.string(),\n mac: z.string(),\n pid: z.number().int().nullable(),\n vcpu: z.number().int(),\n memMib: z.number().int(),\n diskBytes: z.number().int(), // the box's own writable disk, as the guest sees it\n diskUsedBytes: z.number().int(), // what that sparse file really occupies on the host\n base: z.string().nullable(), // base image version the box runs on (null: pre-overlay box with its own copy)\n createdAt: z.string(),\n source: z.string(), // \"base\" or \"snapshot:<name>\"\n status: z.enum([\"running\", \"stopped\"]),\n});\nexport type Machine = z.infer<typeof MachineSchema>;\n\nexport const CreateMachineRequest = z.object({\n name: z.string().optional(),\n size: SizeSchema.optional(),\n from: z.string().optional(),\n force: z.boolean().optional(),\n});\nexport type CreateMachineRequest = z.infer<typeof CreateMachineRequest>;\n\nexport const SnapshotSchema = z.object({\n name: z.string(),\n source: z.string(),\n vcpu: z.number().int(),\n memMib: z.number().int(),\n createdAt: z.string(),\n});\nexport type Snapshot = z.infer<typeof SnapshotSchema>;\n\nexport const CreateSnapshotRequest = z.object({\n box: z.string(),\n name: z.string(),\n});\nexport type CreateSnapshotRequest = z.infer<typeof CreateSnapshotRequest>;\n\nexport const CapacitySchema = z.object({\n hostMemMib: z.number().int(),\n hostCores: z.number().int(),\n reservedMemMib: z.number().int(),\n reservedVcpu: z.number().int(),\n running: z.number().int(),\n});\nexport type Capacity = z.infer<typeof CapacitySchema>;\n\nexport const HealthSchema = z.object({\n ok: z.literal(true),\n version: z.string(),\n pid: z.number().int(),\n});\nexport type Health = z.infer<typeof HealthSchema>;\n\nexport const ApiErrorSchema = z.object({\n error: z.string(),\n});\n","/**\n * Create, list and destroy boxes: what spikes #4 and #6 did by hand.\n * See architecture \"Process model\". The server is the only writer of ~/.kvbox/machines.\n */\nimport { spawn } from \"node:child_process\";\nimport { closeSync, existsSync, openSync, statSync } from \"node:fs\";\nimport { mkdir, readFile, realpath, rm, truncate, writeFile } from \"node:fs/promises\";\nimport { basename } from \"node:path\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { KvboxError, NotFoundError, NotImplementedError } from \"@kvbox/shared/errors\";\nimport type { CreateMachineRequest, Machine } from \"@kvbox/shared/api\";\nimport { paths, type Paths } from \"@kvbox/shared/paths\";\nimport { SIZES, isSizeName } from \"@kvbox/shared/sizes\";\nimport { loadConfig, type Config } from \"./config.js\";\nimport { run } from \"./exec.js\";\nimport { FirecrackerClient } from \"./firecracker.js\";\nimport { createTap, deleteTap, ensureHostNetwork, macFor, nextFreeIp } from \"./network.js\";\nimport { listMachineNames, pidAlive, readMachineMeta, writeMachineMeta, type MachineMeta } from \"./state.js\";\n\n/** Box names double as hostnames and tap suffixes (kv- + name ≤ 15 chars). */\nconst NAME_RE = /^[a-z0-9][a-z0-9-]{0,11}$/;\nconst AUTO_PREFIX = \"box\";\n\nfunction diskStats(disk: string): { diskBytes: number; diskUsedBytes: number } {\n try {\n const st = statSync(disk);\n return { diskBytes: st.size, diskUsedBytes: st.blocks * 512 };\n } catch {\n return { diskBytes: 0, diskUsedBytes: 0 };\n }\n}\n\n/** A box's own disk file: its overlay, or the full copy pre-overlay boxes were made with. */\nfunction ownDisk(meta: MachineMeta, p: Paths): string {\n const m = p.machine(meta.name);\n return meta.base === undefined ? m.disk : m.overlay;\n}\n\nfunction toMachine(meta: MachineMeta, p: Paths = paths()): Machine {\n const alive = pidAlive(meta.pid);\n return {\n name: meta.name,\n ip: meta.ip,\n mac: meta.mac,\n pid: alive ? meta.pid : null,\n vcpu: meta.vcpu,\n memMib: meta.memMib,\n ...diskStats(ownDisk(meta, p)),\n base: meta.base ?? null,\n createdAt: meta.createdAt,\n source: meta.source,\n status: alive ? \"running\" : \"stopped\",\n };\n}\n\nasync function readAllMeta(p: Paths): Promise<MachineMeta[]> {\n const metas: MachineMeta[] = [];\n for (const name of await listMachineNames(p)) {\n try {\n metas.push(await readMachineMeta(name, p));\n } catch (err) {\n console.error(`warning: skipping machines/${name}: ${(err as Error).message}`);\n }\n }\n return metas;\n}\n\nexport async function listMachines(p: Paths = paths()): Promise<Machine[]> {\n return (await readAllMeta(p)).map((m) => toMachine(m, p));\n}\n\nexport async function getMachine(name: string, p: Paths = paths()): Promise<Machine> {\n return toMachine(await readMeta(name, p), p);\n}\n\nfunction pickName(requested: string | undefined, taken: Set<string>): string {\n if (requested !== undefined) {\n if (!NAME_RE.test(requested)) {\n throw new KvboxError(`invalid box name \"${requested}\": 1-12 chars, lowercase letters, digits and '-', starting with a letter or digit`);\n }\n if (taken.has(requested)) throw new KvboxError(`box ${requested} already exists`);\n return requested;\n }\n for (let i = 1; ; i++) {\n const candidate = `${AUTO_PREFIX}${i}`;\n if (!taken.has(candidate)) return candidate;\n }\n}\n\nexport function bootArgs(name: string, ip: string, cfg: Config, pubkeyB64: string, mode: { overlay: boolean }): string {\n // Firecracker appends `pci=off root=/dev/vda` plus ro/rw itself. ip= is read by the guest\n // kernel, kvbox.name= / kvbox.sshkey= by kvbox-init on first boot (the kvbox-images contract).\n // With an overlay, /sbin/overlay-init in the image stacks the box's disk (/dev/vdb) on the\n // read-only base before systemd starts.\n const init = mode.overlay ? \" init=/sbin/overlay-init kvbox.overlay=/dev/vdb\" : \"\";\n const args = `console=ttyS0 reboot=k panic=1${init} ip=${ip}::${cfg.gateway}:255.255.255.0:${name}:eth0:off kvbox.name=${name} kvbox.sshkey=${pubkeyB64}`;\n return process.arch === \"arm64\" ? `keep_bootcon ${args}` : args;\n}\n\n/** `assets/rootfs-<version>.ext4` -> `<version>`; anything else is the layout from before overlays. */\nexport function baseVersionOf(file: string): string {\n const m = /^rootfs-(.+)\\.ext4$/.exec(basename(file));\n if (!m) throw new KvboxError(`base image ${file} is not versioned (expected assets/rootfs-<version>.ext4); run install-server.sh`);\n return m[1]!;\n}\n\n/** Follow config's rootfs symlink to the base image a new box should be made from. */\nasync function currentBase(cfg: Config, p: Paths): Promise<{ version: string; file: string }> {\n const file = await realpath(cfg.rootfs);\n const version = baseVersionOf(file);\n const expected = p.baseImage(version);\n if (file !== expected) throw new KvboxError(`base image ${cfg.rootfs} points outside ${p.assets} (${file})`);\n return { version, file };\n}\n\n/** The base image this box boots from; a pre-overlay box has none (its own disk copy is the root). */\nfunction baseFileFor(meta: Pick<MachineMeta, \"name\" | \"base\">, p: Paths): string | undefined {\n if (meta.base === undefined) return undefined;\n const file = p.baseImage(meta.base);\n if (!existsSync(file)) throw new KvboxError(`box ${meta.name} runs on base image ${meta.base}, which is gone from ${p.assets}`);\n return file;\n}\n\nconst overlayCapable = new Map<string, boolean>();\n\n/** A base image older than the overlay layout has no /sbin/overlay-init and would panic at boot. debugfs reads the ext4 without mounting it. */\nasync function assertOverlayCapable(baseFile: string, version: string): Promise<void> {\n let ok = overlayCapable.get(baseFile);\n if (ok === undefined) {\n const { stdout } = await run([\"debugfs\", \"-R\", \"stat /sbin/overlay-init\", baseFile]);\n ok = /Type:\\s+regular/.test(stdout);\n overlayCapable.set(baseFile, ok);\n }\n if (!ok) throw new KvboxError(`base image ${version} is too old for this server (no /sbin/overlay-init); rerun install-server.sh to get a newer one`);\n}\n\n/** The box's writable disk: a sparse ext4 of `gib` GiB, ready in milliseconds and occupying a few MB until written to. */\nexport async function createOverlay(file: string, gib: number): Promise<void> {\n await writeFile(file, \"\", { mode: 0o600 });\n await truncate(file, gib * 1024 ** 3);\n // -F: it is a file, not a block device. -m 0: no blocks held back for root, the whole disk is the box's.\n // Lazy init: the inode tables are zeroed by the guest kernel as it goes, so mkfs writes almost nothing.\n await run([\"mkfs.ext4\", \"-q\", \"-F\", \"-m\", \"0\", \"-L\", \"kvbox-box\", \"-E\", \"lazy_itable_init=1,lazy_journal_init=1\", file]);\n}\n\nasync function waitFor(pred: () => boolean, timeoutMs: number, stepMs = 50): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (pred()) return true;\n await sleep(stepMs);\n }\n return pred();\n}\n\nasync function tailOf(file: string, lines = 5): Promise<string> {\n try {\n return (await readFile(file, \"utf8\")).trim().split(\"\\n\").slice(-lines).join(\"\\n\");\n } catch {\n return \"\";\n }\n}\n\ninterface LaunchSpec {\n name: string;\n ip: string;\n mac: string;\n vcpu: number;\n memMib: number;\n base?: string | undefined;\n}\n\n/** Spawn Firecracker detached, configure it over its socket, InstanceStart. Returns the pid. Caller rolls back on throw. */\nasync function launch(spec: LaunchSpec, tap: string, cfg: Config, pubkeyB64: string, p: Paths): Promise<number> {\n const m = p.machine(spec.name);\n const baseFile = baseFileFor(spec, p);\n await rm(m.sock, { force: true });\n const logFd = openSync(m.log, \"a\");\n let child;\n try {\n child = spawn(cfg.firecracker, [\"--api-sock\", m.sock, \"--id\", spec.name, \"--level\", \"Error\"], {\n detached: true,\n stdio: [\"ignore\", logFd, logFd],\n });\n } finally {\n closeSync(logFd);\n }\n child.unref();\n const pid = child.pid;\n if (pid === undefined) throw new KvboxError(\"could not start firecracker\");\n try {\n if (!(await waitFor(() => existsSync(m.sock), 3000))) {\n throw new KvboxError(`firecracker did not open its API socket; last log lines:\\n${await tailOf(m.log)}`);\n }\n const fc = new FirecrackerClient(m.sock);\n try {\n await fc.bootSource(cfg.kernel, bootArgs(spec.name, spec.ip, cfg, pubkeyB64, { overlay: baseFile !== undefined }));\n if (baseFile !== undefined) {\n await fc.rootDrive(baseFile, true);\n await fc.overlayDrive(m.overlay);\n } else {\n await fc.rootDrive(m.disk, false);\n }\n await fc.machineConfig(spec.vcpu, spec.memMib);\n await fc.networkInterface(\"eth0\", tap, spec.mac);\n await fc.start();\n } finally {\n await fc.close();\n }\n return pid;\n } catch (err) {\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n /* already gone */\n }\n throw err;\n }\n}\n\nasync function readPubkeyB64(p: Paths): Promise<string> {\n try {\n return Buffer.from((await readFile(`${p.sshKey}.pub`, \"utf8\")).trim()).toString(\"base64\");\n } catch {\n throw new KvboxError(`SSH key missing at ${p.sshKey}.pub; run install-server.sh`);\n }\n}\n\nfunction assertAssets(cfg: Config): void {\n for (const [what, file] of [[\"kernel\", cfg.kernel], [\"base image\", cfg.rootfs], [\"firecracker\", cfg.firecracker]] as const) {\n if (!existsSync(file)) throw new KvboxError(`${what} missing at ${file}; run install-server.sh`);\n }\n}\n\nexport async function createMachine(req: CreateMachineRequest, p: Paths = paths()): Promise<Machine> {\n if (req.from !== undefined) throw new NotImplementedError(\"new --from <snapshot> (#13)\");\n const cfg = await loadConfig(p);\n const sizeName = req.size ?? cfg.defaultSize;\n if (!isSizeName(sizeName)) throw new KvboxError(`unknown size \"${sizeName}\"; use one of ${Object.keys(SIZES).join(\", \")}`);\n const size = SIZES[sizeName];\n assertAssets(cfg);\n const pubkeyB64 = await readPubkeyB64(p);\n\n const existing = await readAllMeta(p);\n const name = pickName(req.name, new Set(existing.map((m) => m.name)));\n const ip = nextFreeIp(cfg.subnet, [...existing.map((m) => m.ip), cfg.gateway]);\n const mac = macFor(ip);\n const m = p.machine(name);\n\n const base = await currentBase(cfg, p);\n await assertOverlayCapable(base.file, base.version);\n\n await ensureHostNetwork(cfg);\n await mkdir(m.dir, { recursive: true, mode: 0o700 });\n let tapCreated = false;\n try {\n // The base image is shared read-only by every box; only the box's own writes land in its overlay.\n await createOverlay(m.overlay, size.diskGib);\n const tap = await createTap(name, cfg);\n tapCreated = true;\n const spec: LaunchSpec = { name, ip, mac, vcpu: size.vcpu, memMib: size.memMib, base: base.version };\n const pid = await launch(spec, tap, cfg, pubkeyB64, p);\n const meta: MachineMeta = { ...spec, pid, createdAt: new Date().toISOString(), source: \"base\" };\n await writeMachineMeta(meta, p);\n return toMachine(meta, p);\n } catch (err) {\n // leave nothing half-made behind\n if (tapCreated) await deleteTap(name, cfg).catch(() => undefined);\n await rm(m.dir, { recursive: true, force: true });\n throw err;\n }\n}\n\n/** Clean guest shutdown (Ctrl+Alt+Del), SIGKILL after `graceMs`; tap removed; disk and meta kept. */\nasync function shutdown(meta: MachineMeta, cfg: Config, p: Paths, graceMs: number): Promise<void> {\n const pid = meta.pid;\n if (pidAlive(pid)) {\n const fc = new FirecrackerClient(p.machine(meta.name).sock);\n try {\n await fc.sendCtrlAltDel();\n } catch {\n process.kill(pid!, \"SIGTERM\");\n } finally {\n await fc.close();\n }\n if (!(await waitFor(() => !pidAlive(pid), graceMs, 100))) {\n try {\n process.kill(pid!, \"SIGKILL\");\n } catch {\n /* exited meanwhile */\n }\n await waitFor(() => !pidAlive(pid), 1000, 50);\n }\n }\n await deleteTap(meta.name, cfg);\n await rm(p.machine(meta.name).sock, { force: true });\n}\n\nexport async function stopMachine(name: string, p: Paths = paths()): Promise<Machine> {\n const meta = await readMeta(name, p);\n const cfg = await loadConfig(p);\n if (!pidAlive(meta.pid)) throw new KvboxError(`box ${name} is already stopped`);\n await shutdown(meta, cfg, p, 10_000);\n const stopped: MachineMeta = { ...meta, pid: null };\n await writeMachineMeta(stopped, p);\n return toMachine(stopped, p);\n}\n\nexport async function startMachine(name: string, p: Paths = paths()): Promise<Machine> {\n const meta = await readMeta(name, p);\n const cfg = await loadConfig(p);\n if (pidAlive(meta.pid)) throw new KvboxError(`box ${name} is already running`);\n assertAssets(cfg);\n const pubkeyB64 = await readPubkeyB64(p);\n await ensureHostNetwork(cfg);\n const tap = await createTap(name, cfg);\n try {\n const pid = await launch(meta, tap, cfg, pubkeyB64, p);\n const started: MachineMeta = { ...meta, pid };\n await writeMachineMeta(started, p);\n return toMachine(started, p);\n } catch (err) {\n await deleteTap(name, cfg).catch(() => undefined);\n throw err;\n }\n}\n\nasync function readMeta(name: string, p: Paths): Promise<MachineMeta> {\n try {\n return await readMachineMeta(name, p);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") throw new NotFoundError(`box ${name}`);\n throw err;\n }\n}\n\nexport async function destroyMachine(name: string, p: Paths = paths()): Promise<void> {\n const meta = await readMeta(name, p);\n const cfg = await loadConfig(p);\n await shutdown(meta, cfg, p, 2000);\n await rm(p.machine(name).dir, { recursive: true, force: true });\n}\n","/**\n * Client for one Firecracker process's API socket. Ten-odd endpoints, all\n * PUT/PATCH with JSON bodies, so a thin wrapper over undici beats an SDK.\n */\nimport { Agent, request } from \"undici\";\nimport { KvboxError } from \"@kvbox/shared/errors\";\n\nexport class FirecrackerClient {\n private readonly agent: Agent;\n\n constructor(readonly socketPath: string) {\n this.agent = new Agent({ connect: { socketPath } });\n }\n\n private async call(method: \"GET\" | \"PUT\" | \"PATCH\", path: string, body?: unknown): Promise<unknown> {\n const res = await request(`http://localhost${path}`, {\n dispatcher: this.agent,\n method,\n headers: body === undefined ? {} : { \"content-type\": \"application/json\" },\n body: body === undefined ? null : JSON.stringify(body),\n });\n const text = await res.body.text();\n if (res.statusCode >= 300) {\n throw new KvboxError(`firecracker ${method} ${path} -> ${res.statusCode}: ${text}`);\n }\n return text ? JSON.parse(text) : undefined;\n }\n\n bootSource(kernelImagePath: string, bootArgs: string) {\n return this.call(\"PUT\", \"/boot-source\", { kernel_image_path: kernelImagePath, boot_args: bootArgs });\n }\n\n /**\n * /dev/vda. Firecracker adds `root=/dev/vda` plus `ro` or `rw` to the boot args to match.\n * Read-only for the shared base image; read-write only for pre-overlay boxes with their own copy.\n */\n rootDrive(pathOnHost: string, readOnly: boolean) {\n return this.drive(\"rootfs\", pathOnHost, { root: true, readOnly });\n }\n\n /** The box's own writable disk, /dev/vdb: the guest stacks it on the base as an overlay. */\n overlayDrive(pathOnHost: string) {\n return this.drive(\"overlay\", pathOnHost, { root: false, readOnly: false });\n }\n\n private drive(id: string, pathOnHost: string, opts: { root: boolean; readOnly: boolean }) {\n return this.call(\"PUT\", `/drives/${id}`, {\n drive_id: id,\n path_on_host: pathOnHost,\n is_root_device: opts.root,\n is_read_only: opts.readOnly,\n // \"Unsafe\" (the default) acks guest flushes without doing anything, so the last journal\n // commit of a clean shutdown can be lost when the guest resets and Firecracker exits.\n // Writeback makes a guest flush a real fsync; a box's writes must survive `kvbox stop`.\n cache_type: \"Writeback\",\n });\n }\n\n machineConfig(vcpuCount: number, memSizeMib: number) {\n return this.call(\"PUT\", \"/machine-config\", { vcpu_count: vcpuCount, mem_size_mib: memSizeMib });\n }\n\n networkInterface(ifaceId: string, hostDevName: string, guestMac: string) {\n return this.call(\"PUT\", `/network-interfaces/${ifaceId}`, {\n iface_id: ifaceId,\n host_dev_name: hostDevName,\n guest_mac: guestMac,\n });\n }\n\n start() {\n return this.call(\"PUT\", \"/actions\", { action_type: \"InstanceStart\" });\n }\n\n /** Ask the guest to shut down cleanly (systemd handles Ctrl+Alt+Del as poweroff on Firecracker). */\n sendCtrlAltDel() {\n return this.call(\"PUT\", \"/actions\", { action_type: \"SendCtrlAltDel\" });\n }\n\n pause() {\n return this.call(\"PATCH\", \"/vm\", { state: \"Paused\" });\n }\n\n resume() {\n return this.call(\"PATCH\", \"/vm\", { state: \"Resumed\" });\n }\n\n snapshotCreate(snapshotPath: string, memFilePath: string) {\n return this.call(\"PUT\", \"/snapshot/create\", {\n snapshot_type: \"Full\",\n snapshot_path: snapshotPath,\n mem_file_path: memFilePath,\n });\n }\n\n snapshotLoad(snapshotPath: string, memFilePath: string, resume = true) {\n return this.call(\"PUT\", \"/snapshot/load\", {\n snapshot_path: snapshotPath,\n mem_backend: { backend_type: \"File\", backend_path: memFilePath },\n resume_vm: resume,\n });\n }\n\n info() {\n return this.call(\"GET\", \"/\");\n }\n\n async close() {\n await this.agent.close();\n }\n}\n","import { Hono } from \"hono\";\nimport { CreateSnapshotRequest } from \"@kvbox/shared/api\";\nimport { deleteSnapshot, listSnapshots, saveSnapshot } from \"../core/snapshots.js\";\n\nexport function snapshotRoutes() {\n const r = new Hono();\n r.get(\"/\", async (c) => c.json(await listSnapshots()));\n r.post(\"/\", async (c) => {\n const body = CreateSnapshotRequest.parse(await c.req.json());\n return c.json(await saveSnapshot(body), 201);\n });\n r.delete(\"/:name\", async (c) => {\n await deleteSnapshot(c.req.param(\"name\"));\n return c.body(null, 204);\n });\n return r;\n}\n","/** Full snapshots (vmstate + mem + disk copy) and their listing. */\nimport { NotImplementedError } from \"@kvbox/shared/errors\";\nimport type { CreateSnapshotRequest, Snapshot } from \"@kvbox/shared/api\";\n\nexport async function listSnapshots(): Promise<Snapshot[]> {\n throw new NotImplementedError(\"snapshots.listSnapshots\");\n}\n\nexport async function saveSnapshot(_req: CreateSnapshotRequest): Promise<Snapshot> {\n throw new NotImplementedError(\"snapshots.saveSnapshot\");\n}\n\nexport async function deleteSnapshot(_name: string): Promise<void> {\n throw new NotImplementedError(\"snapshots.deleteSnapshot\");\n}\n"],"mappings":"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","names":["rm","writeFile","readFile","writeFile","z","z","readFile","writeFile","Hono","require","Hono","z","z","existsSync","mkdir","readFile","writeFile","bootArgs","existsSync","writeFile","readFile","m","mkdir","Hono","Hono","Hono","Hono","rm","writeFile","shutdown"]}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghalex/kvbox-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "kvbox host daemon: owns Firecracker microVMs on a Linux host and serves the kvbox API. Managed with the @ghalex/kvbox CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
8
|
-
"kvbox-server": "
|
|
8
|
+
"kvbox-server": "dist/kvbox-server.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "https://github.com/ghalex/kvbox",
|
|
19
|
+
"url": "git+https://github.com/ghalex/kvbox.git",
|
|
20
20
|
"directory": "packages/server"
|
|
21
21
|
},
|
|
22
22
|
"homepage": "https://kvbox.sh",
|