@forgezero/agent 0.1.115 → 0.1.117

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 CHANGED
@@ -229,8 +229,8 @@ import {
229
229
  ForgeZero,
230
230
  } from '@forgezero/vault';
231
231
 
232
- const fz = new ForgeZero({ project: 'altpilot', environment: 'production' });
233
- await fz.get('STRIPE_KEY');
232
+ const fz = new ForgeZero({ project: 'payments-api', environment: 'production' });
233
+ await fz.get('PAYMENT_PROVIDER');
234
234
  ```
235
235
 
236
236
  ## A local copy, not a cache
@@ -917,7 +917,7 @@ async function postSignedNode(options, path, body) {
917
917
  }
918
918
 
919
919
  // src/version.ts
920
- var VERSION3 = "0.1.115";
920
+ var VERSION3 = "0.1.117";
921
921
 
922
922
  // src/agent-heartbeat.ts
923
923
  function readAgentHostMetrics() {
@@ -89,7 +89,7 @@ export interface EnrolledComputeActivationConfig {
89
89
  profile?: string;
90
90
  deployRoot?: string;
91
91
  software?: SoftwareRequirement[];
92
- /** Compatibility-only per-node SSH deploy key. GitHub App is the scalable default. */
92
+ /** Compatibility-only per-node SSH deploy key for non-provider sources. */
93
93
  gitDeployKey?: boolean;
94
94
  installCloudflared?: boolean;
95
95
  installWarp?: boolean;
@@ -115,7 +115,6 @@ export interface PlatformBootstrapSecrets {
115
115
  backupS3Secret?: string;
116
116
  cloudflareTunnelToken?: string;
117
117
  cloudflareApiToken?: string;
118
- githubOAuthClientSecret: string;
119
118
  }
120
119
  export interface EnrolledComputeBootstrapSecrets {
121
120
  enrolmentToken: string;
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.115";
1463
+ var VERSION = "0.1.117";
1464
1464
 
1465
1465
  // src/software.ts
1466
1466
  var PINNED_BUN_VERSION = "1.3.14";
@@ -3127,6 +3127,9 @@ function platformGenesisClaims(guests, sshPublicKeys) {
3127
3127
  throw new Error("platform genesis requires at least one operator SSH public key");
3128
3128
  return reviewed.map((guest) => claimFor(guest, keys, "create"));
3129
3129
  }
3130
+ function platformGenesisDeletionClaims(guests) {
3131
+ return validatePlatformGenesisGuests(guests).map((guest) => claimFor(guest, [], "delete"));
3132
+ }
3130
3133
  function platformCommunityRehearsalClaims(seedGuests, rehearsalGuest, sshPublicKeys) {
3131
3134
  const reviewed = validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4);
3132
3135
  const keys = [...new Set(sshPublicKeys.map(publicKey))];
@@ -3269,9 +3272,6 @@ function validatePlatformSharedEnvironment(input) {
3269
3272
  } else if (input.email !== undefined) {
3270
3273
  throw new Error("Bootstrap email provider must be smtp or jetemail.");
3271
3274
  }
3272
- if (!input.githubOAuth || !/^(?:Iv1\.[A-Fa-f0-9]{16}|Ov23li[A-Za-z0-9]{14,})$/.test(input.githubOAuth.clientId)) {
3273
- throw new Error("GitHub OAuth client id is malformed.");
3274
- }
3275
3275
  boundedInteger("publicApiPort", input.publicApiPort, 1024, 65533);
3276
3276
  if (!Array.isArray(input.seedSyncMembers) || input.seedSyncMembers.length < 3 || input.seedSyncMembers.length > 64 || new Set(input.seedSyncMembers).size !== input.seedSyncMembers.length) {
3277
3277
  throw new Error("seedSyncMembers must contain 3 to 64 unique physical host identities.");
@@ -3402,7 +3402,6 @@ function renderPlatformSharedEnvironment(input) {
3402
3402
  FZ_REALTIME_WORKER_SCRIPT: value.realtime?.workerScriptName ?? "",
3403
3403
  FZ_REALTIME_ENDPOINT: value.realtime?.endpoint ?? "",
3404
3404
  FZ_REALTIME_PRODUCER: value.realtime?.producer ?? "",
3405
- FZ_GITHUB_OAUTH_CLIENT_ID: value.githubOAuth.clientId,
3406
3405
  FZ_PLATFORM_INITIAL_INVENTORY: value.initialInventory ? JSON.stringify(value.initialInventory) : ""
3407
3406
  };
3408
3407
  return `# Generated by fz bootstrap platform. Non-secret coordinates only.
@@ -3414,7 +3413,6 @@ function platformApiCredentialSpecs(options) {
3414
3413
  const optional = [
3415
3414
  ["fz_smtp.password", options.emailProvider === "smtp"],
3416
3415
  ["fz_jetemail.apiKey", options.emailProvider === "jetemail"],
3417
- ["fz_oauth.github.clientSecret", options.githubOAuth],
3418
3416
  ["CF_API_TOKEN", options.cloudflareKv],
3419
3417
  ["CF_TUNNEL_TOKEN", options.cloudflareKv],
3420
3418
  ["REALTIME_PUBLISH_SECRET", options.realtime],
@@ -3811,15 +3809,7 @@ function validatePlatformBootstrapSecrets(config, input) {
3811
3809
  if (!input || typeof input !== "object" || Array.isArray(input))
3812
3810
  throw new Error("bootstrap credential input must be an object");
3813
3811
  const source = input;
3814
- const allowed = [
3815
- "clusterBootstrapCode",
3816
- "emailSecret",
3817
- "enrolmentToken",
3818
- "backupS3Secret",
3819
- "cloudflareTunnelToken",
3820
- "cloudflareApiToken",
3821
- "githubOAuthClientSecret"
3822
- ];
3812
+ const allowed = ["clusterBootstrapCode", "emailSecret", "enrolmentToken", "backupS3Secret", "cloudflareTunnelToken", "cloudflareApiToken"];
3823
3813
  const unknown = Object.keys(source).filter((key) => !allowed.includes(key));
3824
3814
  if (unknown.length)
3825
3815
  throw new Error(`bootstrap credential input contains unsupported field ${unknown[0]}`);
@@ -3829,7 +3819,6 @@ function validatePlatformBootstrapSecrets(config, input) {
3829
3819
  const backupS3Secret = typeof source.backupS3Secret === "string" ? source.backupS3Secret.trim() : undefined;
3830
3820
  const cloudflareTunnelToken = typeof source.cloudflareTunnelToken === "string" ? source.cloudflareTunnelToken.trim() : undefined;
3831
3821
  const cloudflareApiToken = typeof source.cloudflareApiToken === "string" ? source.cloudflareApiToken.trim() : undefined;
3832
- const githubOAuthClientSecret = typeof source.githubOAuthClientSecret === "string" ? source.githubOAuthClientSecret.trim() : "";
3833
3822
  if (!/^[a-f0-9]{64}$/i.test(clusterBootstrapCode))
3834
3823
  throw new Error("cluster bootstrap code must contain exactly 64 hexadecimal characters");
3835
3824
  if (!emailSecret || emailSecret.length > 16384 || /[\r\n\0]/.test(emailSecret))
@@ -3849,16 +3838,12 @@ function validatePlatformBootstrapSecrets(config, input) {
3849
3838
  }
3850
3839
  if (cloudflareTunnelToken)
3851
3840
  validateCloudflareBootstrapSecretPair({ cloudflareTunnelToken, cloudflareApiToken });
3852
- if (githubOAuthClientSecret.length < 20 || githubOAuthClientSecret.length > 512 || /[\r\n\0]/.test(githubOAuthClientSecret)) {
3853
- throw new Error("GitHub OAuth client secret is malformed");
3854
- }
3855
3841
  return {
3856
3842
  clusterBootstrapCode,
3857
3843
  emailSecret,
3858
3844
  ...enrolmentToken ? { enrolmentToken } : {},
3859
3845
  ...backupS3Secret ? { backupS3Secret } : {},
3860
- ...cloudflareTunnelToken ? { cloudflareTunnelToken, cloudflareApiToken } : {},
3861
- githubOAuthClientSecret
3846
+ ...cloudflareTunnelToken ? { cloudflareTunnelToken, cloudflareApiToken } : {}
3862
3847
  };
3863
3848
  }
3864
3849
  var platformBootstrapRunner = (config) => config.kind === "platform" && config.database.role === "master";
@@ -4781,8 +4766,7 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
4781
4766
  enrolmentToken: checked4.enrolmentToken,
4782
4767
  backup: checked4.backupS3Secret,
4783
4768
  cloudflareTunnelToken: checked4.cloudflareTunnelToken,
4784
- cloudflareApiToken: checked4.cloudflareApiToken,
4785
- githubOAuthClientSecret: checked4.githubOAuthClientSecret
4769
+ cloudflareApiToken: checked4.cloudflareApiToken
4786
4770
  };
4787
4771
  })() : undefined;
