@forgezero/agent 0.1.64 → 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.
- package/README.md +745 -157
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap-bundle.d.ts +1 -0
- package/dist/bootstrap-bundle.js +4 -3
- package/dist/bootstrap.d.ts +7 -1
- package/dist/bootstrap.js +264 -13
- package/dist/cli/agent-install.d.ts +9 -0
- package/dist/community-rehearsal-host.d.ts +12 -0
- package/dist/community-rehearsal-host.js +99 -36
- package/dist/definition.js +41 -4
- package/dist/deploy-compiler.js +4 -4
- package/dist/deploy-file.js +41 -4
- package/dist/deploy-plan-runner.js +4 -4
- package/dist/deploy-plan.js +4 -4
- package/dist/fz-agent.js +165 -76
- package/dist/fz.js +501 -258
- package/dist/index.d.ts +1 -1
- package/dist/metal-bootstrap.js +120 -46
- package/dist/metal-helper-socket.js +406 -65
- package/dist/metal-provision.js +402 -61
- package/dist/operator-bootstrap.d.ts +20 -4
- package/dist/operator-bootstrap.js +429 -203
- package/dist/platform-bootstrap-runtime.d.ts +33 -0
- package/dist/platform-bootstrap-runtime.js +551 -44
- package/dist/platform-fleet-verification.js +242 -18
- package/dist/platform-genesis-config.d.ts +7 -1
- package/dist/platform-genesis.d.ts +16 -17
- package/dist/platform-genesis.js +464 -0
- package/dist/provision.js +42 -5
- package/dist/software-helper.js +41 -4
- package/dist/software.d.ts +8 -2
- package/dist/software.js +41 -4
- package/dist/ssh-bootstrap.d.ts +5 -0
- package/dist/ubuntu.d.ts +15 -14
- package/dist/ubuntu.js +345 -9
- package/dist/version.d.ts +1 -1
- package/package.json +8 -4
|
@@ -21,7 +21,30 @@ var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cb
|
|
|
21
21
|
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
22
22
|
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
23
23
|
var OS_CATALOG = [
|
|
24
|
-
{
|
|
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
|
+
}
|
|
25
48
|
];
|
|
26
49
|
var SOFTWARE_CATALOG = [
|
|
27
50
|
{ id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
@@ -32,7 +55,12 @@ var SOFTWARE_CATALOG = [
|
|
|
32
55
|
{ id: "cloudflare-warp", version: "2026.6.822.0-min", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
33
56
|
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
34
57
|
{ id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
35
|
-
{ id: "git", 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" }
|
|
36
64
|
];
|
|
37
65
|
var UBUNTU_2604_X64 = [
|
|
38
66
|
{ requirement: { id: "bun", version: "1.3.14" } },
|
|
@@ -45,6 +73,15 @@ var UBUNTU_2604_X64 = [
|
|
|
45
73
|
{ requirement: { id: "openssh-client", version: "ubuntu-26.04" } },
|
|
46
74
|
{ requirement: { id: "git", version: "ubuntu-26.04" } }
|
|
47
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;
|
|
48
85
|
var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
49
86
|
var DOCKER_DAEMON_PATH = "/etc/docker/daemon.json";
|
|
50
87
|
var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
|
|
@@ -91,7 +128,7 @@ var aptInstall = async (name) => {
|
|
|
91
128
|
};
|
|
92
129
|
async function executeSoftwareOperation(operation) {
|
|
93
130
|
const { software, version } = operation;
|
|
94
|
-
if (!UBUNTU_2604_X64.some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
131
|
+
if (![...UBUNTU_2604_X64, ...UBUNTU_2404_X64].some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
95
132
|
return { exitCode: 2, output: "unsupported software operation" };
|
|
96
133
|
}
|
|
97
134
|
if (operation.kind === "check") {
|
|
@@ -261,7 +298,7 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
261
298
|
}
|
|
262
299
|
const results = [];
|
|
263
300
|
for (const requirement of requirements) {
|
|
264
|
-
const strategy =
|
|
301
|
+
const strategy = strategiesFor(observation)?.find(({ requirement: candidate }) => candidate.id === requirement.id && candidate.version === requirement.version);
|
|
265
302
|
if (!strategy)
|
|
266
303
|
throw new Error(`unsupported software requirement: ${requirement.id}@${requirement.version}`);
|
|
267
304
|
const before = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
|
|
@@ -283,19 +320,15 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
283
320
|
// src/community-rehearsal-host.ts
|
|
284
321
|
import { createHash as createHash2 } from "node:crypto";
|
|
285
322
|
import { lstatSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, symlinkSync as symlinkSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
323
|
+
import { isIP } from "node:net";
|
|
286
324
|
import { dirname } from "node:path";
|
|
287
325
|
var COMMUNITY_REHEARSAL_VERSION = "3.11.14";
|
|
288
326
|
var COMMUNITY_REHEARSAL_ROOT = "/opt/forgezero-rehearsals/community-cluster-api";
|
|
289
327
|
var COMMUNITY_DATABASE_ROOT = "/var/lib/forgezero-rehearsal-cluster";
|
|
290
328
|
var COMMUNITY_CREDENTIAL_PARENT = "/etc/forgezero-rehearsal";
|
|
291
329
|
var COMMUNITY_CREDENTIAL_ROOT = "/etc/forgezero-rehearsal/creds";
|
|
292
|
-
var NODES = new Map([
|
|
293
|
-
["dev-fz-n1", { address: "10.42.0.21", agency: true }],
|
|
294
|
-
["dev-fz-n2", { address: "10.42.0.22", agency: true }],
|
|
295
|
-
["dev-fz-n3", { address: "10.42.0.23", agency: true }],
|
|
296
|
-
["dev-fz-n4", { address: "10.42.0.24", agency: false }]
|
|
297
|
-
]);
|
|
298
330
|
var RELEASE = /^[a-f0-9]{64}$/;
|
|
331
|
+
var SAFE_NAME = /^[a-z][a-z0-9-]{0,62}$/;
|
|
299
332
|
async function ensureCommunityRehearsalArango(execute = executeSoftwareOperation) {
|
|
300
333
|
const requirement = { software: "arangodb", version: COMMUNITY_REHEARSAL_VERSION };
|
|
301
334
|
if ((await execute({ kind: "check", ...requirement })).exitCode === 0)
|
|
@@ -310,50 +343,70 @@ var exactObject = (value) => {
|
|
|
310
343
|
throw new Error("community rehearsal request must be an object");
|
|
311
344
|
return value;
|
|
312
345
|
};
|
|
313
|
-
var
|
|
314
|
-
if (typeof node !== "string")
|
|
346
|
+
var nodeName = (node) => {
|
|
347
|
+
if (typeof node !== "string" || !SAFE_NAME.test(node))
|
|
315
348
|
throw new Error("community rehearsal node is invalid");
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
349
|
+
return node;
|
|
350
|
+
};
|
|
351
|
+
var validateCommunityRehearsalTopology = (value) => {
|
|
352
|
+
if (!Array.isArray(value) || value.length !== 4)
|
|
353
|
+
throw new Error("community rehearsal requires exactly four reviewed nodes");
|
|
354
|
+
const rows = value.map((item, index) => {
|
|
355
|
+
const row = exactObject(item);
|
|
356
|
+
if (Object.keys(row).some((key) => !["name", "address", "agency", "starter", "readPreferred"].includes(key)) || typeof row.name !== "string" || !SAFE_NAME.test(row.name) || typeof row.address !== "string" || isIP(row.address) !== 4 || typeof row.agency !== "boolean" || typeof row.starter !== "boolean" || typeof row.readPreferred !== "boolean") {
|
|
357
|
+
throw new Error(`community rehearsal topology node ${index + 1} is invalid`);
|
|
358
|
+
}
|
|
359
|
+
return row;
|
|
360
|
+
});
|
|
361
|
+
if (new Set(rows.map(({ name }) => name)).size !== rows.length || new Set(rows.map(({ address }) => address)).size !== rows.length) {
|
|
362
|
+
throw new Error("community rehearsal node names and addresses must be unique");
|
|
363
|
+
}
|
|
364
|
+
if (rows.filter(({ agency }) => agency).length !== 3 || rows.filter(({ starter }) => starter).length !== 1 || !rows.find(({ starter }) => starter).agency || rows.filter(({ readPreferred }) => readPreferred).length !== 1 || rows.find(({ readPreferred }) => readPreferred).agency) {
|
|
365
|
+
throw new Error("community rehearsal requires three Agency voters, one voter starter and one non-voting read-preferred member");
|
|
366
|
+
}
|
|
367
|
+
return rows;
|
|
320
368
|
};
|
|
321
369
|
function parseCommunityRehearsalHostRequest(value) {
|
|
322
370
|
const row = exactObject(value);
|
|
323
|
-
const node =
|
|
371
|
+
const node = nodeName(row.node);
|
|
372
|
+
const topology = validateCommunityRehearsalTopology(row.topology);
|
|
373
|
+
const expected = topology.find((entry) => entry.name === node);
|
|
374
|
+
if (!expected)
|
|
375
|
+
throw new Error("community rehearsal node is not present in the reviewed topology");
|
|
324
376
|
const action = row.action;
|
|
325
377
|
if (action === "prepare") {
|
|
326
|
-
const allowed = ["action", "node", "address", "agency", "release", "archivePath", "archiveSha256", "jwt"];
|
|
327
|
-
if (Object.keys(row).some((key) => !allowed.includes(key)) || row.address !==
|
|
378
|
+
const allowed = ["action", "node", "address", "agency", "topology", "release", "archivePath", "archiveSha256", "jwt"];
|
|
379
|
+
if (Object.keys(row).some((key) => !allowed.includes(key)) || row.address !== expected.address || row.agency !== expected.agency || typeof row.release !== "string" || !RELEASE.test(row.release) || row.archiveSha256 !== row.release || row.archivePath !== `/tmp/forgezero-community-${row.release}.tar.gz` || typeof row.jwt !== "string" || !/^[a-f0-9]{64}$/.test(row.jwt)) {
|
|
328
380
|
throw new Error("community rehearsal prepare request does not match the dedicated fleet contract");
|
|
329
381
|
}
|
|
330
|
-
return row;
|
|
382
|
+
return { ...row, topology };
|
|
331
383
|
}
|
|
332
384
|
if (action === "cleanup") {
|
|
333
|
-
if (Object.keys(row).some((key) => !["action", "node"].includes(key))) {
|
|
385
|
+
if (Object.keys(row).some((key) => !["action", "node", "topology"].includes(key))) {
|
|
334
386
|
throw new Error("community rehearsal cleanup has unknown fields");
|
|
335
387
|
}
|
|
336
|
-
return { action, node
|
|
388
|
+
return { action, node, topology };
|
|
337
389
|
}
|
|
338
390
|
if (["database-enable", "api-enable", "database-ready", "starter-ready", "api-ready", "database-status", "api-status"].includes(String(action))) {
|
|
339
|
-
if (Object.keys(row).some((key) => !["action", "node"].includes(key)))
|
|
391
|
+
if (Object.keys(row).some((key) => !["action", "node", "topology"].includes(key)))
|
|
340
392
|
throw new Error("community rehearsal host action has unknown fields");
|
|
341
|
-
return { action, node
|
|
393
|
+
return { action, node, topology };
|
|
342
394
|
}
|
|
343
395
|
if (action === "api") {
|
|
344
|
-
if (Object.keys(row).some((key) => !["action", "node", "operation", "key", "value"].includes(key)) || !["health", "init", "write", "read", "query", "cluster"].includes(String(row.operation)) || row.key !== undefined && (typeof row.key !== "string" || !/^[A-Za-z0-9_.:-]{1,128}$/.test(row.key)) || row.value !== undefined && (typeof row.value !== "string" || row.value.length > 4096)) {
|
|
396
|
+
if (Object.keys(row).some((key) => !["action", "node", "topology", "operation", "key", "value"].includes(key)) || !["health", "init", "write", "read", "query", "cluster"].includes(String(row.operation)) || row.key !== undefined && (typeof row.key !== "string" || !/^[A-Za-z0-9_.:-]{1,128}$/.test(row.key)) || row.value !== undefined && (typeof row.value !== "string" || row.value.length > 4096)) {
|
|
345
397
|
throw new Error("community rehearsal API request is invalid");
|
|
346
398
|
}
|
|
347
399
|
if ((row.operation === "write" || row.operation === "read") && typeof row.key !== "string")
|
|
348
400
|
throw new Error("community rehearsal API key is required");
|
|
349
401
|
if ((row.operation === "write" || row.operation === "query") && typeof row.value !== "string")
|
|
350
402
|
throw new Error("community rehearsal API value is required");
|
|
351
|
-
return row;
|
|
403
|
+
return { ...row, node, topology };
|
|
352
404
|
}
|
|
353
405
|
throw new Error("unsupported community rehearsal host action");
|
|
354
406
|
}
|
|
355
|
-
var databaseUnit = (node) => {
|
|
356
|
-
const
|
|
407
|
+
var databaseUnit = (node, topology) => {
|
|
408
|
+
const starter = topology.find((entry) => entry.starter);
|
|
409
|
+
const join2 = node.starter ? "" : ` --starter.join=${starter.address}`;
|
|
357
410
|
const role = node.agency ? "" : " --cluster.start-agent=false --cluster.start-coordinator=true --cluster.start-dbserver=true";
|
|
358
411
|
return `[Unit]
|
|
359
412
|
Description=ForgeZero isolated Community ${COMMUNITY_REHEARSAL_VERSION} rehearsal cluster
|
|
@@ -388,7 +441,10 @@ LockPersonality=true
|
|
|
388
441
|
WantedBy=multi-user.target
|
|
389
442
|
`;
|
|
390
443
|
};
|
|
391
|
-
var apiUnit = (node) =>
|
|
444
|
+
var apiUnit = (node, topology) => {
|
|
445
|
+
const urls = topology.map(({ address }) => `http://${address}:8529`).join(",");
|
|
446
|
+
const readPreferred = topology.filter(({ readPreferred: readPreferred2 }) => readPreferred2).map(({ address }) => `http://${address}:8529`).join(",");
|
|
447
|
+
return `[Unit]
|
|
392
448
|
Description=ForgeZero typed Community cluster rehearsal API
|
|
393
449
|
After=forgezero-rehearsal-db.service network-online.target
|
|
394
450
|
Requires=forgezero-rehearsal-db.service
|
|
@@ -403,8 +459,8 @@ Environment=HOST=127.0.0.1
|
|
|
403
459
|
Environment=PORT=8787
|
|
404
460
|
Environment=NODE_NAME=${node.name}
|
|
405
461
|
Environment=ARANGO_URL=http://127.0.0.1:8529
|
|
406
|
-
Environment=ARANGO_URLS
|
|
407
|
-
Environment=ARANGO_READ_PREFERRED_URLS
|
|
462
|
+
Environment=ARANGO_URLS=${urls}
|
|
463
|
+
Environment=ARANGO_READ_PREFERRED_URLS=${readPreferred}
|
|
408
464
|
Environment=ARANGO_READ_PREFERRED_FALLBACK=error
|
|
409
465
|
ExecStart=/usr/local/bin/bun src/index.ts
|
|
410
466
|
Restart=always
|
|
@@ -424,8 +480,10 @@ LockPersonality=true
|
|
|
424
480
|
[Install]
|
|
425
481
|
WantedBy=multi-user.target
|
|
426
482
|
`;
|
|
483
|
+
};
|
|
427
484
|
function planCommunityRehearsalPrepare(request) {
|
|
428
|
-
const
|
|
485
|
+
const topology = validateCommunityRehearsalTopology(request.topology);
|
|
486
|
+
const node = topology.find(({ name }) => name === request.node);
|
|
429
487
|
const releaseRoot = `${COMMUNITY_REHEARSAL_ROOT}/releases/${request.release}`;
|
|
430
488
|
return [
|
|
431
489
|
{ kind: "exec", argv: ["/usr/sbin/arangod", "--version"] },
|
|
@@ -440,13 +498,13 @@ function planCommunityRehearsalPrepare(request) {
|
|
|
440
498
|
` },
|
|
441
499
|
{ kind: "exec", argv: ["/usr/bin/chmod", "0400", `${COMMUNITY_CREDENTIAL_ROOT}/arangodb-jwt.cred.next`] },
|
|
442
500
|
{ kind: "exec", argv: ["/usr/bin/mv", "-f", `${COMMUNITY_CREDENTIAL_ROOT}/arangodb-jwt.cred.next`, `${COMMUNITY_CREDENTIAL_ROOT}/arangodb-jwt.cred`] },
|
|
443
|
-
{ kind: "write", path: "/etc/systemd/system/forgezero-rehearsal-db.service", content: databaseUnit(node), mode: 420 },
|
|
501
|
+
{ kind: "write", path: "/etc/systemd/system/forgezero-rehearsal-db.service", content: databaseUnit(node, topology), mode: 420 },
|
|
444
502
|
{ kind: "exec", argv: ["/usr/bin/tar", "-xzf", request.archivePath, "-C", releaseRoot] },
|
|
445
503
|
{ kind: "exec", argv: ["/usr/bin/chown", "-R", "forgezero:forgezero", releaseRoot] },
|
|
446
504
|
{ kind: "exec", argv: ["/usr/sbin/runuser", "-u", "forgezero", "--", "/usr/local/bin/bun", "install", "--cwd", releaseRoot, "--frozen-lockfile", "--production"] },
|
|
447
505
|
{ kind: "unlink", path: `${COMMUNITY_REHEARSAL_ROOT}/current` },
|
|
448
506
|
{ kind: "symlink", target: releaseRoot, path: `${COMMUNITY_REHEARSAL_ROOT}/current` },
|
|
449
|
-
{ kind: "write", path: "/etc/systemd/system/forgezero-community-api.service", content: apiUnit(node), mode: 420 },
|
|
507
|
+
{ kind: "write", path: "/etc/systemd/system/forgezero-community-api.service", content: apiUnit(node, topology), mode: 420 },
|
|
450
508
|
{ kind: "unlink", path: request.archivePath }
|
|
451
509
|
];
|
|
452
510
|
}
|
|
@@ -524,7 +582,10 @@ var responseFor = async (request) => {
|
|
|
524
582
|
async function runCommunityRehearsalHost(request) {
|
|
525
583
|
if ((process.getuid?.() ?? -1) !== 0)
|
|
526
584
|
throw new Error("community-rehearsal must run as root");
|
|
527
|
-
const
|
|
585
|
+
const topology = validateCommunityRehearsalTopology(request.topology);
|
|
586
|
+
const node = topology.find((entry) => entry.name === request.node);
|
|
587
|
+
if (!node)
|
|
588
|
+
throw new Error("community rehearsal node is not present in the reviewed topology");
|
|
528
589
|
if (request.action === "cleanup") {
|
|
529
590
|
await applyOperations(planCommunityRehearsalCleanup());
|
|
530
591
|
return { ok: true, action: request.action, node: node.name };
|
|
@@ -561,7 +622,8 @@ async function runCommunityRehearsalHost(request) {
|
|
|
561
622
|
return { ok: true, action: request.action, node: node.name };
|
|
562
623
|
}
|
|
563
624
|
if (request.action === "database-ready" || request.action === "starter-ready" || request.action === "api-ready") {
|
|
564
|
-
const
|
|
625
|
+
const starter = topology.find((entry) => entry.starter);
|
|
626
|
+
const url = request.action === "database-ready" ? "http://127.0.0.1:8529/_api/version" : request.action === "starter-ready" ? `http://${starter.address}:8528/version` : "http://127.0.0.1:8787/health";
|
|
565
627
|
const response2 = await fetch(url, { signal: AbortSignal.timeout(2000) }).catch(() => {
|
|
566
628
|
return;
|
|
567
629
|
});
|
|
@@ -578,6 +640,7 @@ async function runCommunityRehearsalHost(request) {
|
|
|
578
640
|
return { ok: true, action: request.action, node: node.name, operation: request.operation, status: response.status, body };
|
|
579
641
|
}
|
|
580
642
|
export {
|
|
643
|
+
validateCommunityRehearsalTopology,
|
|
581
644
|
runCommunityRehearsalHost,
|
|
582
645
|
planCommunityRehearsalPrepare,
|
|
583
646
|
planCommunityRehearsalCleanup,
|
package/dist/definition.js
CHANGED
|
@@ -21,7 +21,30 @@ var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cb
|
|
|
21
21
|
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
22
22
|
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
23
23
|
var OS_CATALOG = [
|
|
24
|
-
{
|
|
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
|
+
}
|
|
25
48
|
];
|
|
26
49
|
var SOFTWARE_CATALOG = [
|
|
27
50
|
{ id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
@@ -32,7 +55,12 @@ var SOFTWARE_CATALOG = [
|
|
|
32
55
|
{ id: "cloudflare-warp", version: "2026.6.822.0-min", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
33
56
|
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
34
57
|
{ id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
35
|
-
{ id: "git", 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" }
|
|
36
64
|
];
|
|
37
65
|
var UBUNTU_2604_X64 = [
|
|
38
66
|
{ requirement: { id: "bun", version: "1.3.14" } },
|
|
@@ -45,6 +73,15 @@ var UBUNTU_2604_X64 = [
|
|
|
45
73
|
{ requirement: { id: "openssh-client", version: "ubuntu-26.04" } },
|
|
46
74
|
{ requirement: { id: "git", version: "ubuntu-26.04" } }
|
|
47
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;
|
|
48
85
|
var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
49
86
|
var DOCKER_DAEMON_PATH = "/etc/docker/daemon.json";
|
|
50
87
|
var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
|
|
@@ -91,7 +128,7 @@ var aptInstall = async (name) => {
|
|
|
91
128
|
};
|
|
92
129
|
async function executeSoftwareOperation(operation) {
|
|
93
130
|
const { software, version } = operation;
|
|
94
|
-
if (!UBUNTU_2604_X64.some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
131
|
+
if (![...UBUNTU_2604_X64, ...UBUNTU_2404_X64].some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
95
132
|
return { exitCode: 2, output: "unsupported software operation" };
|
|
96
133
|
}
|
|
97
134
|
if (operation.kind === "check") {
|
|
@@ -261,7 +298,7 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
261
298
|
}
|
|
262
299
|
const results = [];
|
|
263
300
|
for (const requirement of requirements) {
|
|
264
|
-
const strategy =
|
|
301
|
+
const strategy = strategiesFor(observation)?.find(({ requirement: candidate }) => candidate.id === requirement.id && candidate.version === requirement.version);
|
|
265
302
|
if (!strategy)
|
|
266
303
|
throw new Error(`unsupported software requirement: ${requirement.id}@${requirement.version}`);
|
|
267
304
|
const before = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
|
package/dist/deploy-compiler.js
CHANGED
|
@@ -92,13 +92,13 @@ function validateBuiltInProvider(requirement2, where) {
|
|
|
92
92
|
return;
|
|
93
93
|
const expected = {
|
|
94
94
|
"forgezero.bun": { capability: "runtime.javascript", versions: ["1.3.14"], keys: ["installScope"] },
|
|
95
|
-
"forgezero.docker": { capability: "runtime.container", versions: ["ubuntu-26.04"], keys: ["installScope", "storageDriver", "dataRoot", "liveRestore", "logDriver", "defaultNetwork", "rootless"] },
|
|
96
|
-
"forgezero.nginx": { capability: "proxy.http", versions: ["ubuntu-26.04"], keys: ["installScope", "websocket", "maximumBodyMiB", "workerProcesses", "workerConnections"] },
|
|
95
|
+
"forgezero.docker": { capability: "runtime.container", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "storageDriver", "dataRoot", "liveRestore", "logDriver", "defaultNetwork", "rootless"] },
|
|
96
|
+
"forgezero.nginx": { capability: "proxy.http", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "websocket", "maximumBodyMiB", "workerProcesses", "workerConnections"] },
|
|
97
97
|
"forgezero.arangodb": { capability: "database.arangodb", versions: ["3.11.14"], keys: ["installScope", "runtime", "dataPath", "bind"] },
|
|
98
98
|
"forgezero.cloudflared": { capability: "network.public-tunnel", versions: ["2026.7.3"], keys: ["installScope", "mode"] },
|
|
99
99
|
"forgezero.warp": { capability: "network.private", versions: ["2026.6.822.0-min"], keys: ["installScope", "mode"] },
|
|
100
|
-
"forgezero.ufw": { capability: "security.firewall", versions: ["ubuntu-26.04"], keys: ["installScope", "defaultIncoming", "defaultOutgoing"] },
|
|
101
|
-
"forgezero.openssh": { capability: "transport.ssh", versions: ["ubuntu-26.04"], keys: ["installScope", "mode"] },
|
|
100
|
+
"forgezero.ufw": { capability: "security.firewall", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "defaultIncoming", "defaultOutgoing"] },
|
|
101
|
+
"forgezero.openssh": { capability: "transport.ssh", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "mode"] },
|
|
102
102
|
"forgezero.systemd": { capability: "service.manager", versions: ["ubuntu-26.04"], keys: ["installScope", "credentials", "serviceSandbox"] }
|
|
103
103
|
};
|
|
104
104
|
const contract = expected[requirement2.provider];
|
package/dist/deploy-file.js
CHANGED
|
@@ -21,7 +21,30 @@ var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cb
|
|
|
21
21
|
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
22
22
|
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
23
23
|
var OS_CATALOG = [
|
|
24
|
-
{
|
|
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
|
+
}
|
|
25
48
|
];
|
|
26
49
|
var SOFTWARE_CATALOG = [
|
|
27
50
|
{ id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
@@ -32,7 +55,12 @@ var SOFTWARE_CATALOG = [
|
|
|
32
55
|
{ id: "cloudflare-warp", version: "2026.6.822.0-min", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
33
56
|
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
34
57
|
{ id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
35
|
-
{ id: "git", 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" }
|
|
36
64
|
];
|
|
37
65
|
var UBUNTU_2604_X64 = [
|
|
38
66
|
{ requirement: { id: "bun", version: "1.3.14" } },
|
|
@@ -45,6 +73,15 @@ var UBUNTU_2604_X64 = [
|
|
|
45
73
|
{ requirement: { id: "openssh-client", version: "ubuntu-26.04" } },
|
|
46
74
|
{ requirement: { id: "git", version: "ubuntu-26.04" } }
|
|
47
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;
|
|
48
85
|
var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
49
86
|
var DOCKER_DAEMON_PATH = "/etc/docker/daemon.json";
|
|
50
87
|
var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
|
|
@@ -91,7 +128,7 @@ var aptInstall = async (name) => {
|
|
|
91
128
|
};
|
|
92
129
|
async function executeSoftwareOperation(operation) {
|
|
93
130
|
const { software, version } = operation;
|
|
94
|
-
if (!UBUNTU_2604_X64.some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
131
|
+
if (![...UBUNTU_2604_X64, ...UBUNTU_2404_X64].some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
95
132
|
return { exitCode: 2, output: "unsupported software operation" };
|
|
96
133
|
}
|
|
97
134
|
if (operation.kind === "check") {
|
|
@@ -261,7 +298,7 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
261
298
|
}
|
|
262
299
|
const results = [];
|
|
263
300
|
for (const requirement of requirements) {
|
|
264
|
-
const strategy =
|
|
301
|
+
const strategy = strategiesFor(observation)?.find(({ requirement: candidate }) => candidate.id === requirement.id && candidate.version === requirement.version);
|
|
265
302
|
if (!strategy)
|
|
266
303
|
throw new Error(`unsupported software requirement: ${requirement.id}@${requirement.version}`);
|
|
267
304
|
const before = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
|
|
@@ -92,13 +92,13 @@ function validateBuiltInProvider(requirement2, where) {
|
|
|
92
92
|
return;
|
|
93
93
|
const expected = {
|
|
94
94
|
"forgezero.bun": { capability: "runtime.javascript", versions: ["1.3.14"], keys: ["installScope"] },
|
|
95
|
-
"forgezero.docker": { capability: "runtime.container", versions: ["ubuntu-26.04"], keys: ["installScope", "storageDriver", "dataRoot", "liveRestore", "logDriver", "defaultNetwork", "rootless"] },
|
|
96
|
-
"forgezero.nginx": { capability: "proxy.http", versions: ["ubuntu-26.04"], keys: ["installScope", "websocket", "maximumBodyMiB", "workerProcesses", "workerConnections"] },
|
|
95
|
+
"forgezero.docker": { capability: "runtime.container", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "storageDriver", "dataRoot", "liveRestore", "logDriver", "defaultNetwork", "rootless"] },
|
|
96
|
+
"forgezero.nginx": { capability: "proxy.http", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "websocket", "maximumBodyMiB", "workerProcesses", "workerConnections"] },
|
|
97
97
|
"forgezero.arangodb": { capability: "database.arangodb", versions: ["3.11.14"], keys: ["installScope", "runtime", "dataPath", "bind"] },
|
|
98
98
|
"forgezero.cloudflared": { capability: "network.public-tunnel", versions: ["2026.7.3"], keys: ["installScope", "mode"] },
|
|
99
99
|
"forgezero.warp": { capability: "network.private", versions: ["2026.6.822.0-min"], keys: ["installScope", "mode"] },
|
|
100
|
-
"forgezero.ufw": { capability: "security.firewall", versions: ["ubuntu-26.04"], keys: ["installScope", "defaultIncoming", "defaultOutgoing"] },
|
|
101
|
-
"forgezero.openssh": { capability: "transport.ssh", versions: ["ubuntu-26.04"], keys: ["installScope", "mode"] },
|
|
100
|
+
"forgezero.ufw": { capability: "security.firewall", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "defaultIncoming", "defaultOutgoing"] },
|
|
101
|
+
"forgezero.openssh": { capability: "transport.ssh", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "mode"] },
|
|
102
102
|
"forgezero.systemd": { capability: "service.manager", versions: ["ubuntu-26.04"], keys: ["installScope", "credentials", "serviceSandbox"] }
|
|
103
103
|
};
|
|
104
104
|
const contract = expected[requirement2.provider];
|
package/dist/deploy-plan.js
CHANGED
|
@@ -92,13 +92,13 @@ function validateBuiltInProvider(requirement2, where) {
|
|
|
92
92
|
return;
|
|
93
93
|
const expected = {
|
|
94
94
|
"forgezero.bun": { capability: "runtime.javascript", versions: ["1.3.14"], keys: ["installScope"] },
|
|
95
|
-
"forgezero.docker": { capability: "runtime.container", versions: ["ubuntu-26.04"], keys: ["installScope", "storageDriver", "dataRoot", "liveRestore", "logDriver", "defaultNetwork", "rootless"] },
|
|
96
|
-
"forgezero.nginx": { capability: "proxy.http", versions: ["ubuntu-26.04"], keys: ["installScope", "websocket", "maximumBodyMiB", "workerProcesses", "workerConnections"] },
|
|
95
|
+
"forgezero.docker": { capability: "runtime.container", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "storageDriver", "dataRoot", "liveRestore", "logDriver", "defaultNetwork", "rootless"] },
|
|
96
|
+
"forgezero.nginx": { capability: "proxy.http", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "websocket", "maximumBodyMiB", "workerProcesses", "workerConnections"] },
|
|
97
97
|
"forgezero.arangodb": { capability: "database.arangodb", versions: ["3.11.14"], keys: ["installScope", "runtime", "dataPath", "bind"] },
|
|
98
98
|
"forgezero.cloudflared": { capability: "network.public-tunnel", versions: ["2026.7.3"], keys: ["installScope", "mode"] },
|
|
99
99
|
"forgezero.warp": { capability: "network.private", versions: ["2026.6.822.0-min"], keys: ["installScope", "mode"] },
|
|
100
|
-
"forgezero.ufw": { capability: "security.firewall", versions: ["ubuntu-26.04"], keys: ["installScope", "defaultIncoming", "defaultOutgoing"] },
|
|
101
|
-
"forgezero.openssh": { capability: "transport.ssh", versions: ["ubuntu-26.04"], keys: ["installScope", "mode"] },
|
|
100
|
+
"forgezero.ufw": { capability: "security.firewall", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "defaultIncoming", "defaultOutgoing"] },
|
|
101
|
+
"forgezero.openssh": { capability: "transport.ssh", versions: ["ubuntu-24.04", "ubuntu-26.04"], keys: ["installScope", "mode"] },
|
|
102
102
|
"forgezero.systemd": { capability: "service.manager", versions: ["ubuntu-26.04"], keys: ["installScope", "credentials", "serviceSandbox"] }
|
|
103
103
|
};
|
|
104
104
|
const contract = expected[requirement2.provider];
|