@forgezero/agent 0.1.136 → 0.1.138

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.
@@ -917,7 +917,7 @@ async function postSignedNode(options, path, body) {
917
917
  }
918
918
 
919
919
  // src/version.ts
920
- var VERSION3 = "0.1.136";
920
+ var VERSION3 = "0.1.138";
921
921
 
922
922
  // src/agent-heartbeat.ts
923
923
  function readAgentHostMetrics() {
@@ -12,8 +12,14 @@ export interface AttendedPlatformBootstrapResult {
12
12
  export interface AttendedPlatformBootstrapDependencies {
13
13
  cloudflare?: typeof runAttendedCloudflareBootstrap;
14
14
  fleet?: typeof applyOperatorPlatformFleetBootstrap;
15
+ validateInvite?: typeof validatePlatformInvitation;
15
16
  finalize?: typeof finalizeCloudflareBootstrapAcceptance;
16
17
  }
18
+ export declare function validatePlatformInvitation(args: {
19
+ apiOrigin: string;
20
+ inviteUrl: string;
21
+ fetcher?: typeof fetch;
22
+ }): Promise<void>;
17
23
  /**
18
24
  * The sole attended genesis composition boundary. All non-secret coordinates
19
25
  * are checked before the first Cloudflare or SSH mutation. Secret values exist
package/dist/bootstrap.js CHANGED
@@ -1460,7 +1460,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1460
1460
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1461
1461
 
1462
1462
  // src/version.ts
1463
- var VERSION = "0.1.136";
1463
+ var VERSION = "0.1.138";
1464
1464
 
1465
1465
  // src/software.ts
1466
1466
  var PINNED_BUN_VERSION = "1.3.14";
package/dist/fz-agent.js CHANGED
@@ -9683,7 +9683,7 @@ async function writeAndCloseProcessInput(input, value) {
9683
9683
  }
9684
9684
 
9685
9685
  // src/version.ts
9686
- var VERSION2 = "0.1.136";
9686
+ var VERSION2 = "0.1.138";
9687
9687
 
9688
9688
  // src/ssh-bootstrap.ts
9689
9689
  function deriveMetalAdmissionProvisioning(lscpu) {
@@ -10706,6 +10706,7 @@ var macForAddress = (address) => {
10706
10706
  }
10707
10707
  return `52:54:00:f0:${octets[2].toString(16).padStart(2, "0")}:${octets[3].toString(16).padStart(2, "0")}`;
10708
10708
  };
10709
+ var shouldCloseGuestMapper = (openedHere, active) => openedHere && active;
10709
10710
  function validateMetalProfile(profile) {
10710
10711
  if (!SAFE_NAME.test(profile.volumeGroup))
10711
10712
  throw new MetalProvisionError("invalid volume group");
@@ -11039,6 +11040,7 @@ async function provisionMetalGuest(profile, claim, exec) {
11039
11040
  const credentialPath = `/etc/forgezero/creds/${credentialName}.cred`;
11040
11041
  const keyPath = `/run/forgezero/${credentialName}.key`;
11041
11042
  let imageTarget = lv;
11043
+ let mapperOpenedHere = false;
11042
11044
  if (encrypted) {
11043
11045
  mkdirSync5(dirname5(credentialPath), { recursive: true, mode: 448 });
11044
11046
  mkdirSync5(dirname5(keyPath), { recursive: true, mode: 448 });
@@ -11079,6 +11081,7 @@ async function provisionMetalGuest(profile, claim, exec) {
11079
11081
  }
11080
11082
  if ((await exec(["cryptsetup", "status", mapper])).exitCode !== 0) {
11081
11083
  await checked2(exec, ["cryptsetup", "open", "--type", "luks2", "--key-file", keyPath, lv, mapper]);
11084
+ mapperOpenedHere = true;
11082
11085
  }
11083
11086
  imageTarget = mappedDisk;
11084
11087
  if (manifest.phase === "allocating") {
@@ -11089,7 +11092,7 @@ async function provisionMetalGuest(profile, claim, exec) {
11089
11092
  } finally {
11090
11093
  if (existsSync7(keyPath))
11091
11094
  unlinkSync5(keyPath);
11092
- if ((await exec(["cryptsetup", "status", mapper])).exitCode === 0) {
11095
+ if (shouldCloseGuestMapper(mapperOpenedHere, (await exec(["cryptsetup", "status", mapper])).exitCode === 0)) {
11093
11096
  await checked2(exec, ["cryptsetup", "close", mapper]);
11094
11097
  }
11095
11098
  }
@@ -15429,7 +15432,17 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
15429
15432
  const slots = join10(normalized.root, "slots");
15430
15433
  mkdirSync14(slots, { recursive: true, mode: 493 });
15431
15434
  const slotFile = join10(slots, ".active");
15432
- const previousSlot = existsSync19(slotFile) ? readFileSync14(slotFile, "utf8").trim() : undefined;
15435
+ let previousSlot = existsSync19(slotFile) ? readFileSync14(slotFile, "utf8").trim() : undefined;
15436
+ if (previousSlot !== "blue" && previousSlot !== "green") {
15437
+ previousSlot = undefined;
15438
+ for (const candidate of ["blue", "green"]) {
15439
+ const active = await exec(["/usr/bin/systemctl", "is-active", "--quiet", `forgezero@${candidate}.service`]);
15440
+ if (active.exitCode === 0) {
15441
+ previousSlot = candidate;
15442
+ break;
15443
+ }
15444
+ }
15445
+ }
15433
15446
  const target2 = previousSlot === "blue" ? "green" : "blue";
15434
15447
  const port = target2 === "blue" ? normalized.bluePort : normalized.greenPort;
15435
15448
  const targetLink = join10(slots, target2);
@@ -15462,6 +15475,8 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
15462
15475
  const stopTarget = async () => {
15463
15476
  await exec(["/usr/bin/systemctl", "stop", service]);
15464
15477
  replaceLink(targetLink, previousTarget);
15478
+ if (previousSlot)
15479
+ await exec(["/usr/bin/systemctl", "start", `forgezero@${previousSlot}.service`]);
15465
15480
  };
15466
15481
  if (!healthy) {
15467
15482
  await stopTarget();
package/dist/fz.js CHANGED
@@ -6007,7 +6007,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
6007
6007
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
6008
6008
 
6009
6009
  // src/version.ts
6010
- var VERSION2 = "0.1.136";
6010
+ var VERSION2 = "0.1.138";
6011
6011
 
6012
6012
  // src/software.ts
6013
6013
  var PINNED_BUN_VERSION = "1.3.14";
@@ -18401,6 +18401,35 @@ var invitationFrom = (fleet, master) => {
18401
18401
  } catch {}
18402
18402
  throw new Error("platform bootstrap completed without the initial custodian invitation");
18403
18403
  };
18404
+ async function validatePlatformInvitation(args) {
18405
+ let invite;
18406
+ let api;
18407
+ try {
18408
+ invite = new URL(args.inviteUrl);
18409
+ api = new URL(args.apiOrigin);
18410
+ } catch {
18411
+ throw new Error("platform bootstrap produced malformed public invitation coordinates");
18412
+ }
18413
+ const token = invite.searchParams.get("token") ?? "";
18414
+ if (invite.protocol !== "https:" || api.protocol !== "https:" || !/^plt_[a-f0-9]{48}$/.test(token)) {
18415
+ throw new Error("platform bootstrap produced malformed public invitation coordinates");
18416
+ }
18417
+ const endpoint2 = new URL(`/api/onboarding/invite/${encodeURIComponent(token)}`, api);
18418
+ let response;
18419
+ try {
18420
+ response = await (args.fetcher ?? fetch)(endpoint2, {
18421
+ headers: { accept: "application/json" },
18422
+ cache: "no-store",
18423
+ signal: AbortSignal.timeout(15000)
18424
+ });
18425
+ } catch {
18426
+ throw new Error("platform bootstrap invitation could not be verified through public ingress");
18427
+ }
18428
+ const body = await response.json().catch(() => null);
18429
+ if (!response.ok || body?.ok !== true || body.kind !== "platform-bootstrap" || body.claimable !== true) {
18430
+ throw new Error("platform bootstrap invitation is not claimable; the environment was repaired from existing state instead of clean genesis");
18431
+ }
18432
+ }
18404
18433
  async function applyAttendedPlatformBootstrap(fleetRequest, cloudflareRequest, acceptancePath, secrets, dependencies = {}) {
18405
18434
  if (cloudflareRequest.mode !== "apply")
18406
18435
  throw new Error("attended platform bootstrap requires Cloudflare apply mode");
@@ -18425,6 +18454,10 @@ async function applyAttendedPlatformBootstrap(fleetRequest, cloudflareRequest, a
18425
18454
  if (!fleet.ok)
18426
18455
  return { kind: "forgezero-attended-platform-bootstrap", ok: false, edge, fleet };
18427
18456
  const inviteUrl = invitationFrom(fleet, fleetCoordinates.nodes[0].computeReference);
18457
+ await (dependencies.validateInvite ?? validatePlatformInvitation)({
18458
+ apiOrigin: fleetCoordinates.apiOrigin,
18459
+ inviteUrl
18460
+ });
18428
18461
  const acceptance = await (dependencies.finalize ?? finalizeCloudflareBootstrapAcceptance)({
18429
18462
  checkpointPath: edge.checkpointFile,
18430
18463
  acceptancePath
@@ -366,7 +366,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
366
366
  }
367
367
 
368
368
  // src/version.ts
369
- var VERSION = "0.1.136";
369
+ var VERSION = "0.1.138";
370
370
 
371
371
  // src/otel-collector.ts
372
372
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
@@ -833,6 +833,7 @@ var macForAddress = (address) => {
833
833
  }
834
834
  return `52:54:00:f0:${octets[2].toString(16).padStart(2, "0")}:${octets[3].toString(16).padStart(2, "0")}`;
835
835
  };
836
+ var shouldCloseGuestMapper = (openedHere, active) => openedHere && active;
836
837
  function validateMetalProfile(profile) {
837
838
  if (!SAFE_NAME.test(profile.volumeGroup))
838
839
  throw new MetalProvisionError("invalid volume group");
@@ -1166,6 +1167,7 @@ async function provisionMetalGuest(profile, claim, exec) {
1166
1167
  const credentialPath = `/etc/forgezero/creds/${credentialName}.cred`;
1167
1168
  const keyPath = `/run/forgezero/${credentialName}.key`;
1168
1169
  let imageTarget = lv;
1170
+ let mapperOpenedHere = false;
1169
1171
  if (encrypted) {
1170
1172
  mkdirSync2(dirname2(credentialPath), { recursive: true, mode: 448 });
1171
1173
  mkdirSync2(dirname2(keyPath), { recursive: true, mode: 448 });
@@ -1206,6 +1208,7 @@ async function provisionMetalGuest(profile, claim, exec) {
1206
1208
  }
1207
1209
  if ((await exec(["cryptsetup", "status", mapper])).exitCode !== 0) {
1208
1210
  await checked(exec, ["cryptsetup", "open", "--type", "luks2", "--key-file", keyPath, lv, mapper]);
1211
+ mapperOpenedHere = true;
1209
1212
  }
1210
1213
  imageTarget = mappedDisk;
1211
1214
  if (manifest.phase === "allocating") {
@@ -1216,7 +1219,7 @@ async function provisionMetalGuest(profile, claim, exec) {
1216
1219
  } finally {
1217
1220
  if (existsSync2(keyPath))
1218
1221
  unlinkSync2(keyPath);
1219
- if ((await exec(["cryptsetup", "status", mapper])).exitCode === 0) {
1222
+ if (shouldCloseGuestMapper(mapperOpenedHere, (await exec(["cryptsetup", "status", mapper])).exitCode === 0)) {
1220
1223
  await checked(exec, ["cryptsetup", "close", mapper]);
1221
1224
  }
1222
1225
  }
@@ -72,6 +72,8 @@ export declare const guestNameFor: (computeKey: string) => string;
72
72
  /** Linux interface names are capped at 15 bytes. */
73
73
  export declare const tapNameFor: (computeKey: string) => string;
74
74
  export declare const macForAddress: (address: string) => string;
75
+ /** A reconciler may close only the transient mapper it opened itself. */
76
+ export declare const shouldCloseGuestMapper: (openedHere: boolean, active: boolean) => boolean;
75
77
  export declare function validateMetalProfile(profile: MetalProvisionProfile): void;
76
78
  export declare function allocateAddress(profile: MetalProvisionProfile, computeKey: string, rows: GuestManifest[]): string;
77
79
  /** Stable tight-fit allocation of exact physical cores inside one NUMA-local pool. */
@@ -833,6 +833,7 @@ var macForAddress = (address) => {
833
833
  }
834
834
  return `52:54:00:f0:${octets[2].toString(16).padStart(2, "0")}:${octets[3].toString(16).padStart(2, "0")}`;
835
835
  };
836
+ var shouldCloseGuestMapper = (openedHere, active) => openedHere && active;
836
837
  function validateMetalProfile(profile) {
837
838
  if (!SAFE_NAME.test(profile.volumeGroup))
838
839
  throw new MetalProvisionError("invalid volume group");
@@ -1166,6 +1167,7 @@ async function provisionMetalGuest(profile, claim, exec) {
1166
1167
  const credentialPath = `/etc/forgezero/creds/${credentialName}.cred`;
1167
1168
  const keyPath = `/run/forgezero/${credentialName}.key`;
1168
1169
  let imageTarget = lv;
1170
+ let mapperOpenedHere = false;
1169
1171
  if (encrypted) {
1170
1172
  mkdirSync2(dirname2(credentialPath), { recursive: true, mode: 448 });
1171
1173
  mkdirSync2(dirname2(keyPath), { recursive: true, mode: 448 });
@@ -1206,6 +1208,7 @@ async function provisionMetalGuest(profile, claim, exec) {
1206
1208
  }
1207
1209
  if ((await exec(["cryptsetup", "status", mapper])).exitCode !== 0) {
1208
1210
  await checked(exec, ["cryptsetup", "open", "--type", "luks2", "--key-file", keyPath, lv, mapper]);
1211
+ mapperOpenedHere = true;
1209
1212
  }
1210
1213
  imageTarget = mappedDisk;
1211
1214
  if (manifest.phase === "allocating") {
@@ -1216,7 +1219,7 @@ async function provisionMetalGuest(profile, claim, exec) {
1216
1219
  } finally {
1217
1220
  if (existsSync2(keyPath))
1218
1221
  unlinkSync2(keyPath);
1219
- if ((await exec(["cryptsetup", "status", mapper])).exitCode === 0) {
1222
+ if (shouldCloseGuestMapper(mapperOpenedHere, (await exec(["cryptsetup", "status", mapper])).exitCode === 0)) {
1220
1223
  await checked(exec, ["cryptsetup", "close", mapper]);
1221
1224
  }
1222
1225
  }
@@ -1388,6 +1391,7 @@ async function removeMetalGuest(profile, claim, exec) {
1388
1391
  export {
1389
1392
  validateMetalProfile,
1390
1393
  tapNameFor,
1394
+ shouldCloseGuestMapper,
1391
1395
  removeMetalGuest,
1392
1396
  provisionMetalGuest,
1393
1397
  macForAddress,
@@ -1460,7 +1460,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1460
1460
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1461
1461
 
1462
1462
  // src/version.ts
1463
- var VERSION = "0.1.136";
1463
+ var VERSION = "0.1.138";
1464
1464
 
1465
1465
  // src/software.ts
1466
1466
  var PINNED_BUN_VERSION = "1.3.14";
@@ -1260,7 +1260,17 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
1260
1260
  const slots = join2(normalized.root, "slots");
1261
1261
  mkdirSync2(slots, { recursive: true, mode: 493 });
1262
1262
  const slotFile = join2(slots, ".active");
1263
- const previousSlot = existsSync2(slotFile) ? readFileSync2(slotFile, "utf8").trim() : undefined;
1263
+ let previousSlot = existsSync2(slotFile) ? readFileSync2(slotFile, "utf8").trim() : undefined;
1264
+ if (previousSlot !== "blue" && previousSlot !== "green") {
1265
+ previousSlot = undefined;
1266
+ for (const candidate of ["blue", "green"]) {
1267
+ const active = await exec(["/usr/bin/systemctl", "is-active", "--quiet", `forgezero@${candidate}.service`]);
1268
+ if (active.exitCode === 0) {
1269
+ previousSlot = candidate;
1270
+ break;
1271
+ }
1272
+ }
1273
+ }
1264
1274
  const target = previousSlot === "blue" ? "green" : "blue";
1265
1275
  const port = target === "blue" ? normalized.bluePort : normalized.greenPort;
1266
1276
  const targetLink = join2(slots, target);
@@ -1293,6 +1303,8 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
1293
1303
  const stopTarget = async () => {
1294
1304
  await exec(["/usr/bin/systemctl", "stop", service]);
1295
1305
  replaceLink(targetLink, previousTarget);
1306
+ if (previousSlot)
1307
+ await exec(["/usr/bin/systemctl", "start", `forgezero@${previousSlot}.service`]);
1296
1308
  };
1297
1309
  if (!healthy) {
1298
1310
  await stopTarget();
@@ -3049,7 +3049,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
3049
3049
  }
3050
3050
 
3051
3051
  // src/version.ts
3052
- var VERSION3 = "0.1.136";
3052
+ var VERSION3 = "0.1.138";
3053
3053
 
3054
3054
  // src/egress-policy.ts
3055
3055
  import { realpathSync as realpathSync3 } from "node:fs";
@@ -4776,7 +4776,17 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
4776
4776
  const slots = join5(normalized.root, "slots");
4777
4777
  mkdirSync8(slots, { recursive: true, mode: 493 });
4778
4778
  const slotFile = join5(slots, ".active");
4779
- const previousSlot = existsSync8(slotFile) ? readFileSync6(slotFile, "utf8").trim() : undefined;
4779
+ let previousSlot = existsSync8(slotFile) ? readFileSync6(slotFile, "utf8").trim() : undefined;
4780
+ if (previousSlot !== "blue" && previousSlot !== "green") {
4781
+ previousSlot = undefined;
4782
+ for (const candidate of ["blue", "green"]) {
4783
+ const active = await exec(["/usr/bin/systemctl", "is-active", "--quiet", `forgezero@${candidate}.service`]);
4784
+ if (active.exitCode === 0) {
4785
+ previousSlot = candidate;
4786
+ break;
4787
+ }
4788
+ }
4789
+ }
4780
4790
  const target = previousSlot === "blue" ? "green" : "blue";
4781
4791
  const port = target === "blue" ? normalized.bluePort : normalized.greenPort;
4782
4792
  const targetLink = join5(slots, target);
@@ -4809,6 +4819,8 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
4809
4819
  const stopTarget = async () => {
4810
4820
  await exec(["/usr/bin/systemctl", "stop", service]);
4811
4821
  replaceLink(targetLink, previousTarget);
4822
+ if (previousSlot)
4823
+ await exec(["/usr/bin/systemctl", "start", `forgezero@${previousSlot}.service`]);
4812
4824
  };
4813
4825
  if (!healthy) {
4814
4826
  await stopTarget();
package/dist/provision.js CHANGED
@@ -3049,7 +3049,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
3049
3049
  }
3050
3050
 
3051
3051
  // src/version.ts
3052
- var VERSION3 = "0.1.136";
3052
+ var VERSION3 = "0.1.138";
3053
3053
 
3054
3054
  // src/egress-policy.ts
3055
3055
  import { realpathSync as realpathSync3 } from "node:fs";
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** One package version shared by both public binaries. Pinned to package.json by tests. */
2
- export declare const VERSION = "0.1.136";
2
+ export declare const VERSION = "0.1.138";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/agent",
3
- "version": "0.1.136",
3
+ "version": "0.1.138",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",