4788
4772
  const enrolledPrivate = config.kind === "enrolled-compute" ? validateEnrolledComputeBootstrapSecrets(config, secrets) : undefined;
@@ -4883,7 +4867,6 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
4883
4867
  const emailCredentialName = config.runtime.environment.email?.provider === "smtp" ? "fz_smtp.password" : config.runtime.environment.email?.provider === "jetemail" ? "fz_jetemail.apiKey" : undefined;
4884
4868
  for (const [name, source] of Object.entries({
4885
4869
  ...emailCredentialName ? { [emailCredentialName]: platformPrivate.email } : {},
4886
- "fz_oauth.github.clientSecret": platformPrivate.githubOAuthClientSecret,
4887
4870
  "backup.s3.secretAccessKey": platformPrivate.backup
4888
4871
  })) {
4889
4872
  if (source) {
@@ -4900,7 +4883,6 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
4900
4883
  const credentials = platformApiCredentialSpecs({
4901
4884
  emailProvider: runtime.environment.email?.provider,
4902
4885
  cloudflareKv: cloudflareConfigured,
4903
- githubOAuth: true,
4904
4886
  realtime: Boolean(runtime.environment.realtime)
4905
4887
  });
4906
4888
  const units = renderPlatformApiUnits({
@@ -5148,7 +5130,6 @@ function strictBootstrapDocument(value) {
5148
5130
  "agentOtlpEndpoint",
5149
5131
  "custodianEmail",
5150
5132
  "email",
5151
- "githubOAuth",
5152
5133
  "deployProfile",
5153
5134
  "otlpFlushIntervalMs",
5154
5135
  "otlpTraceSampleRatio",
@@ -5159,7 +5140,6 @@ function strictBootstrapDocument(value) {
5159
5140
  "databaseReadPreferredCoordinators"
5160
5141
  ], "runtime environment");
5161
5142
  const environment = runtime.environment;
5162
- exactKeys(environment.githubOAuth, ["clientId"], "GitHub OAuth config");
5163
5143
  if (environment.initialInventory !== undefined) {
5164
5144
  const inventory = exactKeys(environment.initialInventory, ["metalHostname", "metalIdentity", "region", "computes", "attestation", "deployment"], "initial inventory");
5165
5145
  if (inventory.metalIdentity !== undefined) {
package/dist/fz-agent.js CHANGED
@@ -9601,7 +9601,7 @@ async function writeAndCloseProcessInput(input, value) {
9601
9601
  }
9602
9602
 
9603
9603
  // src/version.ts
9604
- var VERSION2 = "0.1.115";
9604
+ var VERSION2 = "0.1.117";
9605
9605
 
9606
9606
  // src/ssh-bootstrap.ts
9607
9607
  class SshBootstrapError extends Error {
package/dist/fz.js CHANGED
@@ -4634,7 +4634,7 @@ var VaultError, runtimeEnvironment = () => typeof process !== "undefined" && pro
4634
4634
  } catch {
4635
4635
  return;
4636
4636
  }
4637
- }, VERSION = "0.1.20";
4637
+ }, VERSION = "0.1.21";
4638
4638
  var init_dist = __esm(() => {
4639
4639
  init_identity();
4640
4640
  VaultError = class VaultError extends Error {
@@ -4841,7 +4841,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
4841
4841
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
4842
4842
 
4843
4843
  // src/version.ts
4844
- var VERSION2 = "0.1.115";
4844
+ var VERSION2 = "0.1.117";
4845
4845
 
4846
4846
  // src/software.ts
4847
4847
  var PINNED_BUN_VERSION = "1.3.14";
@@ -12238,6 +12238,9 @@ function platformGenesisClaims(guests, sshPublicKeys) {
12238
12238
  throw new Error("platform genesis requires at least one operator SSH public key");
12239
12239
  return reviewed.map((guest) => claimFor(guest, keys, "create"));
12240
12240
  }
12241
+ function platformGenesisDeletionClaims(guests) {
12242
+ return validatePlatformGenesisGuests(guests).map((guest) => claimFor(guest, [], "delete"));
12243
+ }
12241
12244
  function platformCommunityRehearsalClaims(seedGuests, rehearsalGuest, sshPublicKeys) {
12242
12245
  const reviewed = validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4);
12243
12246
  const keys = [...new Set(sshPublicKeys.map(publicKey))];
@@ -12380,9 +12383,6 @@ function validatePlatformSharedEnvironment(input) {
12380
12383
  } else if (input.email !== undefined) {
12381
12384
  throw new Error("Bootstrap email provider must be smtp or jetemail.");
12382
12385
  }
12383
- if (!input.githubOAuth || !/^(?:Iv1\.[A-Fa-f0-9]{16}|Ov23li[A-Za-z0-9]{14,})$/.test(input.githubOAuth.clientId)) {
12384
- throw new Error("GitHub OAuth client id is malformed.");
12385
- }
12386
12386
  boundedInteger("publicApiPort", input.publicApiPort, 1024, 65533);
12387
12387
  if (!Array.isArray(input.seedSyncMembers) || input.seedSyncMembers.length < 3 || input.seedSyncMembers.length > 64 || new Set(input.seedSyncMembers).size !== input.seedSyncMembers.length) {
12388
12388
  throw new Error("seedSyncMembers must contain 3 to 64 unique physical host identities.");
@@ -12513,7 +12513,6 @@ function renderPlatformSharedEnvironment(input) {
12513
12513
  FZ_REALTIME_WORKER_SCRIPT: value.realtime?.workerScriptName ?? "",
12514
12514
  FZ_REALTIME_ENDPOINT: value.realtime?.endpoint ?? "",
12515
12515
  FZ_REALTIME_PRODUCER: value.realtime?.producer ?? "",
12516
- FZ_GITHUB_OAUTH_CLIENT_ID: value.githubOAuth.clientId,
12517
12516
  FZ_PLATFORM_INITIAL_INVENTORY: value.initialInventory ? JSON.stringify(value.initialInventory) : ""
12518
12517
  };
12519
12518
  return `# Generated by fz bootstrap platform. Non-secret coordinates only.
@@ -12525,7 +12524,6 @@ function platformApiCredentialSpecs(options) {
12525
12524
  const optional = [
12526
12525
  ["fz_smtp.password", options.emailProvider === "smtp"],
12527
12526
  ["fz_jetemail.apiKey", options.emailProvider === "jetemail"],
12528
- ["fz_oauth.github.clientSecret", options.githubOAuth],
12529
12527
  ["CF_API_TOKEN", options.cloudflareKv],
12530
12528
  ["CF_TUNNEL_TOKEN", options.cloudflareKv],
12531
12529
  ["REALTIME_PUBLISH_SECRET", options.realtime],
@@ -14198,15 +14196,7 @@ function validatePlatformBootstrapSecrets(config, input) {
14198
14196
  if (!input || typeof input !== "object" || Array.isArray(input))
14199
14197
  throw new Error("bootstrap credential input must be an object");
14200
14198
  const source = input;
14201
- const allowed = [
14202
- "clusterBootstrapCode",
14203
- "emailSecret",
14204
- "enrolmentToken",
14205
- "backupS3Secret",
14206
- "cloudflareTunnelToken",
14207
- "cloudflareApiToken",
14208
- "githubOAuthClientSecret"
14209
- ];
14199
+ const allowed = ["clusterBootstrapCode", "emailSecret", "enrolmentToken", "backupS3Secret", "cloudflareTunnelToken", "cloudflareApiToken"];
14210
14200
  const unknown = Object.keys(source).filter((key) => !allowed.includes(key));
14211
14201
  if (unknown.length)
14212
14202
  throw new Error(`bootstrap credential input contains unsupported field ${unknown[0]}`);
@@ -14216,7 +14206,6 @@ function validatePlatformBootstrapSecrets(config, input) {
14216
14206
  const backupS3Secret = typeof source.backupS3Secret === "string" ? source.backupS3Secret.trim() : undefined;
14217
14207
  const cloudflareTunnelToken = typeof source.cloudflareTunnelToken === "string" ? source.cloudflareTunnelToken.trim() : undefined;
14218
14208
  const cloudflareApiToken = typeof source.cloudflareApiToken === "string" ? source.cloudflareApiToken.trim() : undefined;
14219
- const githubOAuthClientSecret = typeof source.githubOAuthClientSecret === "string" ? source.githubOAuthClientSecret.trim() : "";
14220
14209
  if (!/^[a-f0-9]{64}$/i.test(clusterBootstrapCode))
14221
14210
  throw new Error("cluster bootstrap code must contain exactly 64 hexadecimal characters");
14222
14211
  if (!emailSecret || emailSecret.length > 16384 || /[\r\n\0]/.test(emailSecret))
@@ -14236,16 +14225,12 @@ function validatePlatformBootstrapSecrets(config, input) {
14236
14225
  }
14237
14226
  if (cloudflareTunnelToken)
14238
14227
  validateCloudflareBootstrapSecretPair({ cloudflareTunnelToken, cloudflareApiToken });
14239
- if (githubOAuthClientSecret.length < 20 || githubOAuthClientSecret.length > 512 || /[\r\n\0]/.test(githubOAuthClientSecret)) {
14240
- throw new Error("GitHub OAuth client secret is malformed");
14241
- }
14242
14228
  return {
14243
14229
  clusterBootstrapCode,
14244
14230
  emailSecret,
14245
14231
  ...enrolmentToken ? { enrolmentToken } : {},
14246
14232
  ...backupS3Secret ? { backupS3Secret } : {},
14247
- ...cloudflareTunnelToken ? { cloudflareTunnelToken, cloudflareApiToken } : {},
14248
- githubOAuthClientSecret
14233
+ ...cloudflareTunnelToken ? { cloudflareTunnelToken, cloudflareApiToken } : {}
14249
14234
  };
14250
14235
  }
14251
14236
  var platformBootstrapRunner = (config) => config.kind === "platform" && config.database.role === "master";
@@ -15168,8 +15153,7 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
15168
15153
  enrolmentToken: checked4.enrolmentToken,
15169
15154
  backup: checked4.backupS3Secret,
15170
15155
  cloudflareTunnelToken: checked4.cloudflareTunnelToken,
15171
- cloudflareApiToken: checked4.cloudflareApiToken,
15172
- githubOAuthClientSecret: checked4.githubOAuthClientSecret
15156
+ cloudflareApiToken: checked4.cloudflareApiToken
15173
15157
  };
15174
15158
  })() : undefined;
15175
15159
  const enrolledPrivate = config.kind === "enrolled-compute" ? validateEnrolledComputeBootstrapSecrets(config, secrets) : undefined;
@@ -15270,7 +15254,6 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
15270
15254
  const emailCredentialName = config.runtime.environment.email?.provider === "smtp" ? "fz_smtp.password" : config.runtime.environment.email?.provider === "jetemail" ? "fz_jetemail.apiKey" : undefined;
15271
15255
  for (const [name, source] of Object.entries({
15272
15256
  ...emailCredentialName ? { [emailCredentialName]: platformPrivate.email } : {},
15273
- "fz_oauth.github.clientSecret": platformPrivate.githubOAuthClientSecret,
15274
15257
  "backup.s3.secretAccessKey": platformPrivate.backup
15275
15258
  })) {
15276
15259
  if (source) {
@@ -15287,7 +15270,6 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
15287
15270
  const credentials = platformApiCredentialSpecs({
15288
15271
  emailProvider: runtime.environment.email?.provider,
15289
15272
  cloudflareKv: cloudflareConfigured,
15290
- githubOAuth: true,
15291
15273
  realtime: Boolean(runtime.environment.realtime)
15292
15274
  });
15293
15275
  const units = renderPlatformApiUnits({
@@ -15535,7 +15517,6 @@ function strictBootstrapDocument(value) {
15535
15517
  "agentOtlpEndpoint",
15536
15518
  "custodianEmail",
15537
15519
  "email",
15538
- "githubOAuth",
15539
15520
  "deployProfile",
15540
15521
  "otlpFlushIntervalMs",
15541
15522
  "otlpTraceSampleRatio",
@@ -15546,7 +15527,6 @@ function strictBootstrapDocument(value) {
15546
15527
  "databaseReadPreferredCoordinators"
15547
15528
  ], "runtime environment");
15548
15529
  const environment = runtime.environment;
15549
- exactKeys2(environment.githubOAuth, ["clientId"], "GitHub OAuth config");
15550
15530
  if (environment.initialInventory !== undefined) {
15551
15531
  const inventory = exactKeys2(environment.initialInventory, ["metalHostname", "metalIdentity", "region", "computes", "attestation", "deployment"], "initial inventory");
15552
15532
  if (inventory.metalIdentity !== undefined) {
@@ -17363,14 +17343,14 @@ function planOperatorMetalBootstrap(request, mode) {
17363
17343
  if ((mode === "rehearsal" || mode === "rehearsal-cleanup") && !request.genesis.rehearsalNode) {
17364
17344
  throw new Error("the reviewed metal request has no rehearsal node");
17365
17345
  }
17366
- const claims = mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
17346
+ const claims = mode === "genesis-cleanup" ? platformGenesisDeletionClaims(request.genesis.nodes) : mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
17367
17347
  const genesisNodes = mode === "host-keys" ? request.genesis.nodes.map(({ name }) => name) : mode === "rehearsal-host-keys" ? guestRows(request).map(({ name }) => name) : claims.map((claim) => claim.spec.guestName).filter((name) => Boolean(name));
17368
17348
  return {
17369
17349
  kind: "metal-remote",
17370
17350
  mode,
17371
17351
  mutation: !["status", "host-keys", "rehearsal-host-keys"].includes(mode),
17372
17352
  target: { address: request.target.address, port: request.target.port, user: request.target.user },
17373
- steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "host-keys" || mode === "rehearsal-host-keys" ? ["verify pinned SSH transport", "collect exact Ed25519 host keys for the reviewed guest addresses"] : mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the three reviewed seed claims plus the reviewed fourth Community rehearsal claim to the constrained Metal helper" : mode === "rehearsal-cleanup" ? "delete the exact four reviewed disposable guests and their owned disks, units, seed data and inventory" : "send the three reviewed platform-genesis claims to the constrained Metal helper"] : [
17353
+ steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "host-keys" || mode === "rehearsal-host-keys" ? ["verify pinned SSH transport", "collect exact Ed25519 host keys for the reviewed guest addresses"] : mode === "genesis" || mode === "genesis-cleanup" || mode === "rehearsal" || mode === "rehearsal-cleanup" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the three reviewed seed claims plus the reviewed fourth Community rehearsal claim to the constrained Metal helper" : mode === "genesis-cleanup" ? "delete the exact three reviewed platform guests and their owned disks, units, seed data and inventory" : mode === "rehearsal-cleanup" ? "delete the exact four reviewed disposable guests and their owned disks, units, seed data and inventory" : "send the three reviewed platform-genesis claims to the constrained Metal helper"] : [
17374
17354
  "verify pinned SSH transport and caller-approved SSH agent",
17375
17355
  "copy pinned Bun and packaged fz artifacts to private staging",
17376
17356
  "stage the strict owner-only metal config and optional seed handoff",
@@ -17968,13 +17948,13 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
17968
17948
  output: JSON.stringify(await collectOperatorGuestHostKeys(request, { ...options, exec }, mode === "rehearsal-host-keys"))
17969
17949
  };
17970
17950
  }
17971
- if (mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup") {
17951
+ if (mode === "genesis" || mode === "genesis-cleanup" || mode === "rehearsal" || mode === "rehearsal-cleanup") {
17972
17952
  const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
17973
17953
  if ((mode === "rehearsal" || mode === "rehearsal-cleanup") && !request.genesis.rehearsalNode) {
17974
17954
  throw new Error("the reviewed metal request has no rehearsal node");
17975
17955
  }
17976
17956
  const outputs = [];
17977
- const claims = mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
17957
+ const claims = mode === "genesis-cleanup" ? platformGenesisDeletionClaims(request.genesis.nodes) : mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
17978
17958
  for (const claim of claims) {
17979
17959
  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)}
17980
17960
  `));
@@ -18260,7 +18240,6 @@ function forgeZeroLaunchTemplate(profile, guests, bundle, owner) {
18260
18240
  otlpTraceSampleRatio: 0.1,
18261
18241
  custodianEmail: owner.custodianEmail,
18262
18242
  email: owner.email,
18263
- githubOAuth: owner.githubOAuth,
18264
18243
  deployProfile: profile.environment
18265
18244
  },
18266
18245
  serviceUser: "forgezero-api",
@@ -18648,18 +18627,15 @@ function readPlatformLaunchEnvironment(path) {
18648
18627
  throw new Error(`bootstrap secret environment is missing ${name}`);
18649
18628
  return value;
18650
18629
  };
18651
- const githubOAuth = { clientId: requiredValue("FZ_GITHUB_OAUTH_CLIENT_ID") };
18652
18630
  const owner = {
18653
18631
  custodianEmail: "info@axxra.in",
18654
- email: { provider: "jetemail", from: "noreply@forgezero.net", eu: false },
18655
- githubOAuth
18632
+ email: { provider: "jetemail", from: "noreply@forgezero.net", eu: false }
18656
18633
  };
18657
18634
  return {
18658
18635
  owner,
18659
18636
  emailSecret: requiredValue("FZ_JETEMAIL_API_KEY"),
18660
18637
  cloudflareTunnelToken: requiredValue("CF_TUNNEL_TOKEN"),
18661
- cloudflareApiToken: requiredValue("CF_API_TOKEN"),
18662
- githubOAuthClientSecret: requiredValue("FZ_GITHUB_OAUTH_CLIENT_SECRET")
18638
+ cloudflareApiToken: requiredValue("CF_API_TOKEN")
18663
18639
  };
18664
18640
  }
18665
18641
 
@@ -18878,7 +18854,9 @@ function parseOptions(argv2) {
18878
18854
  options.bootstrapConfigPath = argv2[++index];
18879
18855
  else if (token === "--secrets-env-file")
18880
18856
  options.bootstrapSecretsEnvFile = argv2[++index];
18881
- else if (token === "--output")
18857
+ else if (token.startsWith("--confirm-disposable-fleet=")) {
18858
+ options.disposableFleetConfirmation = token.slice("--confirm-disposable-fleet=".length);
18859
+ } else if (token === "--output")
18882
18860
  options.outputPath = argv2[++index];
18883
18861
  else if (token === "--query")
18884
18862
  options.queries.push(argv2[++index] ?? "");
@@ -19483,8 +19461,7 @@ async function promptPlatformBootstrapSecrets(config, generatedClusterBootstrapC
19483
19461
  ...config.cloudflareHandoff || config.runtime.environment.cloudflare ? {
19484
19462
  cloudflareTunnelToken: await bootstrapSecret("CF_TUNNEL_TOKEN"),
19485
19463
  cloudflareApiToken: await bootstrapSecret("CF_API_TOKEN")
19486
- } : {},
19487
- githubOAuthClientSecret: await bootstrapSecret("GitHub OAuth client secret")
19464
+ } : {}
19488
19465
  });
19489
19466
  }
19490
19467
  var bootstrapList = (question, fallback) => bootstrapAnswer(question, fallback).split(",").map((value) => value.trim()).filter(Boolean);
@@ -19782,11 +19759,9 @@ function selectedForgeZeroLaunchEnvironment(value) {
19782
19759
  return value;
19783
19760
  }
19784
19761
  function forgeZeroLaunchOwnerInput() {
19785
- const githubOAuth = { clientId: bootstrapAnswer("GitHub OAuth client ID") };
19786
19762
  return {
19787
19763
  custodianEmail: "info@axxra.in",
19788
- email: { provider: "jetemail", from: "noreply@forgezero.net", eu: false },
19789
- githubOAuth
19764
+ email: { provider: "jetemail", from: "noreply@forgezero.net", eu: false }
19790
19765
  };
19791
19766
  }
19792
19767
  async function prepareForgeZeroPlatformLaunch(environment, outputDirectory, projectRoot, owner) {
@@ -19855,7 +19830,6 @@ function interactiveBootstrap(kind, genesisGuests, genesisCloudflareCheckpoint)
19855
19830
  const backupRegion = backupEndpoint ? bootstrapAnswer("Backup S3 region") : undefined;
19856
19831
  const backupBucket = backupEndpoint ? bootstrapAnswer("Backup S3 bucket") : undefined;
19857
19832
  const backupAccessKeyId = backupEndpoint ? bootstrapAnswer("Backup S3 access-key id") : undefined;
19858
- const githubOAuthClientId = bootstrapAnswer("GitHub OAuth client ID");
19859
19833
  const cloudflareHandoffFile = genesis ? cloudflareHostHandoffPath(genesisCloudflareCheckpoint, computeReference) : bootstrapAnswer("Node-specific Cloudflare host handoff (blank for no public edge)", "");
19860
19834
  const cloudflareNodeName = cloudflareHandoffFile ? genesis ? computeReference : bootstrapAnswer("Cloudflare handoff node name", computeReference) : undefined;
19861
19835
  const databaseNetworkMode = "private-lan";
@@ -19910,7 +19884,6 @@ function interactiveBootstrap(kind, genesisGuests, genesisCloudflareCheckpoint)
19910
19884
  otlpTraceSampleRatio: 0.1,
19911
19885
  custodianEmail: role === "master" ? bootstrapAnswer("First custodian email") : undefined,
19912
19886
  email: emailProvider === "smtp" ? { provider: "smtp", host: smtpHost, port: smtpPort, user: smtpUser, from: emailFrom } : { provider: "jetemail", from: emailFrom, eu: jetemailEu },
19913
- githubOAuth: { clientId: githubOAuthClientId },
19914
19887
  ...backupEndpoint && backupRegion && backupBucket && backupAccessKeyId ? {
19915
19888
  backup: { endpoint: backupEndpoint, region: backupRegion, bucket: backupBucket, accessKeyId: backupAccessKeyId }
19916
19889
  } : {},
@@ -19970,8 +19943,7 @@ async function runAttendedPlatformFleet(fleet, apply, generatedClusterBootstrapC
19970
19943
  clusterBootstrapCode: generatedClusterBootstrapCode,
19971
19944
  emailSecret: environmentInput.emailSecret,
19972
19945
  cloudflareTunnelToken: environmentInput.cloudflareTunnelToken,
19973
- cloudflareApiToken: environmentInput.cloudflareApiToken,
19974
- githubOAuthClientSecret: environmentInput.githubOAuthClientSecret
19946
+ cloudflareApiToken: environmentInput.cloudflareApiToken
19975
19947
  }) : await promptPlatformBootstrapSecrets(firstConfig, generatedClusterBootstrapCode);
19976
19948
  const cloudflare = await resolveCloudflareBootstrapCommandConfig(fleet.cloudflareConfigFile, {
19977
19949
  tunnelToken: secrets.cloudflareTunnelToken,
@@ -20075,8 +20047,8 @@ async function cmdBootstrap(options, args) {
20075
20047
  }
20076
20048
  if (operation === "metal" && args[1] === "remote") {
20077
20049
  const mode = args[2];
20078
- if (!mode || !["apply", "genesis", "rehearsal", "rehearsal-cleanup", "host-keys", "rehearsal-host-keys", "status"].includes(mode) || args[3] !== undefined) {
20079
- throw new Error("Usage: fz bootstrap metal remote <apply|genesis|rehearsal|rehearsal-cleanup|host-keys|rehearsal-host-keys|status> --bootstrap-config <owner-only-request.json> [--output <owner-only-evidence.json>] [--apply]");
20050
+ if (!mode || !["apply", "genesis", "genesis-cleanup", "rehearsal", "rehearsal-cleanup", "host-keys", "rehearsal-host-keys", "status"].includes(mode) || args[3] !== undefined) {
20051
+ throw new Error("Usage: fz bootstrap metal remote <apply|genesis|genesis-cleanup|rehearsal|rehearsal-cleanup|host-keys|rehearsal-host-keys|status> --bootstrap-config <owner-only-request.json> [--output <owner-only-evidence.json>] [--confirm-disposable-fleet=<exact-names>] [--apply]");
20080
20052
  }
20081
20053
  if (!options.bootstrapConfigPath)
20082
20054
  throw new Error("remote metal bootstrap requires --bootstrap-config");
@@ -20087,6 +20059,17 @@ async function cmdBootstrap(options, args) {
20087
20059
  validateMetalConfig: mode === "apply"
20088
20060
  });
20089
20061
  const plan2 = planOperatorMetalBootstrap(request, mode);
20062
+ if (mode === "genesis-cleanup") {
20063
+ const expected = plan2.genesisNodes.join(",");
20064
+ if (!options.apply) {
20065
+ out.line(JSON.stringify({ ...plan2, requiredConfirmation: expected }, null, 2));
20066
+ out.step(`Repeat with --confirm-disposable-fleet=${expected} --apply to delete only this reviewed fleet.`);
20067
+ return 0;
20068
+ }
20069
+ if (options.disposableFleetConfirmation !== expected) {
20070
+ throw new Error(`genesis cleanup requires --confirm-disposable-fleet=${expected}`);
20071
+ }
20072
+ }
20090
20073
  if (!options.apply) {
20091
20074
  out.line(JSON.stringify(plan2, null, 2));
20092
20075
  out.step("Review the pinned metal target and exact genesis nodes, then repeat with --apply to contact the host.");
@@ -20188,7 +20171,7 @@ async function cmdBootstrap(options, args) {
20188
20171
  return 0;
20189
20172
  }
20190
20173
  if (!["platform", "repair"].includes(operation)) {
20191
- throw new Error("Usage: fz bootstrap config <metal|platform|cloudflare>|platform [launch|bundle|fleet <apply|status>|remote <apply|status>|cloudflare [verify|finalize]]|metal [remote <apply|genesis|rehearsal|rehearsal-cleanup|host-keys|rehearsal-host-keys|status>]|status|repair [--bootstrap-config <path>] [--apply]");
20174
+ throw new Error("Usage: fz bootstrap config <metal|platform|cloudflare>|platform [launch|bundle|fleet <apply|status>|remote <apply|status>|cloudflare [verify|finalize]]|metal [remote <apply|genesis|genesis-cleanup|rehearsal|rehearsal-cleanup|host-keys|rehearsal-host-keys|status>]|status|repair [--bootstrap-config <path>] [--apply]");
20192
20175
  }
20193
20176
  const config = options.bootstrapConfigPath ? readBootstrapConfig(options.bootstrapConfigPath) : operation === "repair" ? (() => {
20194
20177
  throw new Error("repair requires --bootstrap-config so immutable coordinates are revalidated");
@@ -20858,7 +20841,7 @@ function usage() {
20858
20841
  Persist secret-free public-node acceptance, then remove
20859
20842
  the completed non-secret checkpoint and node handoffs
20860
20843
  fz bootstrap metal Plan/install an identity-only physical provisioner
20861
- fz bootstrap metal remote <apply|genesis|rehearsal|rehearsal-cleanup|host-keys|rehearsal-host-keys|status>
20844
+ fz bootstrap metal remote <apply|genesis|genesis-cleanup|rehearsal|rehearsal-cleanup|host-keys|rehearsal-host-keys|status>
20862
20845
  Install/bootstrap one pinned blank metal target, then
20863
20846
  provision the exact selected three-node genesis seed
20864
20847
  fz bootstrap status Verify persisted profile and supervised units
@@ -366,7 +366,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
366
366
  }
367
367
 
368
368
  // src/version.ts
369
- var VERSION = "0.1.115";
369
+ var VERSION = "0.1.117";
370
370
 
371
371
  // src/otel-collector.ts
372
372
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
@@ -8,7 +8,7 @@ export interface MetalCpuPool {
8
8
  key: string;
9
9
  /** Linux CPU-list syntax, including every SMT sibling in this pool. */
10
10
  cpus: string;
11
- /** Real cores represented by cpus; checked against the commercial promise. */
11
+ /** Real cores represented by cpus; checked against the sellable capacity promise. */
12
12
  physicalCores: number;
13
13
  /** Linux NUMA node-list syntax. Optional on a non-NUMA machine. */
14
14
  memoryNodes?: string;
@@ -90,7 +90,7 @@ export interface OperatorPlatformFleetCoordinates {
90
90
  }[];
91
91
  }
92
92
  export type OperatorPlatformBootstrapMode = 'apply' | 'status';
93
- export type OperatorMetalBootstrapMode = 'apply' | 'genesis' | 'rehearsal' | 'rehearsal-cleanup' | 'host-keys' | 'rehearsal-host-keys' | 'status';
93
+ export type OperatorMetalBootstrapMode = 'apply' | 'genesis' | 'genesis-cleanup' | 'rehearsal' | 'rehearsal-cleanup' | 'host-keys' | 'rehearsal-host-keys' | 'status';
94
94
  export interface OperatorPlatformBootstrapPlan {
95
95
  kind: 'platform-remote';
96
96
  mode: OperatorPlatformBootstrapMode;
@@ -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.115";
1463
+ var VERSION = "0.1.117";
1464
1464
 
1465
1465
  // src/software.ts
1466
1466
  var PINNED_BUN_VERSION = "1.3.14";
@@ -3127,6 +3127,9 @@ function platformGenesisClaims(guests, sshPublicKeys) {
3127
3127
  throw new Error("platform genesis requires at least one operator SSH public key");
3128
3128
  return reviewed.map((guest) => claimFor(guest, keys, "create"));
3129
3129
  }
3130
+ function platformGenesisDeletionClaims(guests) {
3131
+ return validatePlatformGenesisGuests(guests).map((guest) => claimFor(guest, [], "delete"));
3132
+ }
3130
3133
  function platformCommunityRehearsalClaims(seedGuests, rehearsalGuest, sshPublicKeys) {
3131
3134
  const reviewed = validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4);
3132
3135
  const keys = [...new Set(sshPublicKeys.map(publicKey))];
@@ -3269,9 +3272,6 @@ function validatePlatformSharedEnvironment(input) {
3269
3272
  } else if (input.email !== undefined) {
3270
3273
  throw new Error("Bootstrap email provider must be smtp or jetemail.");
3271
3274
  }
3272
- if (!input.githubOAuth || !/^(?:Iv1\.[A-Fa-f0-9]{16}|Ov23li[A-Za-z0-9]{14,})$/.test(input.githubOAuth.clientId)) {
3273
- throw new Error("GitHub OAuth client id is malformed.");
3274
- }
3275
3275
  boundedInteger("publicApiPort", input.publicApiPort, 1024, 65533);
3276
3276
  if (!Array.isArray(input.seedSyncMembers) || input.seedSyncMembers.length < 3 || input.seedSyncMembers.length > 64 || new Set(input.seedSyncMembers).size !== input.seedSyncMembers.length) {
3277
3277
  throw new Error("seedSyncMembers must contain 3 to 64 unique physical host identities.");
@@ -3402,7 +3402,6 @@ function renderPlatformSharedEnvironment(input) {
3402
3402
  FZ_REALTIME_WORKER_SCRIPT: value.realtime?.workerScriptName ?? "",
3403
3403
  FZ_REALTIME_ENDPOINT: value.realtime?.endpoint ?? "",
3404
3404
  FZ_REALTIME_PRODUCER: value.realtime?.producer ?? "",
3405
- FZ_GITHUB_OAUTH_CLIENT_ID: value.githubOAuth.clientId,
3406
3405
  FZ_PLATFORM_INITIAL_INVENTORY: value.initialInventory ? JSON.stringify(value.initialInventory) : ""
3407
3406
  };
3408
3407
  return `# Generated by fz bootstrap platform. Non-secret coordinates only.
@@ -3414,7 +3413,6 @@ function platformApiCredentialSpecs(options) {
3414
3413
  const optional = [
3415
3414
  ["fz_smtp.password", options.emailProvider === "smtp"],
3416
3415
  ["fz_jetemail.apiKey", options.emailProvider === "jetemail"],
3417
- ["fz_oauth.github.clientSecret", options.githubOAuth],
3418
3416
  ["CF_API_TOKEN", options.cloudflareKv],
3419
3417
  ["CF_TUNNEL_TOKEN", options.cloudflareKv],
3420
3418
  ["REALTIME_PUBLISH_SECRET", options.realtime],
@@ -3811,15 +3809,7 @@ function validatePlatformBootstrapSecrets(config, input) {
3811
3809
  if (!input || typeof input !== "object" || Array.isArray(input))
3812
3810
  throw new Error("bootstrap credential input must be an object");
3813
3811
  const source = input;
3814
- const allowed = [
3815
- "clusterBootstrapCode",
3816
- "emailSecret",
3817
- "enrolmentToken",
3818
- "backupS3Secret",
3819
- "cloudflareTunnelToken",
3820
- "cloudflareApiToken",
3821
- "githubOAuthClientSecret"
3822
- ];
3812
+ const allowed = ["clusterBootstrapCode", "emailSecret", "enrolmentToken", "backupS3Secret", "cloudflareTunnelToken", "cloudflareApiToken"];
3823
3813
  const unknown = Object.keys(source).filter((key) => !allowed.includes(key));
3824
3814
  if (unknown.length)
3825
3815
  throw new Error(`bootstrap credential input contains unsupported field ${unknown[0]}`);
@@ -3829,7 +3819,6 @@ function validatePlatformBootstrapSecrets(config, input) {
3829
3819
  const backupS3Secret = typeof source.backupS3Secret === "string" ? source.backupS3Secret.trim() : undefined;
3830
3820
  const cloudflareTunnelToken = typeof source.cloudflareTunnelToken === "string" ? source.cloudflareTunnelToken.trim() : undefined;
3831
3821
  const cloudflareApiToken = typeof source.cloudflareApiToken === "string" ? source.cloudflareApiToken.trim() : undefined;
3832
- const githubOAuthClientSecret = typeof source.githubOAuthClientSecret === "string" ? source.githubOAuthClientSecret.trim() : "";
3833
3822
  if (!/^[a-f0-9]{64}$/i.test(clusterBootstrapCode))
3834
3823
  throw new Error("cluster bootstrap code must contain exactly 64 hexadecimal characters");
3835
3824
  if (!emailSecret || emailSecret.length > 16384 || /[\r\n\0]/.test(emailSecret))
@@ -3849,16 +3838,12 @@ function validatePlatformBootstrapSecrets(config, input) {
3849
3838
  }
3850
3839
  if (cloudflareTunnelToken)
3851
3840
  validateCloudflareBootstrapSecretPair({ cloudflareTunnelToken, cloudflareApiToken });
3852
- if (githubOAuthClientSecret.length < 20 || githubOAuthClientSecret.length > 512 || /[\r\n\0]/.test(githubOAuthClientSecret)) {
3853
- throw new Error("GitHub OAuth client secret is malformed");
3854
- }
3855
3841
  return {
3856
3842
  clusterBootstrapCode,
3857
3843
  emailSecret,
3858
3844
  ...enrolmentToken ? { enrolmentToken } : {},
3859
3845
  ...backupS3Secret ? { backupS3Secret } : {},
3860
- ...cloudflareTunnelToken ? { cloudflareTunnelToken, cloudflareApiToken } : {},
3861
- githubOAuthClientSecret
3846
+ ...cloudflareTunnelToken ? { cloudflareTunnelToken, cloudflareApiToken } : {}
3862
3847
  };
3863
3848
  }
3864
3849
  var platformBootstrapRunner = (config) => config.kind === "platform" && config.database.role === "master";
@@ -4781,8 +4766,7 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
4781
4766
  enrolmentToken: checked4.enrolmentToken,
4782
4767
  backup: checked4.backupS3Secret,
4783
4768
  cloudflareTunnelToken: checked4.cloudflareTunnelToken,
4784
- cloudflareApiToken: checked4.cloudflareApiToken,
4785
- githubOAuthClientSecret: checked4.githubOAuthClientSecret
4769
+ cloudflareApiToken: checked4.cloudflareApiToken
4786
4770
  };
4787
4771
  })() : undefined;
4788
4772
  const enrolledPrivate = config.kind === "enrolled-compute" ? validateEnrolledComputeBootstrapSecrets(config, secrets) : undefined;
@@ -4883,7 +4867,6 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
4883
4867
  const emailCredentialName = config.runtime.environment.email?.provider === "smtp" ? "fz_smtp.password" : config.runtime.environment.email?.provider === "jetemail" ? "fz_jetemail.apiKey" : undefined;
4884
4868
  for (const [name, source] of Object.entries({
4885
4869
  ...emailCredentialName ? { [emailCredentialName]: platformPrivate.email } : {},
4886
- "fz_oauth.github.clientSecret": platformPrivate.githubOAuthClientSecret,
4887
4870
  "backup.s3.secretAccessKey": platformPrivate.backup
4888
4871
  })) {
4889
4872
  if (source) {
@@ -4900,7 +4883,6 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
4900
4883
  const credentials = platformApiCredentialSpecs({
4901
4884
  emailProvider: runtime.environment.email?.provider,
4902
4885
  cloudflareKv: cloudflareConfigured,
4903
- githubOAuth: true,
4904
4886
  realtime: Boolean(runtime.environment.realtime)
4905
4887
  });
4906
4888
  const units = renderPlatformApiUnits({
@@ -5148,7 +5130,6 @@ function strictBootstrapDocument(value) {
5148
5130
  "agentOtlpEndpoint",
5149
5131
  "custodianEmail",
5150
5132
  "email",
5151
- "githubOAuth",
5152
5133
  "deployProfile",
5153
5134
  "otlpFlushIntervalMs",
5154
5135
  "otlpTraceSampleRatio",
@@ -5159,7 +5140,6 @@ function strictBootstrapDocument(value) {
5159
5140
  "databaseReadPreferredCoordinators"
5160
5141
  ], "runtime environment");
5161
5142
  const environment = runtime.environment;
5162
- exactKeys(environment.githubOAuth, ["clientId"], "GitHub OAuth config");
5163
5143
  if (environment.initialInventory !== undefined) {
5164
5144
  const inventory = exactKeys(environment.initialInventory, ["metalHostname", "metalIdentity", "region", "computes", "attestation", "deployment"], "initial inventory");
5165
5145
  if (inventory.metalIdentity !== undefined) {
@@ -6712,14 +6692,14 @@ function planOperatorMetalBootstrap(request, mode) {
6712
6692
  if ((mode === "rehearsal" || mode === "rehearsal-cleanup") && !request.genesis.rehearsalNode) {
6713
6693
  throw new Error("the reviewed metal request has no rehearsal node");
6714
6694
  }
6715
- const claims = mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
6695
+ const claims = mode === "genesis-cleanup" ? platformGenesisDeletionClaims(request.genesis.nodes) : mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
6716
6696
  const genesisNodes = mode === "host-keys" ? request.genesis.nodes.map(({ name }) => name) : mode === "rehearsal-host-keys" ? guestRows(request).map(({ name }) => name) : claims.map((claim) => claim.spec.guestName).filter((name) => Boolean(name));
6717
6697
  return {
6718
6698
  kind: "metal-remote",
6719
6699
  mode,
6720
6700
  mutation: !["status", "host-keys", "rehearsal-host-keys"].includes(mode),
6721
6701
  target: { address: request.target.address, port: request.target.port, user: request.target.user },
6722
- steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "host-keys" || mode === "rehearsal-host-keys" ? ["verify pinned SSH transport", "collect exact Ed25519 host keys for the reviewed guest addresses"] : mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the three reviewed seed claims plus the reviewed fourth Community rehearsal claim to the constrained Metal helper" : mode === "rehearsal-cleanup" ? "delete the exact four reviewed disposable guests and their owned disks, units, seed data and inventory" : "send the three reviewed platform-genesis claims to the constrained Metal helper"] : [
6702
+ steps: mode === "status" ? ["verify pinned SSH transport", "run typed fz bootstrap status"] : mode === "host-keys" || mode === "rehearsal-host-keys" ? ["verify pinned SSH transport", "collect exact Ed25519 host keys for the reviewed guest addresses"] : mode === "genesis" || mode === "genesis-cleanup" || mode === "rehearsal" || mode === "rehearsal-cleanup" ? ["verify pinned SSH transport", mode === "rehearsal" ? "send the three reviewed seed claims plus the reviewed fourth Community rehearsal claim to the constrained Metal helper" : mode === "genesis-cleanup" ? "delete the exact three reviewed platform guests and their owned disks, units, seed data and inventory" : mode === "rehearsal-cleanup" ? "delete the exact four reviewed disposable guests and their owned disks, units, seed data and inventory" : "send the three reviewed platform-genesis claims to the constrained Metal helper"] : [
6723
6703
  "verify pinned SSH transport and caller-approved SSH agent",
6724
6704
  "copy pinned Bun and packaged fz artifacts to private staging",
6725
6705
  "stage the strict owner-only metal config and optional seed handoff",
@@ -7412,13 +7392,13 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
7412
7392
  output: JSON.stringify(await collectOperatorGuestHostKeys(request, { ...options, exec }, mode === "rehearsal-host-keys"))
7413
7393
  };
7414
7394
  }
7415
- if (mode === "genesis" || mode === "rehearsal" || mode === "rehearsal-cleanup") {
7395
+ if (mode === "genesis" || mode === "genesis-cleanup" || mode === "rehearsal" || mode === "rehearsal-cleanup") {
7416
7396
  const keys = request.genesis.sshPublicKeyFiles.map((path) => publicIdentity(path));
7417
7397
  if ((mode === "rehearsal" || mode === "rehearsal-cleanup") && !request.genesis.rehearsalNode) {
7418
7398
  throw new Error("the reviewed metal request has no rehearsal node");
7419
7399
  }
7420
7400
  const outputs = [];
7421
- const claims = mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
7401
+ const claims = mode === "genesis-cleanup" ? platformGenesisDeletionClaims(request.genesis.nodes) : mode === "rehearsal" || mode === "rehearsal-cleanup" ? mode === "rehearsal-cleanup" ? platformCommunityRehearsalDeletionClaims(request.genesis.nodes, request.genesis.rehearsalNode) : platformCommunityRehearsalClaims(request.genesis.nodes, request.genesis.rehearsalNode, keys) : platformGenesisClaims(request.genesis.nodes, keys);
7422
7402
  for (const claim of claims) {
7423
7403
  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)}
7424
7404
  `));
@@ -68,14 +68,6 @@ export type PlatformBootstrapEmail = {
68
68
  from: string;
69
69
  eu: boolean;
70
70
  };
71
- /**
72
- * Public coordinates for one GitHub App. Its client secret, RSA private key and
73
- * webhook secret are attended inputs sealed as systemd credentials and later
74
- * imported into the unlocked platform Vault.
75
- */
76
- export interface PlatformBootstrapGitHubOAuth {
77
- clientId: string;
78
- }
79
71
  export interface PlatformSharedEnvironment {
80
72
  softwareProfile: PlatformSoftwareProfile;
81
73
  databaseRole: PlatformDatabaseRole;
@@ -107,7 +99,6 @@ export interface PlatformSharedEnvironment {
107
99
  otlpTraceSampleRatio: number;
108
100
  custodianEmail?: string;
109
101
  email?: PlatformBootstrapEmail;
110
- githubOAuth: PlatformBootstrapGitHubOAuth;
111
102
  backup?: {
112
103
  endpoint: string;
113
104
  region: string;
@@ -135,13 +126,12 @@ export declare function validatePlatformSharedEnvironment(input: PlatformSharedE
135
126
  /** Render only non-secret runtime coordinates. Passwords/tokens have no field in this contract. */
136
127
  export declare function renderPlatformSharedEnvironment(input: PlatformSharedEnvironment): string;
137
128
  export interface SystemdCredentialSpec {
138
- name: 'arangodb-jwt' | 'arangodb-root-password' | 'seed-sync-root' | 'fz_smtp.password' | 'fz_jetemail.apiKey' | 'fz_oauth.github.clientSecret' | 'CF_API_TOKEN' | 'CF_TUNNEL_TOKEN' | 'REALTIME_PUBLISH_SECRET' | 'REALTIME_TICKET_SECRET';
129
+ name: 'arangodb-jwt' | 'arangodb-root-password' | 'seed-sync-root' | 'fz_smtp.password' | 'fz_jetemail.apiKey' | 'CF_API_TOKEN' | 'CF_TUNNEL_TOKEN' | 'REALTIME_PUBLISH_SECRET' | 'REALTIME_TICKET_SECRET';
139
130
  encryptedPath: string;
140
131
  required: boolean;
141
132
  }
142
133
  export declare function platformApiCredentialSpecs(options: {
143
134
  emailProvider?: PlatformBootstrapEmail['provider'];
144
- githubOAuth: boolean;
145
135
  cloudflareKv: boolean;
146
136
  realtime: boolean;
147
137
  }): SystemdCredentialSpec[];
@@ -669,6 +669,9 @@ function platformGenesisClaims(guests, sshPublicKeys) {
669
669
  throw new Error("platform genesis requires at least one operator SSH public key");
670
670
  return reviewed.map((guest) => claimFor(guest, keys, "create"));
671
671
  }
672
+ function platformGenesisDeletionClaims(guests) {
673
+ return validatePlatformGenesisGuests(guests).map((guest) => claimFor(guest, [], "delete"));
674
+ }
672
675
  function platformCommunityRehearsalClaims(seedGuests, rehearsalGuest, sshPublicKeys) {
673
676
  const reviewed = validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4);
674
677
  const keys = [...new Set(sshPublicKeys.map(publicKey))];
@@ -828,9 +831,6 @@ function validatePlatformSharedEnvironment(input) {
828
831
  } else if (input.email !== undefined) {
829
832
  throw new Error("Bootstrap email provider must be smtp or jetemail.");
830
833
  }
831
- if (!input.githubOAuth || !/^(?:Iv1\.[A-Fa-f0-9]{16}|Ov23li[A-Za-z0-9]{14,})$/.test(input.githubOAuth.clientId)) {
832
- throw new Error("GitHub OAuth client id is malformed.");
833
- }
834
834
  boundedInteger("publicApiPort", input.publicApiPort, 1024, 65533);
835
835
  if (!Array.isArray(input.seedSyncMembers) || input.seedSyncMembers.length < 3 || input.seedSyncMembers.length > 64 || new Set(input.seedSyncMembers).size !== input.seedSyncMembers.length) {
836
836
  throw new Error("seedSyncMembers must contain 3 to 64 unique physical host identities.");
@@ -961,7 +961,6 @@ function renderPlatformSharedEnvironment(input) {
961
961
  FZ_REALTIME_WORKER_SCRIPT: value.realtime?.workerScriptName ?? "",
962
962
  FZ_REALTIME_ENDPOINT: value.realtime?.endpoint ?? "",
963
963
  FZ_REALTIME_PRODUCER: value.realtime?.producer ?? "",
964
- FZ_GITHUB_OAUTH_CLIENT_ID: value.githubOAuth.clientId,
965
964
  FZ_PLATFORM_INITIAL_INVENTORY: value.initialInventory ? JSON.stringify(value.initialInventory) : ""
966
965
  };
967
966
  return `# Generated by fz bootstrap platform. Non-secret coordinates only.
@@ -973,7 +972,6 @@ function platformApiCredentialSpecs(options) {
973
972
  const optional = [
974
973
  ["fz_smtp.password", options.emailProvider === "smtp"],
975
974
  ["fz_jetemail.apiKey", options.emailProvider === "jetemail"],
976
- ["fz_oauth.github.clientSecret", options.githubOAuth],
977
975
  ["CF_API_TOKEN", options.cloudflareKv],
978
976
  ["CF_TUNNEL_TOKEN", options.cloudflareKv],
979
977
  ["REALTIME_PUBLISH_SECRET", options.realtime],
@@ -3009,7 +3009,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
3009
3009
  }
3010
3010
 
3011
3011
  // src/version.ts
3012
- var VERSION3 = "0.1.115";
3012
+ var VERSION3 = "0.1.117";
3013
3013
 
3014
3014
  // src/egress-policy.ts
3015
3015
  import { realpathSync as realpathSync3 } from "node:fs";
@@ -4185,6 +4185,9 @@ function platformGenesisClaims(guests, sshPublicKeys) {
4185
4185
  throw new Error("platform genesis requires at least one operator SSH public key");
4186
4186
  return reviewed.map((guest) => claimFor(guest, keys, "create"));
4187
4187
  }
4188
+ function platformGenesisDeletionClaims(guests) {
4189
+ return validatePlatformGenesisGuests(guests).map((guest) => claimFor(guest, [], "delete"));
4190
+ }
4188
4191
  function platformCommunityRehearsalClaims(seedGuests, rehearsalGuest, sshPublicKeys) {
4189
4192
  const reviewed = validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4);
4190
4193
  const keys = [...new Set(sshPublicKeys.map(publicKey))];
@@ -4344,9 +4347,6 @@ function validatePlatformSharedEnvironment(input) {
4344
4347
  } else if (input.email !== undefined) {
4345
4348
  throw new Error("Bootstrap email provider must be smtp or jetemail.");
4346
4349
  }
4347
- if (!input.githubOAuth || !/^(?:Iv1\.[A-Fa-f0-9]{16}|Ov23li[A-Za-z0-9]{14,})$/.test(input.githubOAuth.clientId)) {
4348
- throw new Error("GitHub OAuth client id is malformed.");
4349
- }
4350
4350
  boundedInteger("publicApiPort", input.publicApiPort, 1024, 65533);
4351
4351
  if (!Array.isArray(input.seedSyncMembers) || input.seedSyncMembers.length < 3 || input.seedSyncMembers.length > 64 || new Set(input.seedSyncMembers).size !== input.seedSyncMembers.length) {
4352
4352
  throw new Error("seedSyncMembers must contain 3 to 64 unique physical host identities.");
@@ -4477,7 +4477,6 @@ function renderPlatformSharedEnvironment(input) {
4477
4477
  FZ_REALTIME_WORKER_SCRIPT: value.realtime?.workerScriptName ?? "",
4478
4478
  FZ_REALTIME_ENDPOINT: value.realtime?.endpoint ?? "",
4479
4479
  FZ_REALTIME_PRODUCER: value.realtime?.producer ?? "",
4480
- FZ_GITHUB_OAUTH_CLIENT_ID: value.githubOAuth.clientId,
4481
4480
  FZ_PLATFORM_INITIAL_INVENTORY: value.initialInventory ? JSON.stringify(value.initialInventory) : ""
4482
4481
  };
4483
4482
  return `# Generated by fz bootstrap platform. Non-secret coordinates only.
@@ -4489,7 +4488,6 @@ function platformApiCredentialSpecs(options) {
4489
4488
  const optional = [
4490
4489
  ["fz_smtp.password", options.emailProvider === "smtp"],
4491
4490
  ["fz_jetemail.apiKey", options.emailProvider === "jetemail"],
4492
- ["fz_oauth.github.clientSecret", options.githubOAuth],
4493
4491
  ["CF_API_TOKEN", options.cloudflareKv],
4494
4492
  ["CF_TUNNEL_TOKEN", options.cloudflareKv],
4495
4493
  ["REALTIME_PUBLISH_SECRET", options.realtime],
@@ -21,6 +21,8 @@ export interface PlatformGenesisGuest {
21
21
  export declare function validatePlatformGenesisGuests(guests: readonly PlatformGenesisGuest[], expectedCount?: number): readonly PlatformGenesisGuest[];
22
22
  /** Convert reviewed desired state into the ordinary Metal provisioning claim. */
23
23
  export declare function platformGenesisClaims(guests: readonly PlatformGenesisGuest[], sshPublicKeys: readonly string[]): readonly RemoteProvisionClaim[];
24
+ /** Exact inverse of the reviewed three-node platform seed. */
25
+ export declare function platformGenesisDeletionClaims(guests: readonly PlatformGenesisGuest[]): readonly RemoteProvisionClaim[];
24
26
  /** Add one explicitly reviewed disposable node to the same three guest requests. */
25
27
  export declare function platformCommunityRehearsalClaims(seedGuests: readonly PlatformGenesisGuest[], rehearsalGuest: PlatformGenesisGuest, sshPublicKeys: readonly string[]): readonly RemoteProvisionClaim[];
26
28
  /** Exact inverse of the reviewed disposable four-host request. */
@@ -669,6 +669,9 @@ function platformGenesisClaims(guests, sshPublicKeys) {
669
669
  throw new Error("platform genesis requires at least one operator SSH public key");
670
670
  return reviewed.map((guest) => claimFor(guest, keys, "create"));
671
671
  }
672
+ function platformGenesisDeletionClaims(guests) {
673
+ return validatePlatformGenesisGuests(guests).map((guest) => claimFor(guest, [], "delete"));
674
+ }
672
675
  function platformCommunityRehearsalClaims(seedGuests, rehearsalGuest, sshPublicKeys) {
673
676
  const reviewed = validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4);
674
677
  const keys = [...new Set(sshPublicKeys.map(publicKey))];
@@ -681,6 +684,7 @@ function platformCommunityRehearsalDeletionClaims(seedGuests, rehearsalGuest) {
681
684
  }
682
685
  export {
683
686
  validatePlatformGenesisGuests,
687
+ platformGenesisDeletionClaims,
684
688
  platformGenesisClaims,
685
689
  platformCommunityRehearsalDeletionClaims,
686
690
  platformCommunityRehearsalClaims
@@ -4,7 +4,6 @@ export interface PlatformLaunchEnvironmentInput {
4
4
  emailSecret: string;
5
5
  cloudflareTunnelToken: string;
6
6
  cloudflareApiToken: string;
7
- githubOAuthClientSecret: string;
8
7
  }
9
8
  /**
10
9
  * Read the development-only attended-launch adapter.
@@ -45,10 +45,6 @@ export interface ForgeZeroLaunchOwnerInput {
45
45
  from: string;
46
46
  eu: boolean;
47
47
  };
48
- /** Required bootstrap identity provider. Repository automation is configured after genesis. */
49
- githubOAuth: {
50
- clientId: string;
51
- };
52
48
  }
53
49
  /**
54
50
  * ForgeZero's own genesis public coordinates. They are reviewed source data,
package/dist/provision.js CHANGED
@@ -3009,7 +3009,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
3009
3009
  }
3010
3010
 
3011
3011
  // src/version.ts
3012
- var VERSION3 = "0.1.115";
3012
+ var VERSION3 = "0.1.117";
3013
3013
 
3014
3014
  // src/egress-policy.ts
3015
3015
  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.115";
2
+ export declare const VERSION = "0.1.117";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/agent",
3
- "version": "0.1.115",
3
+ "version": "0.1.117",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",
@@ -11,13 +11,13 @@
11
11
  "typescript": "^5.6.0",
12
12
  "@types/bun": "latest",
13
13
  "@types/node": "^22.0.0",
14
- "@forgezero/access": "0.1.9",
14
+ "@forgezero/access": "0.1.11",
15
15
  "@noble/curves": "^2.2.0",
16
16
  "@noble/post-quantum": "^0.6.1"
17
17
  },
18
18
  "dependencies": {
19
- "@forgezero/runtime": "0.1.14",
20
- "@forgezero/vault": "0.1.20",
19
+ "@forgezero/runtime": "0.1.16",
20
+ "@forgezero/vault": "0.1.21",
21
21
  "@noble/curves": "2.2.0",
22
22
  "@noble/hashes": "2.2.0",
23
23
  "@noble/post-quantum": "0.6.1",