@forgezero/agent 0.1.68 → 0.1.69

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.68";
752
+ var VERSION3 = "0.1.69";
753
753
 
754
754
  // src/agent-heartbeat.ts
755
755
  var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
@@ -0,0 +1,22 @@
1
+ import { finalizeCloudflareBootstrapAcceptance, runAttendedCloudflareBootstrap, type AttendedCloudflareBootstrapRequest, type CloudflareBootstrapAcceptanceEvidence, type CloudflareBootstrapEvidence } from './cloudflare-bootstrap';
2
+ import { applyOperatorPlatformFleetBootstrap, type OperatorPlatformBootstrapFleetRequest, type OperatorPlatformBootstrapFleetResult } from './operator-bootstrap';
3
+ import type { PlatformBootstrapSecrets } from './bootstrap';
4
+ export interface AttendedPlatformBootstrapResult {
5
+ kind: 'forgezero-attended-platform-bootstrap';
6
+ ok: boolean;
7
+ edge: CloudflareBootstrapEvidence;
8
+ fleet: OperatorPlatformBootstrapFleetResult;
9
+ acceptance?: CloudflareBootstrapAcceptanceEvidence;
10
+ inviteUrl?: string;
11
+ }
12
+ export interface AttendedPlatformBootstrapDependencies {
13
+ cloudflare?: typeof runAttendedCloudflareBootstrap;
14
+ fleet?: typeof applyOperatorPlatformFleetBootstrap;
15
+ finalize?: typeof finalizeCloudflareBootstrapAcceptance;
16
+ }
17
+ /**
18
+ * The sole attended genesis composition boundary. All non-secret coordinates
19
+ * are checked before the first Cloudflare or SSH mutation. Secret values exist
20
+ * only in this call and the authenticated SSH stdin used by the fleet runner.
21
+ */
22
+ export declare function applyAttendedPlatformBootstrap(fleetRequest: OperatorPlatformBootstrapFleetRequest, cloudflareRequest: AttendedCloudflareBootstrapRequest, acceptancePath: string, secrets: PlatformBootstrapSecrets, dependencies?: AttendedPlatformBootstrapDependencies): Promise<AttendedPlatformBootstrapResult>;
package/dist/bootstrap.js CHANGED
@@ -1396,7 +1396,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1396
1396
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1397
1397
 
1398
1398
  // src/version.ts
1399
- var VERSION = "0.1.68";
1399
+ var VERSION = "0.1.69";
1400
1400
 
1401
1401
  // src/software.ts
1402
1402
  var PINNED_BUN_VERSION = "1.3.14";
@@ -4380,6 +4380,7 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
4380
4380
  }
4381
4381
  bindBootstrapIntent(host, config);
4382
4382
  const plan = planBootstrap(config, host.exists(STATE_PATH));
4383
+ await checked3(host, ["hostnamectl", "set-hostname", "--static", config.nodeHostname], "compute hostname");
4383
4384
  const alreadyEnrolled = host.exists("/var/lib/forgezero/enrolment.json");
4384
4385
  host.mkdir(CREDS, 448);
4385
4386
  host.mkdir("/var/lib/forgezero", 448);
package/dist/fz-agent.js CHANGED
@@ -8588,7 +8588,7 @@ import { join as join3 } from "path";
8588
8588
  import { fileURLToPath } from "url";
8589
8589
 
8590
8590
  // src/version.ts
8591
- var VERSION2 = "0.1.68";
8591
+ var VERSION2 = "0.1.69";
8592
8592
 
8593
8593
  // src/ssh-bootstrap.ts
8594
8594
  class SshBootstrapError extends Error {
package/dist/fz.js CHANGED
@@ -4829,7 +4829,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
4829
4829
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
4830
4830
 
4831
4831
  // src/version.ts
4832
- var VERSION2 = "0.1.68";
4832
+ var VERSION2 = "0.1.69";
4833
4833
 
4834
4834
  // src/software.ts
4835
4835
  var PINNED_BUN_VERSION = "1.3.14";
@@ -14399,6 +14399,7 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
14399
14399
  }
