@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.
- 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/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 +487 -251
- 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
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
// src/software.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import {
|
|
4
|
+
accessSync,
|
|
5
|
+
chmodSync,
|
|
6
|
+
copyFileSync,
|
|
7
|
+
existsSync,
|
|
8
|
+
mkdtempSync,
|
|
9
|
+
mkdirSync,
|
|
10
|
+
readFileSync,
|
|
11
|
+
renameSync,
|
|
12
|
+
rmSync,
|
|
13
|
+
symlinkSync,
|
|
14
|
+
unlinkSync,
|
|
15
|
+
writeFileSync
|
|
16
|
+
} from "node:fs";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
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
|
+
export {
|
|
460
|
+
validatePlatformGenesisGuests,
|
|
461
|
+
platformGenesisClaims,
|
|
462
|
+
platformCommunityRehearsalDeletionClaims,
|
|
463
|
+
platformCommunityRehearsalClaims
|
|
464
|
+
};
|
package/dist/provision.js
CHANGED
|
@@ -699,7 +699,30 @@ var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cb
|
|
|
699
699
|
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
700
700
|
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
701
701
|
var OS_CATALOG = [
|
|
702
|
-
{
|
|
702
|
+
{
|
|
703
|
+
id: "ubuntu",
|
|
704
|
+
version: "26.04",
|
|
705
|
+
architecture: "x64",
|
|
706
|
+
status: "active",
|
|
707
|
+
imageKey: "ubuntu-resolute-20260731",
|
|
708
|
+
imageLabel: "Ubuntu 26.04 LTS Resolute",
|
|
709
|
+
imageVersion: "2026-07-31",
|
|
710
|
+
imageUrl: "https://cloud-images.ubuntu.com/releases/resolute/release-20260731/ubuntu-26.04-server-cloudimg-amd64.img",
|
|
711
|
+
imageSha256: "9dc7c5363c0146a08ba0c9aa834d82c2c6dfbb1c471ad9a2f0aba1189e21be05",
|
|
712
|
+
confidentialModes: ["sev-snp"]
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
id: "ubuntu",
|
|
716
|
+
version: "24.04",
|
|
717
|
+
architecture: "x64",
|
|
718
|
+
status: "active",
|
|
719
|
+
imageKey: "ubuntu-noble-20260705",
|
|
720
|
+
imageLabel: "Ubuntu 24.04 LTS Noble",
|
|
721
|
+
imageVersion: "2026-07-05",
|
|
722
|
+
imageUrl: "https://cloud-images.ubuntu.com/releases/noble/release-20260705/ubuntu-24.04-server-cloudimg-amd64.img",
|
|
723
|
+
imageSha256: "ffe6203da54deeb6db5d2a98a83f9ec8e55f149d3f7ba622e1abe5fa966ee3d6",
|
|
724
|
+
confidentialModes: []
|
|
725
|
+
}
|
|
703
726
|
];
|
|
704
727
|
var SOFTWARE_CATALOG = [
|
|
705
728
|
{ id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
@@ -710,7 +733,12 @@ var SOFTWARE_CATALOG = [
|
|
|
710
733
|
{ id: "cloudflare-warp", version: "2026.6.822.0-min", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
711
734
|
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
712
735
|
{ id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
713
|
-
{ id: "git", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
736
|
+
{ id: "git", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
737
|
+
{ id: "docker", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
738
|
+
{ id: "nginx", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
739
|
+
{ id: "ufw", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
740
|
+
{ id: "openssh-client", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
741
|
+
{ id: "git", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
714
742
|
];
|
|
715
743
|
var UBUNTU_2604_X64 = [
|
|
716
744
|
{ requirement: { id: "bun", version: "1.3.14" } },
|
|
@@ -723,6 +751,15 @@ var UBUNTU_2604_X64 = [
|
|
|
723
751
|
{ requirement: { id: "openssh-client", version: "ubuntu-26.04" } },
|
|
724
752
|
{ requirement: { id: "git", version: "ubuntu-26.04" } }
|
|
725
753
|
];
|
|
754
|
+
var UBUNTU_2404_X64 = [
|
|
755
|
+
{ requirement: { id: "bun", version: "1.3.14" } },
|
|
756
|
+
{ requirement: { id: "docker", version: "ubuntu-24.04" } },
|
|
757
|
+
{ requirement: { id: "nginx", version: "ubuntu-24.04" } },
|
|
758
|
+
{ requirement: { id: "ufw", version: "ubuntu-24.04" } },
|
|
759
|
+
{ requirement: { id: "openssh-client", version: "ubuntu-24.04" } },
|
|
760
|
+
{ requirement: { id: "git", version: "ubuntu-24.04" } }
|
|
761
|
+
];
|
|
762
|
+
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;
|
|
726
763
|
var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
727
764
|
var DOCKER_DAEMON_PATH = "/etc/docker/daemon.json";
|
|
728
765
|
var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
|
|
@@ -769,7 +806,7 @@ var aptInstall = async (name) => {
|
|
|
769
806
|
};
|
|
770
807
|
async function executeSoftwareOperation(operation) {
|
|
771
808
|
const { software, version } = operation;
|
|
772
|
-
if (!UBUNTU_2604_X64.some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
809
|
+
if (![...UBUNTU_2604_X64, ...UBUNTU_2404_X64].some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
773
810
|
return { exitCode: 2, output: "unsupported software operation" };
|
|
774
811
|
}
|
|
775
812
|
if (operation.kind === "check") {
|
|
@@ -939,7 +976,7 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
939
976
|
}
|
|
940
977
|
const results = [];
|
|
941
978
|
for (const requirement of requirements) {
|
|
942
|
-
const strategy =
|
|
979
|
+
const strategy = strategiesFor(observation)?.find(({ requirement: candidate }) => candidate.id === requirement.id && candidate.version === requirement.version);
|
|
943
980
|
if (!strategy)
|
|
944
981
|
throw new Error(`unsupported software requirement: ${requirement.id}@${requirement.version}`);
|
|
945
982
|
const before = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
|
|
@@ -2088,7 +2125,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
2088
2125
|
}
|
|
2089
2126
|
|
|
2090
2127
|
// src/version.ts
|
|
2091
|
-
var VERSION3 = "0.1.
|
|
2128
|
+
var VERSION3 = "0.1.67";
|
|
2092
2129
|
|
|
2093
2130
|
// src/egress-policy.ts
|
|
2094
2131
|
import { realpathSync as realpathSync3 } from "node:fs";
|
package/dist/software-helper.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 });
|