@forgezero/agent 0.1.55 → 0.1.56

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.
@@ -749,7 +749,7 @@ async function postSignedNode(options, path, body) {
749
749
  }
750
750
 
751
751
  // src/version.ts
752
- var VERSION3 = "0.1.55";
752
+ var VERSION3 = "0.1.56";
753
753
 
754
754
  // src/agent-heartbeat.ts
755
755
  var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
package/dist/bootstrap.js CHANGED
@@ -1207,7 +1207,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1207
1207
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1208
1208
 
1209
1209
  // src/version.ts
1210
- var VERSION = "0.1.55";
1210
+ var VERSION = "0.1.56";
1211
1211
 
1212
1212
  // src/software.ts
1213
1213
  var PINNED_BUN_VERSION = "1.3.14";
@@ -2,6 +2,7 @@ import { type SoftwareExec } from './software';
2
2
  export declare const COMMUNITY_REHEARSAL_VERSION = "3.11.14";
3
3
  export declare const COMMUNITY_REHEARSAL_ROOT = "/opt/forgezero-rehearsals/community-cluster-api";
4
4
  export declare const COMMUNITY_DATABASE_ROOT = "/var/lib/forgezero-rehearsal-cluster";
5
+ export declare const COMMUNITY_CREDENTIAL_PARENT = "/etc/forgezero-rehearsal";
5
6
  export declare const COMMUNITY_CREDENTIAL_ROOT = "/etc/forgezero-rehearsal/creds";
6
7
  export type CommunityRehearsalApiOperation = 'health' | 'init' | 'write' | 'read' | 'query' | 'cluster';