14400
14400
  bindBootstrapIntent(host, config);
14401
14401
  const plan = planBootstrap(config, host.exists(STATE_PATH));
14402
+ await checked3(host, ["hostnamectl", "set-hostname", "--static", config.nodeHostname], "compute hostname");
14402
14403
  const alreadyEnrolled = host.exists("/var/lib/forgezero/enrolment.json");
14403
14404
  host.mkdir(CREDS, 448);
14404
14405
  host.mkdir("/var/lib/forgezero", 448);
@@ -15076,6 +15077,14 @@ async function runCloudflareBootstrapFinalizeCommand(configPath, dependencies =
15076
15077
  return evidence;
15077
15078
  }
15078
15079
 
15080
+ // src/operator-bootstrap.ts
15081
+ import { createHash as createHash6, randomBytes as randomBytes9 } from "crypto";
15082
+ import { chmodSync as chmodSync6, lstatSync as lstatSync7, mkdirSync as mkdirSync12, mkdtempSync, readFileSync as readFileSync12, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
15083
+ import { isIP as isIP6 } from "net";
15084
+ import { tmpdir } from "os";
15085
+ import { basename as basename2, dirname as dirname13, isAbsolute as isAbsolute5, join as join10, resolve as resolve9 } from "path";
15086
+ import { fileURLToPath as fileURLToPath2 } from "url";
15087
+
15079
15088
  // src/metal-bootstrap.ts
15080
15089
  import { createHash as createHash5, randomBytes as randomBytes8 } from "crypto";
15081
15090
  import {
@@ -15986,12 +15995,6 @@ async function metalBootstrapStatus(exec = defaultExec2) {
15986
15995
  }
15987
15996
 
15988
15997
  // src/operator-bootstrap.ts
15989
- import { createHash as createHash6, randomBytes as randomBytes9 } from "crypto";
15990
- import { chmodSync as chmodSync6, lstatSync as lstatSync7, mkdirSync as mkdirSync12, mkdtempSync, readFileSync as readFileSync12, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
15991
- import { isIP as isIP6 } from "net";
15992
- import { tmpdir } from "os";
15993
- import { basename as basename2, dirname as dirname13, isAbsolute as isAbsolute5, join as join10, resolve as resolve9 } from "path";
15994
- import { fileURLToPath as fileURLToPath2 } from "url";
15995
15998
  var REQUEST_LIMIT = 64 * 1024;
15996
15999
  var SECRET_LIMIT = 256 * 1024;
15997
16000
  var REMOTE_STAGE = "/run/forgezero-operator-bootstrap";
@@ -16129,7 +16132,12 @@ function writeOperatorPlatformBootstrapRequest(path, request) {
16129
16132
  }
16130
16133
  function readOperatorPlatformBootstrapFleetRequest(path) {
16131
16134
  const value = JSON.parse(new TextDecoder().decode(ownerFile(path, REQUEST_LIMIT, "operator fleet request")));
16132
- const root = exactKeys5(value, ["kind", "requests"], "operator fleet request");
16135
+ const root = exactKeys5(value, [
16136
+ "kind",
16137
+ "requests",
16138
+ "cloudflareConfigFile",
16139
+ "cloudflareAcceptanceFile"
16140
+ ], "operator fleet request");
16133
16141
  if (root.kind !== "platform-fleet-remote")
16134
16142
  throw new Error("operator fleet kind must be platform-fleet-remote");
16135
16143
  if (!Array.isArray(root.requests) || root.requests.length !== 3 || root.requests.some((entry) => typeof entry !== "string")) {
@@ -16140,10 +16148,25 @@ function readOperatorPlatformBootstrapFleetRequest(path) {
16140
16148
  throw new Error("operator fleet request files must be unique");
16141
16149
  for (const request of requests)
16142
16150
  readOperatorPlatformBootstrapRequest(request);
16143
- return { kind: "platform-fleet-remote", requests };
16151
+ if (typeof root.cloudflareConfigFile !== "string" || typeof root.cloudflareAcceptanceFile !== "string") {
16152
+ throw new Error("operator fleet requires Cloudflare coordinates and an acceptance evidence path");
16153
+ }
16154
+ const cloudflareConfigFile = canonicalOutputPath(root.cloudflareConfigFile, "operator fleet Cloudflare config");
16155
+ const cloudflareAcceptanceFile = canonicalOutputPath(root.cloudflareAcceptanceFile, "operator fleet Cloudflare acceptance evidence");
16156
+ return {
16157
+ kind: "platform-fleet-remote",
16158
+ requests,
16159
+ cloudflareConfigFile,
16160
+ cloudflareAcceptanceFile
16161
+ };
16144
16162
  }
16145
- function writeOperatorPlatformBootstrapFleetRequest(path, requestFiles) {
16146
- const request = { kind: "platform-fleet-remote", requests: requestFiles };
16163
+ function writeOperatorPlatformBootstrapFleetRequest(path, requestFiles, cloudflare) {
16164
+ const request = {
16165
+ kind: "platform-fleet-remote",
16166
+ requests: requestFiles,
16167
+ cloudflareConfigFile: canonicalOutputPath(cloudflare.configFile, "operator fleet Cloudflare config"),
16168
+ cloudflareAcceptanceFile: canonicalOutputPath(cloudflare.acceptanceFile, "operator fleet Cloudflare acceptance evidence")
16169
+ };
16147
16170
  if (new Set(requestFiles).size !== 3)
16148
16171
  throw new Error("operator fleet request files must be unique");
16149
16172
  for (const file of requestFiles)
@@ -16396,6 +16419,20 @@ function validatedPlatformFleet(fleet) {
16396
16419
  }
16397
16420
  return { requests, configs, environment };
16398
16421
  }
16422
+ function operatorPlatformFleetCoordinates(fleet) {
16423
+ const { requests, configs, environment } = validatedPlatformFleet(fleet);
16424
+ return {
16425
+ environment,
16426
+ appOrigin: configs[0].runtime.environment.appOrigin,
16427
+ apiOrigin: configs[0].runtime.environment.apiOrigin,
16428
+ nodes: configs.map((config, index) => ({
16429
+ computeReference: config.computeReference,
16430
+ address: requests[index].target.address,
16431
+ nodeHostname: config.nodeHostname,
16432
+ databaseRole: config.database.role
16433
+ }))
16434
+ };
16435
+ }
16399
16436
  function planOperatorPlatformFleetBootstrap(fleet, mode) {
16400
16437
  const { requests, configs, environment } = validatedPlatformFleet(fleet);
16401
16438
  const secretInputs = mode === "apply" ? attendedSecretNames(configs[0]) : [];
@@ -16797,6 +16834,47 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
16797
16834
  }
16798
16835
  }
16799
16836
 
16837
+ // src/attended-platform-bootstrap.ts
16838
+ var invitationFrom = (fleet, master) => {
16839
+ const output = fleet.outcomes.find(({ computeReference }) => computeReference === master)?.output;
16840
+ try {
16841
+ const parsed = JSON.parse(output ?? "");
16842
+ if (typeof parsed.launch?.inviteUrl === "string")
16843
+ return parsed.launch.inviteUrl;
16844
+ } catch {}
16845
+ throw new Error("platform bootstrap completed without the initial custodian invitation");
16846
+ };
16847
+ async function applyAttendedPlatformBootstrap(fleetRequest, cloudflareRequest, acceptancePath, secrets, dependencies = {}) {
16848
+ if (cloudflareRequest.mode !== "apply")
16849
+ throw new Error("attended platform bootstrap requires Cloudflare apply mode");
16850
+ if (!secrets.cloudflareTunnelToken || !secrets.cloudflareApiToken) {
16851
+ throw new Error("attended platform bootstrap requires both Cloudflare tokens");
16852
+ }
16853
+ const fleetCoordinates = operatorPlatformFleetCoordinates(fleetRequest);
16854
+ if (cloudflareRequest.coordinates.nodes.length !== fleetCoordinates.nodes.length || fleetCoordinates.nodes.some((node, index) => {
16855
+ const edge2 = cloudflareRequest.coordinates.nodes[index];
16856
+ return edge2?.nodeName !== node.computeReference || edge2.hostname !== node.nodeHostname || edge2.service !== "http://127.0.0.1:3000";
16857
+ })) {
16858
+ throw new Error("Cloudflare coordinates do not match the exact reviewed platform fleet");
16859
+ }
16860
+ const edge = await (dependencies.cloudflare ?? runAttendedCloudflareBootstrap)({
16861
+ ...cloudflareRequest,
16862
+ tokens: {
16863
+ tunnelToken: secrets.cloudflareTunnelToken,
16864
+ apiToken: secrets.cloudflareApiToken
16865
+ }
16866
+ });
16867
+ const fleet = await (dependencies.fleet ?? applyOperatorPlatformFleetBootstrap)(fleetRequest, "apply", { secrets });
16868
+ if (!fleet.ok)
16869
+ return { kind: "forgezero-attended-platform-bootstrap", ok: false, edge, fleet };
16870
+ const inviteUrl = invitationFrom(fleet, fleetCoordinates.nodes[0].computeReference);
16871
+ const acceptance = await (dependencies.finalize ?? finalizeCloudflareBootstrapAcceptance)({
16872
+ checkpointPath: edge.checkpointFile,
16873
+ acceptancePath
16874
+ });
16875
+ return { kind: "forgezero-attended-platform-bootstrap", ok: true, edge, fleet, acceptance, inviteUrl };
16876
+ }
16877
+
16800
16878
  // src/platform-genesis-config.ts
16801
16879
  function platformGenesisBootstrapConfigs(template, guests, nodes, metalHostname, region) {
16802
16880
  const fleet = validatePlatformGenesisGuests(guests);
@@ -18102,7 +18180,8 @@ function writePlatformGenesisFleet(directory) {
18102
18180
  const guestHostKeys = readOperatorGuestHostKeyEvidence(guestHostKeysFile, metalRequest);
18103
18181
  const fleet = metalRequest.genesis.nodes;
18104
18182
  const metalHostname = readMetalBootstrapConfig(metalRequest.metalConfigFile).metalHostname;
18105
- const template = interactiveBootstrap("platform", fleet);
18183
+ const checkpointPath = join12(output, "cloudflare-handoff.json");
18184
+ const template = interactiveBootstrap("platform", fleet, checkpointPath);
18106
18185
  const region = {
18107
18186
  label: bootstrapAnswer(`Region ${template.runtime.environment.nodeRegion} display label`),
18108
18187
  country: bootstrapAnswer("Region ISO 3166-1 alpha-2 country code").toUpperCase(),
@@ -18110,11 +18189,32 @@ function writePlatformGenesisFleet(directory) {
18110
18189
  };
18111
18190
  const nodes = fleet.map((guest, index) => index === 0 ? {
18112
18191
  nodeHostname: template.nodeHostname,
18113
- cloudflareHandoffFile: template.cloudflareHandoff.handoffFile
18192
+ cloudflareHandoffFile: cloudflareHostHandoffPath(checkpointPath, guest.name)
18114
18193
  } : {
18115
18194
  nodeHostname: bootstrapAnswer(`${guest.name} public node hostname`),
18116
- cloudflareHandoffFile: bootstrapAnswer(`${guest.name} node-specific Cloudflare host handoff`)
18195
+ cloudflareHandoffFile: cloudflareHostHandoffPath(checkpointPath, guest.name)
18117
18196
  });
18197
+ const realtimeEnabled = bootstrapBoolean("Configure existing Worker realtime fan-out?", "yes");
18198
+ const cloudflareConfig = writeBootstrapConfig(join12(output, "cloudflare.json"), createCloudflareBootstrapCommandConfig({
18199
+ checkpointPath,
18200
+ coordinates: {
18201
+ accountId: bootstrapAnswer("Cloudflare account id"),
18202
+ zoneId: bootstrapAnswer("Cloudflare zone id"),
18203
+ kvNamespaceId: bootstrapAnswer("Existing Worker-bound KV namespace id"),
18204
+ ...realtimeEnabled ? { realtime: {
18205
+ workerScriptName: bootstrapAnswer("Existing Worker script name"),
18206
+ endpoint: bootstrapAnswer("Stable public Worker HTTPS endpoint"),
18207
+ producer: bootstrapAnswer("Realtime producer identity", "platform-api")
18208
+ } } : {},
18209
+ nodes: fleet.map((guest, index) => ({
18210
+ nodeName: guest.name,
18211
+ hostname: nodes[index].nodeHostname,
18212
+ service: "http://127.0.0.1:3000",
18213
+ tunnelName: bootstrapAnswer(`${guest.name} Tunnel name`, guest.name)
18214
+ }))
18215
+ }
18216
+ }));
18217
+ const cloudflareAcceptance = join12(output, "cloudflare-acceptance.json");
18118
18218
  const configs = platformGenesisBootstrapConfigs(template, fleet, nodes, metalHostname, region).map((config) => {
18119
18219
  const path = `${output}/${config.computeReference}-platform.json`;
18120
18220
  writeBootstrapConfig(path, config);
@@ -18144,10 +18244,10 @@ function writePlatformGenesisFleet(directory) {
18144
18244
  }
18145
18245
  });
18146
18246
  });
18147
- const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(join12(output, "platform-fleet-remote.json"), requests);
18148
- return { configs, requests, fleetRequest };
18247
+ const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(join12(output, "platform-fleet-remote.json"), requests, { configFile: cloudflareConfig, acceptanceFile: cloudflareAcceptance });
18248
+ return { configs, requests, fleetRequest, cloudflareConfig, cloudflareAcceptance };
18149
18249
  }
18150
- function interactiveBootstrap(kind, genesisGuests) {
18250
+ function interactiveBootstrap(kind, genesisGuests, genesisCloudflareCheckpoint) {
18151
18251
  if (!process.stdin.isTTY)
18152
18252
  throw new Error("non-interactive bootstrap requires --bootstrap-config <private-json-file>");
18153
18253
  const environment = bootstrapAnswer("Environment (production/development)", "production");
@@ -18187,7 +18287,7 @@ function interactiveBootstrap(kind, genesisGuests) {
18187
18287
  const backupRegion = backupEndpoint ? bootstrapAnswer("Backup S3 region") : undefined;
18188
18288
  const backupBucket = backupEndpoint ? bootstrapAnswer("Backup S3 bucket") : undefined;
18189
18289
  const backupAccessKeyId = backupEndpoint ? bootstrapAnswer("Backup S3 access-key id") : undefined;
18190
- const cloudflareHandoffFile = bootstrapAnswer(genesis ? `${computeReference} node-specific Cloudflare host handoff` : "Node-specific Cloudflare host handoff (blank for no public edge)", genesis ? undefined : "");
18290
+ const cloudflareHandoffFile = genesis ? cloudflareHostHandoffPath(genesisCloudflareCheckpoint, computeReference) : bootstrapAnswer("Node-specific Cloudflare host handoff (blank for no public edge)", "");
18191
18291
  const cloudflareNodeName = cloudflareHandoffFile ? bootstrapAnswer("Cloudflare handoff node name", computeReference) : undefined;
18192
18292
  const databaseNetworkMode = "private-lan";
18193
18293
  const enrolmentSource = genesis ? "genesis-derived" : bootstrapAnswer("Enrolment source (genesis-derived/api-token)", "genesis-derived");
@@ -18293,19 +18393,49 @@ async function cmdBootstrap(options, args) {
18293
18393
  throw new Error("remote platform fleet bootstrap requires --bootstrap-config");
18294
18394
  const fleet = readOperatorPlatformBootstrapFleetRequest(options.bootstrapConfigPath);
18295
18395
  const plan2 = planOperatorPlatformFleetBootstrap(fleet, mode);
18396
+ const cloudflare = readCloudflareBootstrapCommandConfig(fleet.cloudflareConfigFile, options.apply && mode === "apply" ? "apply" : "plan");
18397
+ const coordinates = operatorPlatformFleetCoordinates(fleet);
18398
+ if (cloudflare.coordinates.nodes.length !== coordinates.nodes.length || coordinates.nodes.some((node, index) => {
18399
+ const edge = cloudflare.coordinates.nodes[index];
18400
+ return edge?.nodeName !== node.computeReference || edge.hostname !== node.nodeHostname || edge.service !== "http://127.0.0.1:3000";
18401
+ })) {
18402
+ throw new Error("Cloudflare coordinates do not match the exact reviewed platform fleet");
18403
+ }
18296
18404
  if (!options.apply) {
18297
- out.line(JSON.stringify(plan2, null, 2));
18298
- out.step("Review all three pinned targets and shared secret names, then repeat with --apply.");
18405
+ out.line(JSON.stringify({
18406
+ kind: "forgezero-attended-platform-bootstrap-plan",
18407
+ mutation: true,
18408
+ cloudflare: {
18409
+ checkpointFile: cloudflare.checkpointPath,
18410
+ nodes: cloudflare.coordinates.nodes.map(({ nodeName, hostname: hostname2 }) => ({ nodeName, hostname: hostname2 }))
18411
+ },
18412
+ fleet: plan2,
18413
+ acceptanceFile: fleet.cloudflareAcceptanceFile,
18414
+ completion: ["public ingress verified", "initial custodian invitation printed"]
18415
+ }, null, 2));
18416
+ out.step("Review the complete Tunnel, hostname, bundle, credential and three-node plan, then repeat with --apply.");
18299
18417
  return 0;
18300
18418
  }
18301
18419
  const first = readOperatorPlatformBootstrapRequest(fleet.requests[0]);
18302
18420
  const firstConfig = readBootstrapConfig(first.platformConfigFile);
18303
18421
  if (firstConfig.kind !== "platform")
18304
18422
  throw new Error("remote platform fleet requires platform configs");
18423
+ if (mode === "status") {
18424
+ const result3 = await applyOperatorPlatformFleetBootstrap(fleet, mode);
18425
+ out.line(JSON.stringify(result3, null, 2));
18426
+ return result3.ok ? 0 : 1;
18427
+ }
18305
18428
  const secrets2 = mode === "apply" ? await promptPlatformBootstrapSecrets(firstConfig) : undefined;
18306
- const result2 = await applyOperatorPlatformFleetBootstrap(fleet, mode, { secrets: secrets2 });
18429
+ if (!secrets2?.cloudflareTunnelToken || !secrets2.cloudflareApiToken) {
18430
+ throw new Error("attended platform bootstrap requires both Cloudflare tokens");
18431
+ }
18432
+ const result2 = await applyAttendedPlatformBootstrap(fleet, cloudflare, fleet.cloudflareAcceptanceFile, secrets2);
18307
18433
  out.line(JSON.stringify(result2, null, 2));
18308
- return result2.ok ? 0 : 1;
18434
+ if (!result2.ok)
18435
+ return 1;
18436
+ out.line();
18437
+ out.step(`Initial custodian invitation: ${result2.inviteUrl}`);
18438
+ return 0;
18309
18439
  }
18310
18440
  if (operation === "platform" && args[1] === "remote") {
18311
18441
  const mode = args[2];
@@ -19032,14 +19162,16 @@ function usage() {
19032
19162
  Run typed bootstrap from the operator laptop through
19033
19163
  a pinned host; apply copies and verifies that bundle
19034
19164
  fz bootstrap platform fleet <apply|status>
19035
- Validate the exact three-node genesis identity, prompt
19036
- shared secrets once and run all pinned nodes concurrently
19165
+ One attended genesis operation: reconcile public
19166
+ hostnames/Tunnels, seal credentials, deploy the bundle,
19167
+ run all three nodes, verify ingress and print the invite
19037
19168
  fz bootstrap platform Install/repair a typed elastic platform compute
19038
19169
  fz bootstrap config <metal|platform|cloudflare>
19039
19170
  Write validated owner-only metal config+remote request,
19040
- Cloudflare file, or complete three-node platform request set
19171
+ low-level Cloudflare file, or the complete three-node
19172
+ platform + Cloudflare request set
19041
19173
  fz bootstrap platform cloudflare
19042
- Plan/apply attended Tunnel and DNS reconciliation
19174
+ Low-level resume/diagnostic Tunnel and DNS reconciliation
19043
19175
  using hidden management and KV/Worker runtime token prompts
19044
19176
  fz bootstrap platform cloudflare verify
19045
19177
  From the operator laptop, prove every ordinary public
@@ -354,7 +354,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
354
354
  }
355
355
 
356
356
  // src/version.ts
357
- var VERSION = "0.1.68";
357
+ var VERSION = "0.1.69";
358
358
 
359
359
  // src/otel-collector.ts
360
360
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
@@ -40,6 +40,10 @@ export interface OperatorGuestHostKeyEvidence {
40
40
  export interface OperatorPlatformBootstrapFleetRequest {
41
41
  kind: 'platform-fleet-remote';
42
42
  requests: readonly [string, string, string];
43
+ /** Reviewed non-secret coordinates for the attended Tunnel/DNS/KV/DO phase. */
44
+ cloudflareConfigFile: string;
45
+ /** Secret-free evidence retained after checkpoint and host handoffs are consumed. */
46
+ cloudflareAcceptanceFile: string;
43
47
  }
44
48
  export interface OperatorPlatformBootstrapFleetPlan {
45
49
  kind: 'platform-fleet-remote';
@@ -123,7 +127,10 @@ export declare function createOperatorSshHop(input: Omit<OperatorSshHop, 'hostKe
123
127
  export declare function readOperatorPlatformBootstrapRequest(path: string): OperatorPlatformBootstrapRequest;
124
128
  export declare function writeOperatorPlatformBootstrapRequest(path: string, request: OperatorPlatformBootstrapRequest): string;
125
129
  export declare function readOperatorPlatformBootstrapFleetRequest(path: string): OperatorPlatformBootstrapFleetRequest;
126
- export declare function writeOperatorPlatformBootstrapFleetRequest(path: string, requestFiles: readonly [string, string, string]): string;
130
+ export declare function writeOperatorPlatformBootstrapFleetRequest(path: string, requestFiles: readonly [string, string, string], cloudflare: {
131
+ configFile: string;
132
+ acceptanceFile: string;
133
+ }): string;
127
134
  export declare function readOperatorMetalBootstrapRequest(path: string): OperatorMetalBootstrapRequest;
128
135
  export declare function writeOperatorMetalBootstrapRequest(path: string, request: OperatorMetalBootstrapRequest): string;
129
136
  export declare function readOperatorGuestHostKeyEvidence(path: string, request?: OperatorMetalBootstrapRequest): OperatorGuestHostKeyEvidence;
@@ -1396,7 +1396,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1396
1396
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1397
1397
 
1398
1398
  // src/version.ts
1399
- var VERSION = "0.1.68";
1399
+ var VERSION = "0.1.69";
1400
1400
 
1401
1401
  // src/software.ts
1402
1402
  var PINNED_BUN_VERSION = "1.3.14";
@@ -4380,6 +4380,7 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
4380
4380
  }
4381
4381
  bindBootstrapIntent(host, config);
4382
4382
  const plan = planBootstrap(config, host.exists(STATE_PATH));
4383
+ await checked3(host, ["hostnamectl", "set-hostname", "--static", config.nodeHostname], "compute hostname");
4383
4384
  const alreadyEnrolled = host.exists("/var/lib/forgezero/enrolment.json");
4384
4385
  host.mkdir(CREDS, 448);
4385
4386
  host.mkdir("/var/lib/forgezero", 448);
@@ -5971,7 +5972,12 @@ function writeOperatorPlatformBootstrapRequest(path, request) {
5971
5972
  }
5972
5973
  function readOperatorPlatformBootstrapFleetRequest(path) {
5973
5974
  const value = JSON.parse(new TextDecoder().decode(ownerFile(path, REQUEST_LIMIT, "operator fleet request")));
5974
- const root = exactKeys3(value, ["kind", "requests"], "operator fleet request");
5975
+ const root = exactKeys3(value, [
5976
+ "kind",
5977
+ "requests",
5978
+ "cloudflareConfigFile",
5979
+ "cloudflareAcceptanceFile"
5980
+ ], "operator fleet request");
5975
5981
  if (root.kind !== "platform-fleet-remote")
5976
5982
  throw new Error("operator fleet kind must be platform-fleet-remote");
5977
5983
  if (!Array.isArray(root.requests) || root.requests.length !== 3 || root.requests.some((entry) => typeof entry !== "string")) {
@@ -5982,10 +5988,25 @@ function readOperatorPlatformBootstrapFleetRequest(path) {
5982
5988
  throw new Error("operator fleet request files must be unique");
5983
5989
  for (const request of requests)
5984
5990
  readOperatorPlatformBootstrapRequest(request);
5985
- return { kind: "platform-fleet-remote", requests };
5991
+ if (typeof root.cloudflareConfigFile !== "string" || typeof root.cloudflareAcceptanceFile !== "string") {
5992
+ throw new Error("operator fleet requires Cloudflare coordinates and an acceptance evidence path");
5993
+ }
5994
+ const cloudflareConfigFile = canonicalOutputPath(root.cloudflareConfigFile, "operator fleet Cloudflare config");
5995
+ const cloudflareAcceptanceFile = canonicalOutputPath(root.cloudflareAcceptanceFile, "operator fleet Cloudflare acceptance evidence");
5996
+ return {
5997
+ kind: "platform-fleet-remote",
5998
+ requests,
5999
+ cloudflareConfigFile,
6000
+ cloudflareAcceptanceFile
6001
+ };
5986
6002
  }
5987
- function writeOperatorPlatformBootstrapFleetRequest(path, requestFiles) {
5988
- const request = { kind: "platform-fleet-remote", requests: requestFiles };
6003
+ function writeOperatorPlatformBootstrapFleetRequest(path, requestFiles, cloudflare) {
6004
+ const request = {
6005
+ kind: "platform-fleet-remote",
6006
+ requests: requestFiles,
6007
+ cloudflareConfigFile: canonicalOutputPath(cloudflare.configFile, "operator fleet Cloudflare config"),
6008
+ cloudflareAcceptanceFile: canonicalOutputPath(cloudflare.acceptanceFile, "operator fleet Cloudflare acceptance evidence")
6009
+ };
5989
6010
  if (new Set(requestFiles).size !== 3)
5990
6011
  throw new Error("operator fleet request files must be unique");
5991
6012
  for (const file of requestFiles)
@@ -2125,7 +2125,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2125
2125
  }
2126
2126
 
2127
2127
  // src/version.ts
2128
- var VERSION3 = "0.1.68";
2128
+ var VERSION3 = "0.1.69";
2129
2129
 
2130
2130
  // src/egress-policy.ts
2131
2131
  import { realpathSync as realpathSync3 } from "node:fs";
package/dist/provision.js CHANGED
@@ -2125,7 +2125,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2125
2125
  }
2126
2126
 
2127
2127
  // src/version.ts
2128
- var VERSION3 = "0.1.68";
2128
+ var VERSION3 = "0.1.69";
2129
2129
 
2130
2130
  // src/egress-policy.ts
2131
2131
  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.68";
2
+ export declare const VERSION = "0.1.69";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/agent",
3
- "version": "0.1.68",
3
+ "version": "0.1.69",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",