@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
package/dist/index.d.ts
CHANGED
|
@@ -55,7 +55,7 @@ export type { SnpAttestationOptions } from './snp-attestation';
|
|
|
55
55
|
export { attestNodeOnce, startNodeAttestation } from './attestation-client';
|
|
56
56
|
export type { NodeAttestationOptions } from './attestation-client';
|
|
57
57
|
export { applyMetalIsolation, metalGuestSliceUnit, metalHousekeepingDropIn } from './metal-isolation';
|
|
58
|
-
export { assertSupportedGuestImage, SUPPORTED_GUEST_IMAGE } from './ubuntu';
|
|
58
|
+
export { assertSupportedGuestImage, supportedGuestImage, SUPPORTED_GUEST_IMAGE, SUPPORTED_GUEST_IMAGES } from './ubuntu';
|
|
59
59
|
export { calibrateHttpConcurrency, localCalibrationEndpoint } from './capacity-calibration';
|
|
60
60
|
export type { CapacityCalibration, CapacityCalibrationOptions, CapacityStage } from './capacity-calibration';
|
|
61
61
|
/**
|
package/dist/metal-bootstrap.js
CHANGED
|
@@ -27,15 +27,70 @@ import { join as join2 } from "path";
|
|
|
27
27
|
import { dirname, isAbsolute, join } from "path";
|
|
28
28
|
import { isIP } from "net";
|
|
29
29
|
|
|
30
|
+
// src/software.ts
|
|
31
|
+
var PINNED_BUN_VERSION = "1.3.14";
|
|
32
|
+
var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f";
|
|
33
|
+
var OS_CATALOG = [
|
|
34
|
+
{
|
|
35
|
+
id: "ubuntu",
|
|
36
|
+
version: "26.04",
|
|
37
|
+
architecture: "x64",
|
|
38
|
+
status: "active",
|
|
39
|
+
imageKey: "ubuntu-resolute-20260731",
|
|
40
|
+
imageLabel: "Ubuntu 26.04 LTS Resolute",
|
|
41
|
+
imageVersion: "2026-07-31",
|
|
42
|
+
imageUrl: "https://cloud-images.ubuntu.com/releases/resolute/release-20260731/ubuntu-26.04-server-cloudimg-amd64.img",
|
|
43
|
+
imageSha256: "9dc7c5363c0146a08ba0c9aa834d82c2c6dfbb1c471ad9a2f0aba1189e21be05",
|
|
44
|
+
confidentialModes: ["sev-snp"]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "ubuntu",
|
|
48
|
+
version: "24.04",
|
|
49
|
+
architecture: "x64",
|
|
50
|
+
status: "active",
|
|
51
|
+
imageKey: "ubuntu-noble-20260705",
|
|
52
|
+
imageLabel: "Ubuntu 24.04 LTS Noble",
|
|
53
|
+
imageVersion: "2026-07-05",
|
|
54
|
+
imageUrl: "https://cloud-images.ubuntu.com/releases/noble/release-20260705/ubuntu-24.04-server-cloudimg-amd64.img",
|
|
55
|
+
imageSha256: "ffe6203da54deeb6db5d2a98a83f9ec8e55f149d3f7ba622e1abe5fa966ee3d6",
|
|
56
|
+
confidentialModes: []
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
|
|
60
|
+
"data-root": "/var/lib/docker",
|
|
61
|
+
"storage-driver": "overlay2",
|
|
62
|
+
"live-restore": true,
|
|
63
|
+
"log-driver": "local",
|
|
64
|
+
"log-opts": { "max-size": "10m", "max-file": "3" }
|
|
65
|
+
}, null, 2)}
|
|
66
|
+
`;
|
|
67
|
+
|
|
30
68
|
// src/ubuntu.ts
|
|
69
|
+
var current = OS_CATALOG[0];
|
|
31
70
|
var SUPPORTED_GUEST_IMAGE = Object.freeze({
|
|
32
|
-
key:
|
|
33
|
-
family:
|
|
34
|
-
version:
|
|
35
|
-
label:
|
|
36
|
-
url:
|
|
37
|
-
sha256:
|
|
71
|
+
key: current.imageKey,
|
|
72
|
+
family: `${current.id}-${current.version}`,
|
|
73
|
+
version: current.imageVersion,
|
|
74
|
+
label: current.imageLabel,
|
|
75
|
+
url: current.imageUrl,
|
|
76
|
+
sha256: current.imageSha256
|
|
38
77
|
});
|
|
78
|
+
var SUPPORTED_GUEST_IMAGES = Object.freeze(OS_CATALOG.filter(({ status }) => status === "active").map(({ id, imageKey, imageLabel, imageVersion, version, confidentialModes }) => ({
|
|
79
|
+
key: imageKey,
|
|
80
|
+
label: imageLabel,
|
|
81
|
+
family: `${id}-${version}`,
|
|
82
|
+
version: imageVersion,
|
|
83
|
+
confidential: confidentialModes.some((mode) => mode === "sev-snp")
|
|
84
|
+
})));
|
|
85
|
+
function assertSupportedGuestImage(imageKey, confidential = false) {
|
|
86
|
+
const selected = OS_CATALOG.find((entry) => entry.status === "active" && entry.imageKey === imageKey);
|
|
87
|
+
if (!selected) {
|
|
88
|
+
throw new Error(`unsupported guest image ${imageKey}; supported images: ${SUPPORTED_GUEST_IMAGES.map(({ key }) => key).join(", ")}`);
|
|
89
|
+
}
|
|
90
|
+
if (confidential && !selected.confidentialModes.some((mode) => mode === "sev-snp")) {
|
|
91
|
+
throw new Error(`guest image ${imageKey} is not approved for SEV-SNP`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
39
94
|
|
|
40
95
|
// src/metal-provision.ts
|
|
41
96
|
var SAFE_NAME = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,63}$/;
|
|
@@ -86,9 +141,16 @@ function validateMetalProfile(profile) {
|
|
|
86
141
|
if (telemetryEndpoint.protocol !== "https:" || telemetryEndpoint.username || telemetryEndpoint.password || telemetryEndpoint.search || telemetryEndpoint.hash || isIP(telemetryEndpoint.hostname) !== 0 || !telemetryEndpoint.hostname.includes(".") || telemetryEndpoint.hostname === "localhost" || telemetryEndpoint.hostname.endsWith(".local"))
|
|
87
142
|
throw new MetalProvisionError("Agent telemetry endpoint must be a public HTTPS DNS coordinate without credentials, query or fragment");
|
|
88
143
|
const imageKeys = Object.keys(profile.images);
|
|
89
|
-
if (imageKeys.length
|
|
90
|
-
throw new MetalProvisionError(
|
|
144
|
+
if (imageKeys.length === 0)
|
|
145
|
+
throw new MetalProvisionError("metal profile requires at least one pinned guest image");
|
|
146
|
+
for (const imageKey of imageKeys) {
|
|
147
|
+
const catalog = OS_CATALOG.find((entry) => entry.status === "active" && entry.imageKey === imageKey);
|
|
148
|
+
if (!catalog || profile.images[imageKey]?.sha256 !== catalog.imageSha256) {
|
|
149
|
+
throw new MetalProvisionError(`metal profile image ${imageKey} does not match the pinned OS catalog`);
|
|
150
|
+
}
|
|
91
151
|
}
|
|
152
|
+
if (profile.confidential && !imageKeys.some((key) => OS_CATALOG.find((entry) => entry.imageKey === key)?.confidentialModes.some((mode) => mode === "sev-snp")))
|
|
153
|
+
throw new MetalProvisionError("confidential metal profile requires a pinned SEV-SNP guest image");
|
|
92
154
|
if (!Array.isArray(profile.cpuPools) || profile.cpuPools.length === 0) {
|
|
93
155
|
throw new MetalProvisionError("at least one exclusive CPU pool is required");
|
|
94
156
|
}
|
|
@@ -292,7 +354,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
|
|
|
292
354
|
}
|
|
293
355
|
|
|
294
356
|
// src/version.ts
|
|
295
|
-
var VERSION = "0.1.
|
|
357
|
+
var VERSION = "0.1.67";
|
|
296
358
|
|
|
297
359
|
// src/otel-collector.ts
|
|
298
360
|
var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
|
|
@@ -558,10 +620,9 @@ function validateMetalBootstrapConfig(config) {
|
|
|
558
620
|
if (config.profile.stateDir !== "/etc/forgezero/metal-guests" || config.profile.seedDir !== "/var/lib/forgezero/seed" || config.profile.unitDir !== UNIT_DIRECTORY || config.profile.legacyStateDir !== undefined) {
|
|
559
621
|
throw new MetalBootstrapError("metal bootstrap uses fixed state, seed, and systemd unit directories");
|
|
560
622
|
}
|
|
561
|
-
const image
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
623
|
+
for (const image of Object.values(config.profile.images))
|
|
624
|
+
if (!image.path.startsWith("/var/lib/forgezero/images/") || resolve(image.path) !== image.path || /[\0\r\n]/.test(image.path))
|
|
625
|
+
throw new MetalBootstrapError("every pinned guest image must use the fixed image directory");
|
|
565
626
|
if (config.profile.bunVersion !== SUPPORTED_BUN_VERSION || config.profile.bunReleaseSha256 !== SUPPORTED_BUN_RELEASE_SHA256 || config.profile.agentVersion !== VERSION) {
|
|
566
627
|
throw new MetalBootstrapError("metal guest runtime must use the package-owned Bun and Agent release coordinates");
|
|
567
628
|
}
|
|
@@ -863,9 +924,11 @@ var preflight = async (config, exec) => {
|
|
|
863
924
|
throw new MetalBootstrapError("/dev/sev is required by the confidential profile");
|
|
864
925
|
await runChecked(exec, ["/usr/bin/qemu-system-x86_64", "-object", "sev-snp-guest,help"]);
|
|
865
926
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
927
|
+
for (const image of Object.values(config.profile.images)) {
|
|
928
|
+
const digest = (await runChecked(exec, ["/usr/bin/sha256sum", image.path])).stdout.split(/\s+/)[0];
|
|
929
|
+
if (digest !== image.sha256)
|
|
930
|
+
throw new MetalBootstrapError("pinned guest image digest mismatch");
|
|
931
|
+
}
|
|
869
932
|
};
|
|
870
933
|
var assertSupportedMetalHost = () => {
|
|
871
934
|
if (process.platform !== "linux" || process.arch !== "x64") {
|
|
@@ -877,34 +940,38 @@ var assertSupportedMetalHost = () => {
|
|
|
877
940
|
}
|
|
878
941
|
};
|
|
879
942
|
var ensurePinnedGuestImage = async (config, exec) => {
|
|
880
|
-
const image
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
"--fail",
|
|
889
|
-
"--location",
|
|
890
|
-
"--proto",
|
|
891
|
-
"=https",
|
|
892
|
-
"--tlsv1.2",
|
|
893
|
-
"--output",
|
|
894
|
-
temporary,
|
|
895
|
-
SUPPORTED_GUEST_IMAGE.url
|
|
896
|
-
]);
|
|
897
|
-
const digest = (await runChecked(exec, ["/usr/bin/sha256sum", temporary])).stdout.split(/\s+/)[0];
|
|
898
|
-
if (digest !== image.sha256)
|
|
899
|
-
throw new MetalBootstrapError("downloaded guest image digest mismatch");
|
|
900
|
-
chmodSync(temporary, 292);
|
|
901
|
-
chownSync(temporary, 0, 0);
|
|
902
|
-
renameSync(temporary, image.path);
|
|
903
|
-
} catch (cause) {
|
|
943
|
+
for (const [imageKey, image] of Object.entries(config.profile.images)) {
|
|
944
|
+
if (existsSync(image.path))
|
|
945
|
+
continue;
|
|
946
|
+
const catalog = OS_CATALOG.find((entry) => entry.status === "active" && entry.imageKey === imageKey);
|
|
947
|
+
if (!catalog)
|
|
948
|
+
throw new MetalBootstrapError(`guest image ${imageKey} is not active`);
|
|
949
|
+
mkdirSync2(dirname2(image.path), { recursive: true, mode: 493 });
|
|
950
|
+
const temporary = `${image.path}.next-${process.pid}`;
|
|
904
951
|
try {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
952
|
+
await runChecked(exec, [
|
|
953
|
+
"/usr/bin/curl",
|
|
954
|
+
"--fail",
|
|
955
|
+
"--location",
|
|
956
|
+
"--proto",
|
|
957
|
+
"=https",
|
|
958
|
+
"--tlsv1.2",
|
|
959
|
+
"--output",
|
|
960
|
+
temporary,
|
|
961
|
+
catalog.imageUrl
|
|
962
|
+
]);
|
|
963
|
+
const digest = (await runChecked(exec, ["/usr/bin/sha256sum", temporary])).stdout.split(/\s+/)[0];
|
|
964
|
+
if (digest !== image.sha256)
|
|
965
|
+
throw new MetalBootstrapError("downloaded guest image digest mismatch");
|
|
966
|
+
chmodSync(temporary, 292);
|
|
967
|
+
chownSync(temporary, 0, 0);
|
|
968
|
+
renameSync(temporary, image.path);
|
|
969
|
+
} catch (cause) {
|
|
970
|
+
try {
|
|
971
|
+
unlinkSync(temporary);
|
|
972
|
+
} catch {}
|
|
973
|
+
throw cause;
|
|
974
|
+
}
|
|
908
975
|
}
|
|
909
976
|
};
|
|
910
977
|
async function applyMetalBootstrap(config, options) {
|
|
@@ -1064,10 +1131,17 @@ async function metalBootstrapStatus(exec = defaultExec2) {
|
|
|
1064
1131
|
])).exitCode !== 0) {
|
|
1065
1132
|
problems.push("local OTLP metrics receiver did not accept a proof request");
|
|
1066
1133
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1134
|
+
imageVerified = true;
|
|
1135
|
+
for (const image of Object.values(profile.images)) {
|
|
1136
|
+
if (!existsSync(image.path)) {
|
|
1137
|
+
imageVerified = false;
|
|
1138
|
+
break;
|
|
1139
|
+
}
|
|
1069
1140
|
const digest = (await exec(["/usr/bin/sha256sum", image.path])).stdout.split(/\s+/)[0];
|
|
1070
|
-
|
|
1141
|
+
if (digest !== image.sha256) {
|
|
1142
|
+
imageVerified = false;
|
|
1143
|
+
break;
|
|
1144
|
+
}
|
|
1071
1145
|
}
|
|
1072
1146
|
if (!imageVerified)
|
|
1073
1147
|
problems.push("pinned guest image is absent or has the wrong digest");
|