7
8
  export type CommunityRehearsalHostRequest = {
@@ -13,6 +14,9 @@ export type CommunityRehearsalHostRequest = {
13
14
  archivePath: string;
14
15
  archiveSha256: string;
15
16
  jwt: string;
17
+ } | {
18
+ action: 'cleanup';
19
+ node: string;
16
20
  } | {
17
21
  action: 'database-enable' | 'api-enable' | 'database-ready' | 'starter-ready' | 'api-ready' | 'database-status' | 'api-status';
18
22
  node: string;
@@ -25,7 +29,7 @@ export type CommunityRehearsalHostRequest = {
25
29
  };
26
30
  export type CommunityHostOperation = {
27
31
  kind: 'remove-tree';
28
- path: typeof COMMUNITY_DATABASE_ROOT;
32
+ path: typeof COMMUNITY_DATABASE_ROOT | typeof COMMUNITY_REHEARSAL_ROOT | typeof COMMUNITY_CREDENTIAL_PARENT;
29
33
  } | {
30
34
  kind: 'write';
31
35
  path: string;
@@ -49,5 +53,7 @@ export declare function parseCommunityRehearsalHostRequest(value: unknown): Comm
49
53
  export declare function planCommunityRehearsalPrepare(request: Extract<CommunityRehearsalHostRequest, {
50
54
  action: 'prepare';
51
55
  }>): CommunityHostOperation[];
56
+ /** Remove only the dedicated rehearsal units, credentials, release and data. */
57
+ export declare function planCommunityRehearsalCleanup(): CommunityHostOperation[];
52
58
  /** Execute only the dedicated four-node rehearsal operation selected by a typed request. */
53
59
  export declare function runCommunityRehearsalHost(request: CommunityRehearsalHostRequest): Promise<Record<string, unknown>>;
@@ -285,6 +285,7 @@ import { dirname } from "node:path";
285
285
  var COMMUNITY_REHEARSAL_VERSION = "3.11.14";
286
286
  var COMMUNITY_REHEARSAL_ROOT = "/opt/forgezero-rehearsals/community-cluster-api";
287
287
  var COMMUNITY_DATABASE_ROOT = "/var/lib/forgezero-rehearsal-cluster";
288
+ var COMMUNITY_CREDENTIAL_PARENT = "/etc/forgezero-rehearsal";
288
289
  var COMMUNITY_CREDENTIAL_ROOT = "/etc/forgezero-rehearsal/creds";
289
290
  var NODES = new Map([
290
291
  ["dev-fz-n1", { address: "10.42.0.21", agency: true }],
@@ -326,6 +327,12 @@ function parseCommunityRehearsalHostRequest(value) {
326
327
  }
327
328
  return row;
328
329
  }
330
+ if (action === "cleanup") {
331
+ if (Object.keys(row).some((key) => !["action", "node"].includes(key))) {
332
+ throw new Error("community rehearsal cleanup has unknown fields");
333
+ }
334
+ return { action, node: node.name };
335
+ }
329
336
  if (["database-enable", "api-enable", "database-ready", "starter-ready", "api-ready", "database-status", "api-status"].includes(String(action))) {
330
337
  if (Object.keys(row).some((key) => !["action", "node"].includes(key)))
331
338
  throw new Error("community rehearsal host action has unknown fields");
@@ -441,6 +448,19 @@ function planCommunityRehearsalPrepare(request) {
441
448
  { kind: "unlink", path: request.archivePath }
442
449
  ];
443
450
  }
451
+ function planCommunityRehearsalCleanup() {
452
+ return [
453
+ { kind: "exec", argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-community-api.service"], accepted: [0, 1, 5] },
454
+ { kind: "exec", argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-rehearsal-db.service"], accepted: [0, 1, 5] },
455
+ { kind: "unlink", path: "/etc/systemd/system/forgezero-community-api.service" },
456
+ { kind: "unlink", path: "/etc/systemd/system/forgezero-rehearsal-db.service" },
457
+ { kind: "remove-tree", path: COMMUNITY_DATABASE_ROOT },
458
+ { kind: "remove-tree", path: COMMUNITY_REHEARSAL_ROOT },
459
+ { kind: "remove-tree", path: COMMUNITY_CREDENTIAL_PARENT },
460
+ { kind: "exec", argv: ["/usr/bin/systemctl", "daemon-reload"] },
461
+ { kind: "exec", argv: ["/usr/bin/systemctl", "reset-failed", "forgezero-community-api.service", "forgezero-rehearsal-db.service"], accepted: [0, 1, 5] }
462
+ ];
463
+ }
444
464
  async function exec(argv, stdin) {
445
465
  const child = Bun.spawn([...argv], { stdin: stdin === undefined ? "ignore" : "pipe", stdout: "pipe", stderr: "pipe" });
446
466
  if (stdin !== undefined && child.stdin && typeof child.stdin !== "number") {
@@ -503,6 +523,10 @@ async function runCommunityRehearsalHost(request) {
503
523
  if ((process.getuid?.() ?? -1) !== 0)
504
524
  throw new Error("community-rehearsal must run as root");
505
525
  const node = exactNode(request.node);
526
+ if (request.action === "cleanup") {
527
+ await applyOperations(planCommunityRehearsalCleanup());
528
+ return { ok: true, action: request.action, node: node.name };
529
+ }
506
530
  if (request.action === "prepare") {
507
531
  const metadata = lstatSync(request.archivePath);
508
532
  if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size < 1 || metadata.size > 64 * 1024 * 1024 || createHash2("sha256").update(readFileSync2(request.archivePath)).digest("hex") !== request.archiveSha256) {
@@ -554,10 +578,12 @@ async function runCommunityRehearsalHost(request) {
554
578
  export {
555
579
  runCommunityRehearsalHost,
556
580
  planCommunityRehearsalPrepare,
581
+ planCommunityRehearsalCleanup,
557
582
  parseCommunityRehearsalHostRequest,
558
583
  ensureCommunityRehearsalArango,
559
584
  COMMUNITY_REHEARSAL_VERSION,
560
585
  COMMUNITY_REHEARSAL_ROOT,
561
586
  COMMUNITY_DATABASE_ROOT,
562
- COMMUNITY_CREDENTIAL_ROOT
587
+ COMMUNITY_CREDENTIAL_ROOT,
588
+ COMMUNITY_CREDENTIAL_PARENT
563
589
  };
package/dist/fz-agent.js CHANGED
@@ -8448,7 +8448,7 @@ function assertSupportedGuestImage(imageKey) {
8448
8448
  }
8449
8449
 
8450
8450
  // src/version.ts
8451
- var VERSION2 = "0.1.55";
8451
+ var VERSION2 = "0.1.56";
8452
8452
 
8453
8453
  // src/ssh-bootstrap.ts
8454
8454
  class SshBootstrapError extends Error {
@@ -15003,6 +15003,7 @@ import { dirname as dirname11 } from "path";
15003
15003
  var COMMUNITY_REHEARSAL_VERSION = "3.11.14";
15004
15004
  var COMMUNITY_REHEARSAL_ROOT = "/opt/forgezero-rehearsals/community-cluster-api";
15005
15005
  var COMMUNITY_DATABASE_ROOT = "/var/lib/forgezero-rehearsal-cluster";
15006
+ var COMMUNITY_CREDENTIAL_PARENT = "/etc/forgezero-rehearsal";
15006
15007
  var COMMUNITY_CREDENTIAL_ROOT = "/etc/forgezero-rehearsal/creds";
15007
15008
  var NODES = new Map([
15008
15009
  ["dev-fz-n1", { address: "10.42.0.21", agency: true }],
@@ -15044,6 +15045,12 @@ function parseCommunityRehearsalHostRequest(value) {
15044
15045
  }
15045
15046
  return row2;
15046
15047
  }
15048
+ if (action === "cleanup") {
15049
+ if (Object.keys(row2).some((key) => !["action", "node"].includes(key))) {
15050
+ throw new Error("community rehearsal cleanup has unknown fields");
15051
+ }
15052
+ return { action, node: node.name };
15053
+ }
15047
15054
  if (["database-enable", "api-enable", "database-ready", "starter-ready", "api-ready", "database-status", "api-status"].includes(String(action))) {
15048
15055
  if (Object.keys(row2).some((key) => !["action", "node"].includes(key)))
15049
15056
  throw new Error("community rehearsal host action has unknown fields");
@@ -15159,6 +15166,19 @@ function planCommunityRehearsalPrepare(request) {
15159
15166
  { kind: "unlink", path: request.archivePath }
15160
15167
  ];
15161
15168
  }
15169
+ function planCommunityRehearsalCleanup() {
15170
+ return [
15171
+ { kind: "exec", argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-community-api.service"], accepted: [0, 1, 5] },
15172
+ { kind: "exec", argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-rehearsal-db.service"], accepted: [0, 1, 5] },
15173
+ { kind: "unlink", path: "/etc/systemd/system/forgezero-community-api.service" },
15174
+ { kind: "unlink", path: "/etc/systemd/system/forgezero-rehearsal-db.service" },
15175
+ { kind: "remove-tree", path: COMMUNITY_DATABASE_ROOT },
15176
+ { kind: "remove-tree", path: COMMUNITY_REHEARSAL_ROOT },
15177
+ { kind: "remove-tree", path: COMMUNITY_CREDENTIAL_PARENT },
15178
+ { kind: "exec", argv: ["/usr/bin/systemctl", "daemon-reload"] },
15179
+ { kind: "exec", argv: ["/usr/bin/systemctl", "reset-failed", "forgezero-community-api.service", "forgezero-rehearsal-db.service"], accepted: [0, 1, 5] }
15180
+ ];
15181
+ }
15162
15182
  async function exec(argv2, stdin) {
15163
15183
  const child = Bun.spawn([...argv2], { stdin: stdin === undefined ? "ignore" : "pipe", stdout: "pipe", stderr: "pipe" });
15164
15184
  if (stdin !== undefined && child.stdin && typeof child.stdin !== "number") {
@@ -15221,6 +15241,10 @@ async function runCommunityRehearsalHost(request) {
15221
15241
  if ((process.getuid?.() ?? -1) !== 0)
15222
15242
  throw new Error("community-rehearsal must run as root");
15223
15243
  const node = exactNode(request.node);
15244
+ if (request.action === "cleanup") {
15245
+ await applyOperations(planCommunityRehearsalCleanup());
15246
+ return { ok: true, action: request.action, node: node.name };
15247
+ }
15224
15248
  if (request.action === "prepare") {
15225
15249
  const metadata = lstatSync6(request.archivePath);
15226
15250
  if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size < 1 || metadata.size > 64 * 1024 * 1024 || createHash10("sha256").update(readFileSync17(request.archivePath)).digest("hex") !== request.archiveSha256) {
package/dist/fz.js CHANGED
@@ -4830,7 +4830,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
4830
4830
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
4831
4831
 
4832
4832
  // src/version.ts
4833
- var VERSION2 = "0.1.55";
4833
+ var VERSION2 = "0.1.56";
4834
4834
 
4835
4835
  // src/software.ts
4836
4836
  var PINNED_BUN_VERSION = "1.3.14";
@@ -15413,6 +15413,12 @@ var PLATFORM_GENESIS_FLEETS = {
15413
15413
  ...developmentShape
15414
15414
  }))
15415
15415
  };
15416
+ var DEVELOPMENT_COMMUNITY_REHEARSAL_GUEST = {
15417
+ name: "dev-fz-n4",
15418
+ address: "10.42.0.24",
15419
+ cpuPoolKey: "node7",
15420
+ ...developmentShape
15421
+ };
15416
15422
  var publicKey = (value) => {
15417
15423
  const normalized = value.trim();
15418
15424
  if (!/^ssh-(?:ed25519|rsa) [A-Za-z0-9+/]+={0,3}(?: [^\r\n]+)?$/.test(normalized)) {
@@ -15451,6 +15457,36 @@ function platformGenesisClaims(environment, sshPublicKeys) {
15451
15457
  }
15452
15458
  }));
15453
15459
  }
15460
+ function platformCommunityRehearsalClaims(sshPublicKeys) {
15461
+ const keys = [...new Set(sshPublicKeys.map(publicKey))];
15462
+ if (keys.length === 0)
15463
+ throw new Error("platform genesis requires at least one operator SSH public key");
15464
+ const seed = platformGenesisClaims("development", keys);
15465
+ const guest = DEVELOPMENT_COMMUNITY_REHEARSAL_GUEST;
15466
+ return [...seed, {
15467
+ computeKey: `platform:${guest.name}`,
15468
+ claimToken: "offline-platform-genesis",
15469
+ claimExpiresAtTs: Number.MAX_SAFE_INTEGER,
15470
+ attempt: 1,
15471
+ action: "create",
15472
+ bootstrap: { kind: "platform-genesis" },
15473
+ access: { sshUser: "forgezero", sshPublicKeys: keys },
15474
+ spec: {
15475
+ reference: `platform:${guest.name}`,
15476
+ imageKey: SUPPORTED_GUEST_IMAGE.key,
15477
+ guestName: guest.name,
15478
+ guestAddress: guest.address,
15479
+ cpuPoolKey: guest.cpuPoolKey,
15480
+ physicalCores: guest.physicalCores,
15481
+ vcpu: guest.vcpu,
15482
+ memoryGib: guest.memoryGib,
15483
+ diskGib: guest.diskGib,
15484
+ egressGuaranteedMbps: guest.egressGuaranteedMbps,
15485
+ egressBurstMbps: guest.egressBurstMbps,
15486
+ confidential: true
15487
+ }
15488
+ }];
15489
+ }
15454
15490
 
15455
15491
  // src/operator-bootstrap.ts
15456
15492
  var REQUEST_LIMIT = 64 * 1024;
@@ -15604,13 +15640,17 @@ function readOperatorMetalBootstrapRequest(path) {
15604
15640
  }
15605
15641
  function planOperatorMetalBootstrap(request, mode) {
15606
15642
  const config = readMetalBootstrapConfig(request.metalConfigFile);
15607
- const claims = platformGenesisClaims(request.genesis.environment, request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path)));
15643
+ if (mode === "rehearsal" && request.genesis.environment !== "development") {
15644
+ throw new Error("the disposable four-node Community rehearsal is development-only");
15645
+ }
15646
+ const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
15647
+ const claims = mode === "rehearsal" ? platformCommunityRehearsalClaims(keys) : platformGenesisClaims(request.genesis.environment, keys);
15608
15648
  return {
15609
15649
  kind: "metal-remote",
15610
15650
  mode,
15611
15651
  mutation: mode !== "status",
15612
15652
  target: { address: request.target.address, port: request.target.port, user: request.target.user },
15613
- steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "genesis" ? ["verify pinned SSH transport", "send three fixed platform-genesis claims to the constrained Metal helper"] : [
15653
+ steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "genesis" || mode === "rehearsal" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the fixed three-node seed plus ordinary n4 Community rehearsal claim to the constrained Metal helper" : "send three fixed platform-genesis claims to the constrained Metal helper"] : [
15614
15654
  "verify pinned SSH transport and caller-approved SSH agent",
15615
15655
  "copy pinned Bun and packaged fz artifacts to private staging",
15616
15656
  "stage the strict owner-only metal config and optional seed handoff",
@@ -15905,10 +15945,11 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
15905
15945
  const output2 = await remote(exec, request, knownHosts, ["/usr/bin/sudo", "-n", "/usr/local/bin/fz", "bootstrap", "status"], "remote metal status");
15906
15946
  return { plan, output: output2 };
15907
15947
  }
15908
- if (mode === "genesis") {
15948
+ if (mode === "genesis" || mode === "rehearsal") {
15909
15949
  const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
15910
15950
  const outputs = [];
15911
- for (const claim of platformGenesisClaims(request.genesis.environment, keys)) {
15951
+ const claims = mode === "rehearsal" ? platformCommunityRehearsalClaims(keys) : platformGenesisClaims(request.genesis.environment, keys);
15952
+ for (const claim of claims) {
15912
15953
  outputs.push(await remote(exec, request, knownHosts, ["/usr/bin/sudo", "-n", "/usr/local/bin/fz-agent", "metal-apply", "--claim=-"], `provision ${claim.spec.guestName}`, false, `${JSON.stringify(claim)}
15913
15954
  `));
15914
15955
  }
@@ -17279,8 +17320,8 @@ async function cmdBootstrap(options, args) {
17279
17320
  }
17280
17321
  if (operation === "metal" && args[1] === "remote") {
17281
17322
  const mode = args[2];
17282
- if (!mode || !["apply", "genesis", "status"].includes(mode) || args[3] !== undefined) {
17283
- throw new Error("Usage: fz bootstrap metal remote <apply|genesis|status> --bootstrap-config <owner-only-request.json> [--apply]");
17323
+ if (!mode || !["apply", "genesis", "rehearsal", "status"].includes(mode) || args[3] !== undefined) {
17324
+ throw new Error("Usage: fz bootstrap metal remote <apply|genesis|rehearsal|status> --bootstrap-config <owner-only-request.json> [--apply]");
17284
17325
  }
17285
17326
  if (!options.bootstrapConfigPath)
17286
17327
  throw new Error("remote metal bootstrap requires --bootstrap-config");
@@ -17969,7 +18010,7 @@ function usage() {
17969
18010
  Persist secret-free public-node acceptance, then remove
17970
18011
  the capability-bearing checkpoint and node handoffs
17971
18012
  fz bootstrap metal Plan/install an identity-only physical provisioner
17972
- fz bootstrap metal remote <apply|genesis|status>
18013
+ fz bootstrap metal remote <apply|genesis|rehearsal|status>
17973
18014
  Install/bootstrap one pinned blank metal target, then
17974
18015
  provision the exact selected three-node genesis seed
17975
18016
  fz bootstrap status Verify persisted profile and supervised units
@@ -292,7 +292,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
292
292
  }
293
293
 
294
294
  // src/version.ts
295
- var VERSION = "0.1.55";
295
+ var VERSION = "0.1.56";
296
296
 
297
297
  // src/otel-collector.ts
298
298
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
@@ -25,7 +25,7 @@ export interface OperatorMetalBootstrapRequest {
25
25
  };
26
26
  }
27
27
  export type OperatorPlatformBootstrapMode = 'prepare' | 'apply' | 'status';
28
- export type OperatorMetalBootstrapMode = 'apply' | 'genesis' | 'status';
28
+ export type OperatorMetalBootstrapMode = 'apply' | 'genesis' | 'rehearsal' | 'status';
29
29
  export interface OperatorPlatformBootstrapPlan {
30
30
  kind: 'platform-remote';
31
31
  mode: OperatorPlatformBootstrapMode;
@@ -1207,7 +1207,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1207
1207
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1208
1208
 
1209
1209
  // src/version.ts
1210
- var VERSION = "0.1.55";
1210
+ var VERSION = "0.1.56";
1211
1211
 
1212
1212
  // src/software.ts
1213
1213
  var PINNED_BUN_VERSION = "1.3.14";
@@ -5273,6 +5273,12 @@ var PLATFORM_GENESIS_FLEETS = {
5273
5273
  ...developmentShape
5274
5274
  }))
5275
5275
  };
5276
+ var DEVELOPMENT_COMMUNITY_REHEARSAL_GUEST = {
5277
+ name: "dev-fz-n4",
5278
+ address: "10.42.0.24",
5279
+ cpuPoolKey: "node7",
5280
+ ...developmentShape
5281
+ };
5276
5282
  var publicKey = (value) => {
5277
5283
  const normalized = value.trim();
5278
5284
  if (!/^ssh-(?:ed25519|rsa) [A-Za-z0-9+/]+={0,3}(?: [^\r\n]+)?$/.test(normalized)) {
@@ -5311,6 +5317,36 @@ function platformGenesisClaims(environment, sshPublicKeys) {
5311
5317
  }
5312
5318
  }));
5313
5319
  }
5320
+ function platformCommunityRehearsalClaims(sshPublicKeys) {
5321
+ const keys = [...new Set(sshPublicKeys.map(publicKey))];
5322
+ if (keys.length === 0)
5323
+ throw new Error("platform genesis requires at least one operator SSH public key");
5324
+ const seed = platformGenesisClaims("development", keys);
5325
+ const guest = DEVELOPMENT_COMMUNITY_REHEARSAL_GUEST;
5326
+ return [...seed, {
5327
+ computeKey: `platform:${guest.name}`,
5328
+ claimToken: "offline-platform-genesis",
5329
+ claimExpiresAtTs: Number.MAX_SAFE_INTEGER,
5330
+ attempt: 1,
5331
+ action: "create",
5332
+ bootstrap: { kind: "platform-genesis" },
5333
+ access: { sshUser: "forgezero", sshPublicKeys: keys },
5334
+ spec: {
5335
+ reference: `platform:${guest.name}`,
5336
+ imageKey: SUPPORTED_GUEST_IMAGE.key,
5337
+ guestName: guest.name,
5338
+ guestAddress: guest.address,
5339
+ cpuPoolKey: guest.cpuPoolKey,
5340
+ physicalCores: guest.physicalCores,
5341
+ vcpu: guest.vcpu,
5342
+ memoryGib: guest.memoryGib,
5343
+ diskGib: guest.diskGib,
5344
+ egressGuaranteedMbps: guest.egressGuaranteedMbps,
5345
+ egressBurstMbps: guest.egressBurstMbps,
5346
+ confidential: true
5347
+ }
5348
+ }];
5349
+ }
5314
5350
 
5315
5351
  // src/operator-bootstrap.ts
5316
5352
  var REQUEST_LIMIT = 64 * 1024;
@@ -5464,13 +5500,17 @@ function readOperatorMetalBootstrapRequest(path) {
5464
5500
  }
5465
5501
  function planOperatorMetalBootstrap(request, mode) {
5466
5502
  const config = readMetalBootstrapConfig(request.metalConfigFile);
5467
- const claims = platformGenesisClaims(request.genesis.environment, request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path)));
5503
+ if (mode === "rehearsal" && request.genesis.environment !== "development") {
5504
+ throw new Error("the disposable four-node Community rehearsal is development-only");
5505
+ }
5506
+ const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
5507
+ const claims = mode === "rehearsal" ? platformCommunityRehearsalClaims(keys) : platformGenesisClaims(request.genesis.environment, keys);
5468
5508
  return {
5469
5509
  kind: "metal-remote",
5470
5510
  mode,
5471
5511
  mutation: mode !== "status",
5472
5512
  target: { address: request.target.address, port: request.target.port, user: request.target.user },
5473
- steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "genesis" ? ["verify pinned SSH transport", "send three fixed platform-genesis claims to the constrained Metal helper"] : [
5513
+ steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "genesis" || mode === "rehearsal" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the fixed three-node seed plus ordinary n4 Community rehearsal claim to the constrained Metal helper" : "send three fixed platform-genesis claims to the constrained Metal helper"] : [
5474
5514
  "verify pinned SSH transport and caller-approved SSH agent",
5475
5515
  "copy pinned Bun and packaged fz artifacts to private staging",
5476
5516
  "stage the strict owner-only metal config and optional seed handoff",
@@ -5765,10 +5805,11 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
5765
5805
  const output2 = await remote(exec, request, knownHosts, ["/usr/bin/sudo", "-n", "/usr/local/bin/fz", "bootstrap", "status"], "remote metal status");
5766
5806
  return { plan, output: output2 };
5767
5807
  }
5768
- if (mode === "genesis") {
5808
+ if (mode === "genesis" || mode === "rehearsal") {
5769
5809
  const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
5770
5810
  const outputs = [];
5771
- for (const claim of platformGenesisClaims(request.genesis.environment, keys)) {
5811
+ const claims = mode === "rehearsal" ? platformCommunityRehearsalClaims(keys) : platformGenesisClaims(request.genesis.environment, keys);
5812
+ for (const claim of claims) {
5772
5813
  outputs.push(await remote(exec, request, knownHosts, ["/usr/bin/sudo", "-n", "/usr/local/bin/fz-agent", "metal-apply", "--claim=-"], `provision ${claim.spec.guestName}`, false, `${JSON.stringify(claim)}
5773
5814
  `));
5774
5815
  }
@@ -2086,7 +2086,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2086
2086
  }
2087
2087
 
2088
2088
  // src/version.ts
2089
- var VERSION3 = "0.1.55";
2089
+ var VERSION3 = "0.1.56";
2090
2090
 
2091
2091
  // src/egress-policy.ts
2092
2092
  import { realpathSync as realpathSync3 } from "node:fs";
@@ -14,4 +14,13 @@ export interface PlatformGenesisGuest {
14
14
  }
15
15
  /** Package-owned provisioning coordinates for the exact three-node platform seed. */
16
16
  export declare const PLATFORM_GENESIS_FLEETS: Record<PlatformGenesisEnvironment, readonly PlatformGenesisGuest[]>;
17
+ /**
18
+ * The disposable Community acceptance fleet adds one ordinary post-genesis
19
+ * Coordinator/DBServer host. It is deliberately not part of the three-member
20
+ * platform seed and never acquires Agency authority from this provisioning
21
+ * claim. The rehearsal controller installs that database role later.
22
+ */
23
+ export declare const DEVELOPMENT_COMMUNITY_REHEARSAL_GUEST: PlatformGenesisGuest;
17
24
  export declare function platformGenesisClaims(environment: PlatformGenesisEnvironment, sshPublicKeys: readonly string[]): readonly RemoteProvisionClaim[];
25
+ /** Exact disposable four-host development fleet: three seed guests plus n4. */
26
+ export declare function platformCommunityRehearsalClaims(sshPublicKeys: readonly string[]): readonly RemoteProvisionClaim[];
package/dist/provision.js CHANGED
@@ -2086,7 +2086,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2086
2086
  }
2087
2087
 
2088
2088
  // src/version.ts
2089
- var VERSION3 = "0.1.55";
2089
+ var VERSION3 = "0.1.56";
2090
2090
 
2091
2091
  // src/egress-policy.ts
2092
2092
  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.55";
2
+ export declare const VERSION = "0.1.56";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/agent",
3
- "version": "0.1.55",
3
+ "version": "0.1.56",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",