@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/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
  /**
@@ -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: "ubuntu-resolute-20260731",
33
- family: "ubuntu-26.04",
34
- version: "2026-07-31",
35
- label: "Ubuntu 26.04 LTS Resolute",
36
- url: "https://cloud-images.ubuntu.com/releases/resolute/release-20260731/ubuntu-26.04-server-cloudimg-amd64.img",
37
- sha256: "9dc7c5363c0146a08ba0c9aa834d82c2c6dfbb1c471ad9a2f0aba1189e21be05"
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 !== 1 || imageKeys[0] !== SUPPORTED_GUEST_IMAGE.key || profile.images[SUPPORTED_GUEST_IMAGE.key]?.sha256 !== SUPPORTED_GUEST_IMAGE.sha256) {
90
- throw new MetalProvisionError(`metal profile must contain only the pinned ${SUPPORTED_GUEST_IMAGE.key} image contract`);
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.65";
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 = config.profile.images[Object.keys(config.profile.images)[0]];
562
- if (!image.path.startsWith("/var/lib/forgezero/images/") || resolve(image.path) !== image.path || /[\0\r\n]/.test(image.path)) {
563
- throw new MetalBootstrapError("the pinned guest image must use the fixed image directory");
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
- const digest = (await runChecked(exec, ["/usr/bin/sha256sum", config.profile.images[Object.keys(config.profile.images)[0]].path])).stdout.split(/\s+/)[0];
867
- if (digest !== config.profile.images[Object.keys(config.profile.images)[0]].sha256)
868
- throw new MetalBootstrapError("pinned guest image digest mismatch");
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 = config.profile.images[SUPPORTED_GUEST_IMAGE.key];
881
- if (existsSync(image.path))
882
- return;
883
- mkdirSync2(dirname2(image.path), { recursive: true, mode: 493 });
884
- const temporary = `${image.path}.next-${process.pid}`;
885
- try {
886
- await runChecked(exec, [
887
- "/usr/bin/curl",
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
- unlinkSync(temporary);
906
- } catch {}
907
- throw cause;
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
- const image = profile.images[Object.keys(profile.images)[0]];
1068
- if (existsSync(image.path)) {
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
- imageVerified = digest === image.sha256;
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");