@forgezero/agent 0.1.65 → 0.1.67

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.
@@ -1,21 +1,480 @@
1
- // src/platform-bootstrap-runtime.ts
1
+ // src/software.ts
2
+ import { createHash } from "node:crypto";
2
3
  import {
4
+ accessSync,
3
5
  chmodSync,
4
- chownSync,
5
6
  copyFileSync,
6
7
  existsSync,
7
- lstatSync,
8
+ mkdtempSync,
8
9
  mkdirSync,
9
10
  readFileSync,
10
- readdirSync,
11
- realpathSync,
12
11
  renameSync,
13
12
  rmSync,
14
- statSync,
15
13
  symlinkSync,
14
+ unlinkSync,
16
15
  writeFileSync
17
16
  } from "node:fs";
17
+ import { tmpdir } from "node:os";
18
18
  import { join } from "node:path";
19
+ var PINNED_BUN_VERSION = "1.3.14";
20
+ var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f";
21
+ var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
22
+ var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
23
+ var OS_CATALOG = [
24
+ {
25
+ id: "ubuntu",
26
+ version: "26.04",
27
+ architecture: "x64",
28
+ status: "active",
29
+ imageKey: "ubuntu-resolute-20260731",
30
+ imageLabel: "Ubuntu 26.04 LTS Resolute",
31
+ imageVersion: "2026-07-31",
32
+ imageUrl: "https://cloud-images.ubuntu.com/releases/resolute/release-20260731/ubuntu-26.04-server-cloudimg-amd64.img",
33
+ imageSha256: "9dc7c5363c0146a08ba0c9aa834d82c2c6dfbb1c471ad9a2f0aba1189e21be05",
34
+ confidentialModes: ["sev-snp"]
35
+ },
36
+ {
37
+ id: "ubuntu",
38
+ version: "24.04",
39
+ architecture: "x64",
40
+ status: "active",
41
+ imageKey: "ubuntu-noble-20260705",
42
+ imageLabel: "Ubuntu 24.04 LTS Noble",
43
+ imageVersion: "2026-07-05",
44
+ imageUrl: "https://cloud-images.ubuntu.com/releases/noble/release-20260705/ubuntu-24.04-server-cloudimg-amd64.img",
45
+ imageSha256: "ffe6203da54deeb6db5d2a98a83f9ec8e55f149d3f7ba622e1abe5fa966ee3d6",
46
+ confidentialModes: []
47
+ }
48
+ ];
49
+ var SOFTWARE_CATALOG = [
50
+ { id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
51
+ { id: "docker", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
52
+ { id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
53
+ { id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
54
+ { id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
55
+ { id: "cloudflare-warp", version: "2026.6.822.0-min", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
56
+ { id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
57
+ { id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
58
+ { id: "git", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
59
+ { id: "docker", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
60
+ { id: "nginx", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
61
+ { id: "ufw", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
62
+ { id: "openssh-client", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
63
+ { id: "git", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
64
+ ];
65
+ var UBUNTU_2604_X64 = [
66
+ { requirement: { id: "bun", version: "1.3.14" } },
67
+ { requirement: { id: "docker", version: "ubuntu-26.04" } },
68
+ { requirement: { id: "nginx", version: "ubuntu-26.04" } },
69
+ { requirement: { id: "arangodb", version: "3.11.14" } },
70
+ { requirement: { id: "cloudflared", version: "2026.7.3" } },
71
+ { requirement: { id: "cloudflare-warp", version: "2026.6.822.0-min" } },
72
+ { requirement: { id: "ufw", version: "ubuntu-26.04" } },
73
+ { requirement: { id: "openssh-client", version: "ubuntu-26.04" } },
74
+ { requirement: { id: "git", version: "ubuntu-26.04" } }
75
+ ];
76
+ var UBUNTU_2404_X64 = [
77
+ { requirement: { id: "bun", version: "1.3.14" } },
78
+ { requirement: { id: "docker", version: "ubuntu-24.04" } },
79
+ { requirement: { id: "nginx", version: "ubuntu-24.04" } },
80
+ { requirement: { id: "ufw", version: "ubuntu-24.04" } },
81
+ { requirement: { id: "openssh-client", version: "ubuntu-24.04" } },
82
+ { requirement: { id: "git", version: "ubuntu-24.04" } }
83
+ ];
84
+ var strategiesFor = (observation) => observation.os.id === "ubuntu" && observation.architecture === "x64" ? observation.os.versionId === "26.04" ? UBUNTU_2604_X64 : observation.os.versionId === "24.04" ? UBUNTU_2404_X64 : undefined : undefined;
85
+ var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
86
+ var DOCKER_DAEMON_PATH = "/etc/docker/daemon.json";
87
+ var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
88
+ "data-root": "/var/lib/docker",
89
+ "storage-driver": "overlay2",
90
+ "live-restore": true,
91
+ "log-driver": "local",
92
+ "log-opts": { "max-size": "10m", "max-file": "3" }
93
+ }, null, 2)}
94
+ `;
95
+ var softwareSpawnFailure = (cause, argv) => cause.code === "ENOENT" ? { exitCode: 127, output: `executable is absent: ${argv[0]}` } : undefined;
96
+ var runSoftwareCommand = async (argv, env = {}) => {
97
+ let child;
98
+ try {
99
+ child = Bun.spawn([...argv], { stdout: "pipe", stderr: "pipe", env: { PATH: path, LANG: "C", LC_ALL: "C", ...env } });
100
+ } catch (cause) {
101
+ const failure = softwareSpawnFailure(cause, argv);
102
+ if (failure)
103
+ return failure;
104
+ throw cause;
105
+ }
106
+ const [stdout, stderr, exitCode] = await Promise.all([
107
+ new Response(child.stdout).text(),
108
+ new Response(child.stderr).text(),
109
+ child.exited
110
+ ]);
111
+ return { exitCode, output: `${stdout}${stderr}` };
112
+ };
113
+ var run = runSoftwareCommand;
114
+ var download = async (url, destination, sha256) => {
115
+ const response = await fetch(url, { redirect: "follow", signal: AbortSignal.timeout(120000) });
116
+ if (!response.ok)
117
+ throw new Error(`download failed with HTTP ${response.status}`);
118
+ const bytes = new Uint8Array(await response.arrayBuffer());
119
+ if (createHash("sha256").update(bytes).digest("hex") !== sha256)
120
+ throw new Error("download checksum mismatch");
121
+ writeFileSync(destination, bytes, { mode: 384, flag: "wx" });
122
+ };
123
+ var successful = (result, pattern) => result.exitCode === 0 && (!pattern || pattern.test(result.output));
124
+ var aptInstall = async (name) => {
125
+ const environment = { DEBIAN_FRONTEND: "noninteractive" };
126
+ const update = await run(["/usr/bin/apt-get", "update", "-qq"], environment);
127
+ return update.exitCode === 0 ? run(["/usr/bin/apt-get", "install", "-y", name], environment) : update;
128
+ };
129
+ async function executeSoftwareOperation(operation) {
130
+ const { software, version } = operation;
131
+ if (![...UBUNTU_2604_X64, ...UBUNTU_2404_X64].some(({ requirement }) => requirement.id === software && requirement.version === version)) {
132
+ return { exitCode: 2, output: "unsupported software operation" };
133
+ }
134
+ if (operation.kind === "check") {
135
+ if (software === "bun")
136
+ return run(["/usr/local/bin/bun", "--version"]).then((r) => ({ ...r, exitCode: successful(r, /^1\.3\.14\s*$/m) ? 0 : 1 }));
137
+ if (software === "docker") {
138
+ const binary = await run(["/usr/bin/docker", "--version"]);
139
+ if (!successful(binary, /Docker version/))
140
+ return { ...binary, exitCode: 1 };
141
+ if (!existsSync(DOCKER_DAEMON_PATH) || readFileSync(DOCKER_DAEMON_PATH, "utf8") !== DOCKER_DAEMON_CONFIG)
142
+ return { exitCode: 1, output: "Docker daemon policy is absent or differs from the reviewed ForgeZero policy" };
143
+ const active = await run(["/usr/bin/systemctl", "is-active", "--quiet", "docker.service"]);
144
+ return active.exitCode === 0 ? { exitCode: 0, output: binary.output } : active;
145
+ }
146
+ if (software === "nginx") {
147
+ const binary = await run(["/usr/sbin/nginx", "-v"]);
148
+ return binary.exitCode === 0 ? run(["/usr/bin/systemctl", "is-active", "--quiet", "nginx.service"]) : binary;
149
+ }
150
+ if (software === "arangodb") {
151
+ const binary = await run(["/usr/sbin/arangod", "--version"]);
152
+ if (!successful(binary, /3\.11\.14/))
153
+ return { ...binary, exitCode: 1 };
154
+ const [active, enabled] = await Promise.all([
155
+ run(["/usr/bin/systemctl", "is-active", "--quiet", "arangodb3.service"]),
156
+ run(["/usr/bin/systemctl", "is-enabled", "--quiet", "arangodb3.service"])
157
+ ]);
158
+ return active.exitCode !== 0 && enabled.exitCode !== 0 ? { exitCode: 0, output: binary.output } : { exitCode: 1, output: "vendor standalone unit remains active or enabled" };
159
+ }
160
+ if (software === "cloudflared")
161
+ return run(["/usr/local/bin/cloudflared", "--version"]).then((r) => ({ ...r, exitCode: successful(r, /2026\.7\.3/) ? 0 : 1 }));
162
+ if (software === "cloudflare-warp")
163
+ return run(["/usr/bin/warp-cli", "--version"]).then((result) => {
164
+ const match = result.output.match(/(\d{4})\.(\d+)\.(\d+)\.(\d+)/);
165
+ const observed = match?.slice(1).map(Number);
166
+ const minimum = [2026, 6, 822, 0];
167
+ const supported = observed && observed.some((part, index) => part > minimum[index] && observed.slice(0, index).every((prior, priorIndex) => prior === minimum[priorIndex])) || observed?.every((part, index) => part === minimum[index]);
168
+ return { ...result, exitCode: result.exitCode === 0 && supported ? 0 : 1 };
169
+ });
170
+ const binaries = software === "ufw" ? ["/usr/sbin/ufw"] : software === "git" ? ["/usr/bin/git"] : ["/usr/bin/ssh", "/usr/bin/scp", "/usr/bin/ssh-keyscan", "/usr/bin/ssh-keygen"];
171
+ try {
172
+ binaries.forEach((binary) => accessSync(binary));
173
+ return { exitCode: 0, output: "" };
174
+ } catch (cause) {
175
+ return { exitCode: 1, output: cause instanceof Error ? cause.message : String(cause) };
176
+ }
177
+ }
178
+ if (software === "docker" || software === "nginx" || software === "ufw" || software === "openssh-client" || software === "git") {
179
+ const packageName = software === "openssh-client" ? "openssh-client" : software === "docker" ? "docker.io" : software;
180
+ const installed = await aptInstall(packageName);
181
+ if (installed.exitCode !== 0 || software !== "nginx" && software !== "docker")
182
+ return installed;
183
+ if (software === "docker") {
184
+ mkdirSync("/etc/docker", { recursive: true, mode: 493 });
185
+ if (existsSync(DOCKER_DAEMON_PATH) && readFileSync(DOCKER_DAEMON_PATH, "utf8") !== DOCKER_DAEMON_CONFIG) {
186
+ return { exitCode: 2, output: "refusing to overwrite an existing Docker daemon configuration that differs from the reviewed ForgeZero policy" };
187
+ }
188
+ if (!existsSync(DOCKER_DAEMON_PATH))
189
+ writeFileSync(DOCKER_DAEMON_PATH, DOCKER_DAEMON_CONFIG, { mode: 420, flag: "wx" });
190
+ const enabled = await run(["/usr/bin/systemctl", "enable", "docker.service"]);
191
+ return enabled.exitCode === 0 ? run(["/usr/bin/systemctl", "restart", "docker.service"]) : enabled;
192
+ }
193
+ return run(["/usr/bin/systemctl", "enable", "--now", `${software}.service`]);
194
+ }
195
+ const directory = mkdtempSync(join(tmpdir(), "forgezero-software-"));
196
+ try {
197
+ if (software === "cloudflare-warp") {
198
+ const key = join(directory, "cloudflare-warp-key.gpg");
199
+ await download("https://pkg.cloudflareclient.com/pubkey.gpg", key, "0f37fc298c98e88ee3c0ee68c95b69f1dba9eb477abe3167e13982105911264d");
200
+ mkdirSync("/usr/share/keyrings", { recursive: true, mode: 493 });
201
+ const dearmored = await run([
202
+ "/usr/bin/gpg",
203
+ "--batch",
204
+ "--yes",
205
+ "--dearmor",
206
+ "-o",
207
+ "/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg",
208
+ key
209
+ ]);
210
+ if (dearmored.exitCode !== 0)
211
+ return dearmored;
212
+ mkdirSync("/etc/apt/sources.list.d", { recursive: true, mode: 493 });
213
+ writeFileSync("/etc/apt/sources.list.d/cloudflare-client.list", `deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ resolute main
214
+ `, { mode: 420 });
215
+ return aptInstall("cloudflare-warp");
216
+ }
217
+ if (software === "bun") {
218
+ const archive = join(directory, "bun.zip");
219
+ await download("https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-x64.zip", archive, BUN_RELEASE_SHA256);
220
+ const unpacked = join(directory, "unpacked");
221
+ mkdirSync(unpacked, { mode: 448 });
222
+ const unzipped = await run(["/usr/bin/unzip", "-q", archive, "-d", unpacked]);
223
+ if (unzipped.exitCode !== 0)
224
+ return unzipped;
225
+ mkdirSync("/usr/local/lib/forgezero/runtime", { recursive: true, mode: 493 });
226
+ copyFileSync(join(unpacked, "bun-linux-x64", "bun"), "/usr/local/lib/forgezero/runtime/bun.next");
227
+ chmodSync("/usr/local/lib/forgezero/runtime/bun.next", 493);
228
+ renameSync("/usr/local/lib/forgezero/runtime/bun.next", "/usr/local/lib/forgezero/runtime/bun");
229
+ try {
230
+ unlinkSync("/usr/local/bin/bun");
231
+ } catch {}
232
+ symlinkSync("/usr/local/lib/forgezero/runtime/bun", "/usr/local/bin/bun");
233
+ return { exitCode: 0, output: "" };
234
+ }
235
+ if (software === "cloudflared") {
236
+ const binary = join(directory, "cloudflared");
237
+ await download("https://github.com/cloudflare/cloudflared/releases/download/2026.7.3/cloudflared-linux-amd64", binary, CLOUDFLARED_SHA256);
238
+ chmodSync(binary, 493);
239
+ copyFileSync(binary, "/usr/local/bin/cloudflared");
240
+ chmodSync("/usr/local/bin/cloudflared", 493);
241
+ return { exitCode: 0, output: "" };
242
+ }
243
+ const deb = join(directory, "arangodb.deb");
244
+ await download("https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb", deb, ARANGO_SHA256);
245
+ let installed = await run(["/usr/bin/dpkg", "-i", deb], { DEBIAN_FRONTEND: "noninteractive" });
246
+ if (installed.exitCode !== 0)
247
+ installed = await run(["/usr/bin/apt-get", "-y", "-f", "install"], { DEBIAN_FRONTEND: "noninteractive" });
248
+ if (installed.exitCode !== 0)
249
+ return installed;
250
+ await run(["/usr/bin/systemctl", "disable", "--now", "arangodb3.service"]);
251
+ return { exitCode: 0, output: "" };
252
+ } finally {
253
+ rmSync(directory, { recursive: true, force: true });
254
+ }
255
+ }
256
+ function observeSoftwareHost(osRelease = readFileSync("/etc/os-release", "utf8"), architecture = process.arch) {
257
+ const values = Object.fromEntries(osRelease.split(`
258
+ `).flatMap((line) => {
259
+ const separator = line.indexOf("=");
260
+ return separator > 0 ? [[line.slice(0, separator), line.slice(separator + 1).replace(/^['"]|['"]$/g, "")]] : [];
261
+ }));
262
+ return {
263
+ os: { id: (values.ID ?? "unknown").toLowerCase(), versionId: values.VERSION_ID ?? "unknown" },
264
+ architecture
265
+ };
266
+ }
267
+ function validateSoftwareRequirements(value, _options = {}) {
268
+ if (!Array.isArray(value) || value.length > 32)
269
+ throw new Error("software requirements must be an array of at most 32 entries");
270
+ const seen = new Set;
271
+ return value.map((item) => {
272
+ if (!item || typeof item !== "object" || Array.isArray(item))
273
+ throw new Error("software requirement must be an object");
274
+ const row = item;
275
+ if (Object.keys(row).some((key) => key !== "id" && key !== "version")) {
276
+ throw new Error("software requirement contains an unknown field");
277
+ }
278
+ if (!["bun", "docker", "nginx", "arangodb", "cloudflared", "cloudflare-warp", "ufw", "openssh-client", "git"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
279
+ throw new Error("software requirement coordinate is invalid");
280
+ }
281
+ const requirement = { id: row.id, version: row.version };
282
+ if (seen.has(requirement.id))
283
+ throw new Error(`duplicate software requirement: ${requirement.id}`);
284
+ seen.add(requirement.id);
285
+ const catalog = SOFTWARE_CATALOG.find((candidate) => candidate.id === requirement.id && candidate.version === requirement.version);
286
+ if (!catalog || catalog.status !== "active") {
287
+ throw new Error(`software requirement is not active: ${requirement.id}@${requirement.version}`);
288
+ }
289
+ return requirement;
290
+ });
291
+ }
292
+ async function ensureSoftwareRequirements(requirementsInput, options) {
293
+ const requirements = validateSoftwareRequirements(requirementsInput);
294
+ const observation = options.observation ?? observeSoftwareHost();
295
+ const os = OS_CATALOG.find((candidate) => candidate.id === observation.os.id && candidate.version === observation.os.versionId && candidate.architecture === observation.architecture);
296
+ if (!os || os.status !== "active") {
297
+ throw new Error(`unsupported software strategy: ${observation.os.id} ${observation.os.versionId} ${observation.architecture}`);
298
+ }
299
+ const results = [];
300
+ for (const requirement of requirements) {
301
+ const strategy = strategiesFor(observation)?.find(({ requirement: candidate }) => candidate.id === requirement.id && candidate.version === requirement.version);
302
+ if (!strategy)
303
+ throw new Error(`unsupported software requirement: ${requirement.id}@${requirement.version}`);
304
+ const before = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
305
+ if (before.exitCode === 0) {
306
+ results.push({ ...requirement, changed: false });
307
+ continue;
308
+ }
309
+ const installed = await options.exec({ kind: "install", software: requirement.id, version: requirement.version });
310
+ if (installed.exitCode !== 0)
311
+ throw new Error(`could not install ${requirement.id}@${requirement.version}: ${installed.output.trim()}`);
312
+ const after = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
313
+ if (after.exitCode !== 0)
314
+ throw new Error(`${requirement.id}@${requirement.version} did not pass its post-install check`);
315
+ results.push({ ...requirement, changed: true });
316
+ }
317
+ return results;
318
+ }
319
+
320
+ // src/ubuntu.ts
321
+ var current = OS_CATALOG[0];
322
+ var SUPPORTED_GUEST_IMAGE = Object.freeze({
323
+ key: current.imageKey,
324
+ family: `${current.id}-${current.version}`,
325
+ version: current.imageVersion,
326
+ label: current.imageLabel,
327
+ url: current.imageUrl,
328
+ sha256: current.imageSha256
329
+ });
330
+ var SUPPORTED_GUEST_IMAGES = Object.freeze(OS_CATALOG.filter(({ status }) => status === "active").map(({ id, imageKey, imageLabel, imageVersion, version, confidentialModes }) => ({
331
+ key: imageKey,
332
+ label: imageLabel,
333
+ family: `${id}-${version}`,
334
+ version: imageVersion,
335
+ confidential: confidentialModes.some((mode) => mode === "sev-snp")
336
+ })));
337
+ function supportedGuestImage(imageKey) {
338
+ return SUPPORTED_GUEST_IMAGES.find(({ key }) => key === imageKey);
339
+ }
340
+ function assertSupportedGuestImage(imageKey, confidential = false) {
341
+ const selected = OS_CATALOG.find((entry) => entry.status === "active" && entry.imageKey === imageKey);
342
+ if (!selected) {
343
+ throw new Error(`unsupported guest image ${imageKey}; supported images: ${SUPPORTED_GUEST_IMAGES.map(({ key }) => key).join(", ")}`);
344
+ }
345
+ if (confidential && !selected.confidentialModes.some((mode) => mode === "sev-snp")) {
346
+ throw new Error(`guest image ${imageKey} is not approved for SEV-SNP`);
347
+ }
348
+ }
349
+
350
+ // src/platform-genesis.ts
351
+ var SAFE_NAME = /^[a-z][a-z0-9-]{0,62}$/;
352
+ var SAFE_POOL = /^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/;
353
+ var PRIVATE_V4 = /^(?:10\.(?:\d{1,3}\.){2}\d{1,3}|192\.168\.(?:\d{1,3})\.(?:\d{1,3})|172\.(?:1[6-9]|2\d|3[01])\.(?:\d{1,3})\.(?:\d{1,3}))$/;
354
+ var integer = (value, minimum, maximum, label) => {
355
+ if (!Number.isInteger(value) || value < minimum || value > maximum) {
356
+ throw new Error(`platform genesis ${label} is outside the supported range`);
357
+ }
358
+ return value;
359
+ };
360
+ function validatePlatformGenesisGuests(guests, expectedCount = 3) {
361
+ if (!Array.isArray(guests) || guests.length !== expectedCount) {
362
+ throw new Error(`platform genesis requires exactly ${expectedCount} reviewed guest requests`);
363
+ }
364
+ const names = new Set;
365
+ const addresses = new Set;
366
+ const pools = new Set;
367
+ return guests.map((input) => {
368
+ if (!input || typeof input !== "object" || !SAFE_NAME.test(input.name)) {
369
+ throw new Error("platform genesis guest name is malformed");
370
+ }
371
+ if (!PRIVATE_V4.test(input.address))
372
+ throw new Error("platform genesis guest address must be private IPv4");
373
+ if (typeof input.confidential !== "boolean")
374
+ throw new Error("platform genesis confidential requirement is missing");
375
+ assertSupportedGuestImage(input.imageKey, input.confidential);
376
+ if (input.cpuPoolKey !== undefined && !SAFE_POOL.test(input.cpuPoolKey)) {
377
+ throw new Error("platform genesis CPU pool key is malformed");
378
+ }
379
+ if (names.has(input.name) || addresses.has(input.address) || input.cpuPoolKey !== undefined && pools.has(input.cpuPoolKey)) {
380
+ throw new Error("platform genesis guest names, addresses and explicit CPU pools must be unique");
381
+ }
382
+ names.add(input.name);
383
+ addresses.add(input.address);
384
+ if (input.cpuPoolKey !== undefined)
385
+ pools.add(input.cpuPoolKey);
386
+ const physicalCores = integer(input.physicalCores, 1, 256, "physical core count");
387
+ const vcpu = integer(input.vcpu, 1, 512, "vCPU count");
388
+ if (vcpu < physicalCores)
389
+ throw new Error("platform genesis vCPU count cannot be smaller than physical cores");
390
+ const memoryGib = integer(input.memoryGib, 1, 8192, "memory");
391
+ const diskGib = integer(input.diskGib, 8, 65536, "disk");
392
+ const egressGuaranteedMbps = integer(input.egressGuaranteedMbps, 0, 1e6, "guaranteed egress");
393
+ const egressBurstMbps = integer(input.egressBurstMbps, egressGuaranteedMbps, 1e6, "burst egress");
394
+ return {
395
+ name: input.name,
396
+ address: input.address,
397
+ imageKey: input.imageKey,
398
+ ...input.cpuPoolKey ? { cpuPoolKey: input.cpuPoolKey } : {},
399
+ physicalCores,
400
+ vcpu,
401
+ memoryGib,
402
+ diskGib,
403
+ egressGuaranteedMbps,
404
+ egressBurstMbps,
405
+ confidential: input.confidential
406
+ };
407
+ });
408
+ }
409
+ var publicKey = (value) => {
410
+ const normalized = value.trim();
411
+ if (!/^ssh-(?:ed25519|rsa) [A-Za-z0-9+/]+={0,3}(?: [^\r\n]+)?$/.test(normalized)) {
412
+ throw new Error("platform genesis SSH public key is malformed");
413
+ }
414
+ return normalized;
415
+ };
416
+ var claimFor = (guest, keys, action) => {
417
+ const common = {
418
+ computeKey: `platform:${guest.name}`,
419
+ claimToken: "offline-platform-genesis",
420
+ claimExpiresAtTs: Number.MAX_SAFE_INTEGER,
421
+ attempt: 1,
422
+ bootstrap: { kind: "platform-genesis" },
423
+ spec: {
424
+ reference: `platform:${guest.name}`,
425
+ imageKey: guest.imageKey,
426
+ guestName: guest.name,
427
+ guestAddress: guest.address,
428
+ ...guest.cpuPoolKey ? { cpuPoolKey: guest.cpuPoolKey } : {},
429
+ physicalCores: guest.physicalCores,
430
+ vcpu: guest.vcpu,
431
+ memoryGib: guest.memoryGib,
432
+ diskGib: guest.diskGib,
433
+ egressGuaranteedMbps: guest.egressGuaranteedMbps,
434
+ egressBurstMbps: guest.egressBurstMbps,
435
+ confidential: guest.confidential
436
+ }
437
+ };
438
+ if (action === "delete")
439
+ return { ...common, action: "delete" };
440
+ return { ...common, action: "create", access: { sshUser: "forgezero", sshPublicKeys: [...keys] } };
441
+ };
442
+ function platformGenesisClaims(guests, sshPublicKeys) {
443
+ const reviewed = validatePlatformGenesisGuests(guests);
444
+ const keys = [...new Set(sshPublicKeys.map(publicKey))];
445
+ if (keys.length === 0)
446
+ throw new Error("platform genesis requires at least one operator SSH public key");
447
+ return reviewed.map((guest) => claimFor(guest, keys, "create"));
448
+ }
449
+ function platformCommunityRehearsalClaims(seedGuests, rehearsalGuest, sshPublicKeys) {
450
+ const reviewed = validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4);
451
+ const keys = [...new Set(sshPublicKeys.map(publicKey))];
452
+ if (keys.length === 0)
453
+ throw new Error("platform genesis requires at least one operator SSH public key");
454
+ return reviewed.map((guest) => claimFor(guest, keys, "create"));
455
+ }
456
+ function platformCommunityRehearsalDeletionClaims(seedGuests, rehearsalGuest) {
457
+ return validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4).map((guest) => claimFor(guest, [], "delete"));
458
+ }
459
+
460
+ // src/platform-bootstrap-runtime.ts
461
+ import {
462
+ chmodSync as chmodSync2,
463
+ chownSync,
464
+ copyFileSync as copyFileSync2,
465
+ existsSync as existsSync2,
466
+ lstatSync,
467
+ mkdirSync as mkdirSync2,
468
+ readFileSync as readFileSync2,
469
+ readdirSync,
470
+ realpathSync,
471
+ renameSync as renameSync2,
472
+ rmSync as rmSync2,
473
+ statSync,
474
+ symlinkSync as symlinkSync2,
475
+ writeFileSync as writeFileSync2
476
+ } from "node:fs";
477
+ import { join as join2 } from "node:path";
19
478
  var safeAtom = (name, value) => {
20
479
  if (!value || /[\0\r\n]/.test(value))
21
480
  throw new Error(`${name} must be non-empty and single-line.`);
@@ -46,6 +505,50 @@ var httpsOrigin = (name, raw) => {
46
505
  }
47
506
  return value.origin;
48
507
  };
508
+ function validatePlatformInitialInventory(value) {
509
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["metalHostname", "region", "computes"].includes(key)) || !/^[A-Za-z0-9][A-Za-z0-9.-]{1,252}$/.test(value.metalHostname) || !value.region || typeof value.region !== "object" || Array.isArray(value.region) || Object.keys(value.region).some((key) => !["key", "label", "country", "city", "confidentialCapable"].includes(key)) || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(value.region.key) || !/^[A-Z]{2}$/.test(value.region.country) || value.region.confidentialCapable !== true || !Array.isArray(value.computes)) {
510
+ throw new Error("initial platform inventory coordinates are invalid");
511
+ }
512
+ safeAtom("initialInventory.region.label", value.region.label);
513
+ if (value.region.city !== undefined)
514
+ safeAtom("initialInventory.region.city", value.region.city);
515
+ const computes = validatePlatformGenesisGuests(value.computes.map((compute) => {
516
+ if (!compute || typeof compute !== "object" || Array.isArray(compute) || Object.keys(compute).some((key) => ![
517
+ "name",
518
+ "address",
519
+ "imageKey",
520
+ "cpuPoolKey",
521
+ "physicalCores",
522
+ "vcpu",
523
+ "memoryGib",
524
+ "diskGib",
525
+ "egressGuaranteedMbps",
526
+ "egressBurstMbps",
527
+ "confidential",
528
+ "databaseRole",
529
+ "databaseAgency"
530
+ ].includes(key)))
531
+ throw new Error("initial platform compute contains unknown fields");
532
+ return compute;
533
+ }));
534
+ for (let index = 0;index < computes.length; index += 1) {
535
+ const declared = value.computes[index];
536
+ const expectedRole = index === 0 ? "master" : "joiner";
537
+ if (declared.databaseRole !== expectedRole || declared.databaseAgency !== "member" || declared.confidential !== true) {
538
+ throw new Error("initial platform inventory requires one master, two joiners, three Agency members and confidential compute");
539
+ }
540
+ }
541
+ return {
542
+ metalHostname: value.metalHostname,
543
+ region: { ...value.region },
544
+ computes: value.computes.map((compute, index) => ({
545
+ ...computes[index],
546
+ databaseRole: compute.databaseRole,
547
+ databaseAgency: compute.databaseAgency,
548
+ confidential: true
549
+ }))
550
+ };
551
+ }
49
552
  var systemdValue = (name, raw) => {
50
553
  if (/[\0\r\n]/.test(raw))
51
554
  throw new Error(`${name} must be single-line.`);
@@ -131,12 +634,14 @@ function validatePlatformSharedEnvironment(input) {
131
634
  throw new Error("realtime.producer is invalid.");
132
635
  }
133
636
  }
637
+ const initialInventory = input.initialInventory ? validatePlatformInitialInventory(input.initialInventory) : undefined;
134
638
  return {
135
639
  ...input,
136
640
  databaseCoordinators: coordinators,
137
641
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
138
642
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
139
- agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint)
643
+ agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
644
+ ...initialInventory ? { initialInventory } : {}
140
645
  };
141
646
  }
142
647
  function renderPlatformSharedEnvironment(input) {
@@ -199,7 +704,8 @@ function renderPlatformSharedEnvironment(input) {
199
704
  FZ_CF_TUNNEL_SERVICE: value.cloudflare?.tunnelService ?? "",
200
705
  FZ_REALTIME_WORKER_SCRIPT: value.realtime?.workerScriptName ?? "",
201
706
  FZ_REALTIME_ENDPOINT: value.realtime?.endpoint ?? "",
202
- FZ_REALTIME_PRODUCER: value.realtime?.producer ?? ""
707
+ FZ_REALTIME_PRODUCER: value.realtime?.producer ?? "",
708
+ FZ_PLATFORM_INITIAL_INVENTORY: value.initialInventory ? JSON.stringify(value.initialInventory) : ""
203
709
  };
204
710
  return `# Generated by fz bootstrap platform. Non-secret coordinates only.
205
711
  ` + Object.entries(entries).map(([key, entry]) => `${key}=${systemdValue(key, entry)}`).join(`
@@ -226,8 +732,8 @@ function platformApiCredentialSpecs(options) {
226
732
  ];
227
733
  }
228
734
  function renderPlatformApiUnits(input) {
229
- for (const path of [input.sharedDirectory, input.sharedEnvironmentFile, input.slotsDirectory]) {
230
- if (!path.startsWith("/") || /[\r\n]/.test(path))
735
+ for (const path2 of [input.sharedDirectory, input.sharedEnvironmentFile, input.slotsDirectory]) {
736
+ if (!path2.startsWith("/") || /[\r\n]/.test(path2))
231
737
  throw new Error("Runtime paths must be absolute and single-line.");
232
738
  }
233
739
  if (!/^[a-z_][a-z0-9_-]{0,31}$/.test(input.serviceUser))
@@ -370,51 +876,51 @@ var platformExec = async (argv) => {
370
876
  ]);
371
877
  return { exitCode, output: `${stdout}${stderr}` };
372
878
  };
373
- var replaceLink = (path, target) => {
374
- const pending = `${path}.next`;
375
- rmSync(pending, { force: true });
879
+ var replaceLink = (path2, target) => {
880
+ const pending = `${path2}.next`;
881
+ rmSync2(pending, { force: true });
376
882
  if (!target) {
377
- rmSync(path, { force: true });
883
+ rmSync2(path2, { force: true });
378
884
  return;
379
885
  }
380
- symlinkSync(target, pending);
381
- renameSync(pending, path);
886
+ symlinkSync2(target, pending);
887
+ renameSync2(pending, path2);
382
888
  };
383
- var secureRelease = (path, uid, gid) => {
384
- const visit = (current) => {
385
- const metadata = lstatSync(current);
889
+ var secureRelease = (path2, uid, gid) => {
890
+ const visit = (current2) => {
891
+ const metadata = lstatSync(current2);
386
892
  if (metadata.isSymbolicLink())
387
893
  throw new Error("release contains a symbolic link");
388
- chownSync(current, uid, gid);
389
- chmodSync(current, metadata.isDirectory() ? 365 : 292);
894
+ chownSync(current2, uid, gid);
895
+ chmodSync2(current2, metadata.isDirectory() ? 365 : 292);
390
896
  if (metadata.isDirectory())
391
- for (const name of readdirSync(current))
392
- visit(join(current, name));
897
+ for (const name of readdirSync(current2))
898
+ visit(join2(current2, name));
393
899
  };
394
- visit(path);
900
+ visit(path2);
395
901
  };
396
902
  async function activatePlatformRelease(config, requestedRelease, options = {}) {
397
903
  const rendered = renderPlatformActivationFiles(config);
398
904
  const normalized = JSON.parse(rendered.helper);
399
- const releases = realpathSync(join(normalized.root, "releases"));
905
+ const releases = realpathSync(join2(normalized.root, "releases"));
400
906
  const release = realpathSync(requestedRelease);
401
907
  if (!release.startsWith(`${releases}/`) || release === releases)
402
908
  throw new Error("release is outside configured releases directory");
403
909
  for (const required of ["forgezero.deploy.ts", ".fz/deploy.plan.json", "src/index.ts", "bun.lock"]) {
404
- const metadata = statSync(join(release, required));
910
+ const metadata = statSync(join2(release, required));
405
911
  if (!metadata.isFile() || metadata.size < 1)
406
912
  throw new Error(`release is incomplete: ${required}`);
407
913
  }
408
914
  const exec = options.exec ?? platformExec;
409
915
  const request = options.fetch ?? fetch;
410
916
  const sleep = options.sleep ?? Bun.sleep;
411
- const slots = join(normalized.root, "slots");
412
- mkdirSync(slots, { recursive: true, mode: 493 });
413
- const slotFile = join(normalized.root, ".forge-slot");
414
- const previousSlot = existsSync(slotFile) ? readFileSync(slotFile, "utf8").trim() : undefined;
917
+ const slots = join2(normalized.root, "slots");
918
+ mkdirSync2(slots, { recursive: true, mode: 493 });
919
+ const slotFile = join2(normalized.root, ".forge-slot");
920
+ const previousSlot = existsSync2(slotFile) ? readFileSync2(slotFile, "utf8").trim() : undefined;
415
921
  const target = previousSlot === "blue" ? "green" : "blue";
416
922
  const port = target === "blue" ? normalized.bluePort : normalized.greenPort;
417
- const targetLink = join(slots, target);
923
+ const targetLink = join2(slots, target);
418
924
  let previousTarget;
419
925
  try {
420
926
  previousTarget = realpathSync(targetLink);
@@ -451,35 +957,35 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
451
957
  }
452
958
  const upstream = "/etc/nginx/conf.d/forgezero-upstream.conf";
453
959
  const backup = `${upstream}.forgezero-backup`;
454
- if (existsSync(upstream))
455
- copyFileSync(upstream, backup);
960
+ if (existsSync2(upstream))
961
+ copyFileSync2(upstream, backup);
456
962
  else
457
- rmSync(backup, { force: true });
458
- writeFileSync(upstream, `upstream forgezero { server 127.0.0.1:${port}; }
963
+ rmSync2(backup, { force: true });
964
+ writeFileSync2(upstream, `upstream forgezero { server 127.0.0.1:${port}; }
459
965
  `, { mode: 420 });
460
966
  const test = await exec(["/usr/sbin/nginx", "-t"]);
461
967
  const reload = test.exitCode === 0 ? await exec(["/usr/sbin/nginx", "-s", "reload"]) : test;
462
968
  if (reload.exitCode !== 0) {
463
- if (existsSync(backup))
464
- renameSync(backup, upstream);
969
+ if (existsSync2(backup))
970
+ renameSync2(backup, upstream);
465
971
  else
466
- rmSync(upstream, { force: true });
972
+ rmSync2(upstream, { force: true });
467
973
  await exec(["/usr/sbin/nginx", "-t"]);
468
974
  await exec(["/usr/sbin/nginx", "-s", "reload"]);
469
975
  await stopTarget();
470
976
  throw new Error("nginx refused the promoted upstream");
471
977
  }
472
- rmSync(backup, { force: true });
473
- writeFileSync(slotFile, `${target}
978
+ rmSync2(backup, { force: true });
979
+ writeFileSync2(slotFile, `${target}
474
980
  `, { mode: 420 });
475
981
  if (previousSlot && previousSlot !== target) {
476
982
  await sleep(normalized.drainDeadlineMs);
477
983
  await exec(["/usr/bin/systemctl", "stop", `forgezero@${previousSlot}.service`]);
478
984
  }
479
- const old = readdirSync(releases, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join(releases, entry.name)).sort((left, right) => statSync(right).mtimeMs - statSync(left).mtimeMs).slice(normalized.keepReleases);
480
- for (const path of old)
481
- if (path !== release)
482
- rmSync(path, { recursive: true, force: true });
985
+ const old = readdirSync(releases, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join2(releases, entry.name)).sort((left, right) => statSync(right).mtimeMs - statSync(left).mtimeMs).slice(normalized.keepReleases);
986
+ for (const path2 of old)
987
+ if (path2 !== release)
988
+ rmSync2(path2, { recursive: true, force: true });
483
989
  return { release, slot: target };
484
990
  }
485
991
  function validateCollectorUnit(unit) {
@@ -520,6 +1026,7 @@ function planLocalOtlpProof(endpoint, collectorUnit) {
520
1026
  }
521
1027
  export {
522
1028
  validatePlatformSharedEnvironment,
1029
+ validatePlatformInitialInventory,
523
1030
  renderPlatformSharedEnvironment,
524
1031
  renderPlatformNginx,
525
1032
  renderPlatformApiUnits,