@forgezero/agent 0.1.62 → 0.1.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/fz-agent.js CHANGED
@@ -2,8 +2,8 @@
2
2
  // @bun
3
3
 
4
4
  // src/index.ts
5
- import { randomBytes as randomBytes6 } from "crypto";
6
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync16, existsSync as existsSync20, mkdirSync as mkdirSync16, chmodSync as chmodSync17, lstatSync as lstatSync8, statfsSync as statfsSync2 } from "fs";
5
+ import { randomBytes as randomBytes7 } from "crypto";
6
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync17, existsSync as existsSync21, mkdirSync as mkdirSync17, chmodSync as chmodSync18, lstatSync as lstatSync9, statfsSync as statfsSync2 } from "fs";
7
7
  import { cpus, totalmem } from "os";
8
8
  import { dirname as dirname12, join as join12 } from "path";
9
9
 
@@ -4641,8 +4641,8 @@ function safeOp(line) {
4641
4641
  }
4642
4642
 
4643
4643
  // src/deployment.ts
4644
- import { chmodSync as chmodSync3, existsSync as existsSync3, lstatSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, statfsSync, writeFileSync as writeFileSync2 } from "fs";
4645
- import { createHash as createHash4, randomUUID } from "crypto";
4644
+ import { chmodSync as chmodSync4, existsSync as existsSync4, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync3, statfsSync, writeFileSync as writeFileSync3 } from "fs";
4645
+ import { createHash as createHash5, randomUUID } from "crypto";
4646
4646
  import { dirname, isAbsolute as isAbsolute2, join as join2 } from "path";
4647
4647
 
4648
4648
  // ../runtime/dist/queue.js
@@ -4980,7 +4980,8 @@ var SOFTWARE_CATALOG = [
4980
4980
  { id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
4981
4981
  { id: "cloudflare-warp", version: "2026.6.822.0-min", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
4982
4982
  { id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
4983
- { id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
4983
+ { id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
4984
+ { id: "git", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
4984
4985
  ];
4985
4986
  var UBUNTU_2604_X64 = [
4986
4987
  { requirement: { id: "bun", version: "1.3.14" } },
@@ -4990,7 +4991,8 @@ var UBUNTU_2604_X64 = [
4990
4991
  { requirement: { id: "cloudflared", version: "2026.7.3" } },
4991
4992
  { requirement: { id: "cloudflare-warp", version: "2026.6.822.0-min" } },
4992
4993
  { requirement: { id: "ufw", version: "ubuntu-26.04" } },
4993
- { requirement: { id: "openssh-client", version: "ubuntu-26.04" } }
4994
+ { requirement: { id: "openssh-client", version: "ubuntu-26.04" } },
4995
+ { requirement: { id: "git", version: "ubuntu-26.04" } }
4994
4996
  ];
4995
4997
  var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
4996
4998
  var DOCKER_DAEMON_PATH = "/etc/docker/daemon.json";
@@ -5077,7 +5079,7 @@ async function executeSoftwareOperation(operation) {
5077
5079
  const supported = observed && observed.some((part, index) => part > minimum[index] && observed.slice(0, index).every((prior, priorIndex) => prior === minimum[priorIndex])) || observed?.every((part, index) => part === minimum[index]);
5078
5080
  return { ...result, exitCode: result.exitCode === 0 && supported ? 0 : 1 };
5079
5081
  });
5080
- const binaries = software === "ufw" ? ["/usr/sbin/ufw"] : ["/usr/bin/ssh", "/usr/bin/scp", "/usr/bin/ssh-keyscan", "/usr/bin/ssh-keygen"];
5082
+ const binaries = software === "ufw" ? ["/usr/sbin/ufw"] : software === "git" ? ["/usr/bin/git"] : ["/usr/bin/ssh", "/usr/bin/scp", "/usr/bin/ssh-keyscan", "/usr/bin/ssh-keygen"];
5081
5083
  try {
5082
5084
  binaries.forEach((binary) => accessSync(binary));
5083
5085
  return { exitCode: 0, output: "" };
@@ -5085,7 +5087,7 @@ async function executeSoftwareOperation(operation) {
5085
5087
  return { exitCode: 1, output: cause instanceof Error ? cause.message : String(cause) };
5086
5088
  }
5087
5089
  }
5088
- if (software === "docker" || software === "nginx" || software === "ufw" || software === "openssh-client") {
5090
+ if (software === "docker" || software === "nginx" || software === "ufw" || software === "openssh-client" || software === "git") {
5089
5091
  const packageName = software === "openssh-client" ? "openssh-client" : software === "docker" ? "docker.io" : software;
5090
5092
  const installed = await aptInstall(packageName);
5091
5093
  if (installed.exitCode !== 0 || software !== "nginx" && software !== "docker")
@@ -5185,7 +5187,7 @@ function validateSoftwareRequirements(value, _options = {}) {
5185
5187
  if (Object.keys(row).some((key) => key !== "id" && key !== "version")) {
5186
5188
  throw new Error("software requirement contains an unknown field");
5187
5189
  }
5188
- if (!["bun", "docker", "nginx", "arangodb", "cloudflared", "cloudflare-warp", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
5190
+ if (!["bun", "docker", "nginx", "arangodb", "cloudflared", "cloudflare-warp", "ufw", "openssh-client", "git"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
5189
5191
  throw new Error("software requirement coordinate is invalid");
5190
5192
  }
5191
5193
  const requirement = { id: row.id, version: row.version };
@@ -7120,6 +7122,65 @@ async function runDeploymentPlan(options) {
7120
7122
  // src/deploy-compiler.ts
7121
7123
  var DEPLOY_PLAN_FILE = ".fz/deploy.plan.json";
7122
7124
 
7125
+ // src/bootstrap-bundle.ts
7126
+ import { createHash as createHash4, randomBytes as randomBytes5 } from "crypto";
7127
+ import {
7128
+ chmodSync as chmodSync3,
7129
+ createReadStream,
7130
+ existsSync as existsSync3,
7131
+ lstatSync,
7132
+ mkdirSync as mkdirSync2,
7133
+ readFileSync as readFileSync2,
7134
+ renameSync as renameSync2,
7135
+ rmSync as rmSync2,
7136
+ writeFileSync as writeFileSync2
7137
+ } from "fs";
7138
+ var BOOTSTRAP_BUNDLE_FORMAT = 1;
7139
+ var BOOTSTRAP_BUNDLE_KIND = "forgezero-api-git-bundle";
7140
+ var MAX_BOOTSTRAP_BUNDLE_BYTES = 512 * 1024 * 1024;
7141
+ async function sha256File(path2) {
7142
+ const hash = createHash4("sha256");
7143
+ await new Promise((resolveDone, reject) => {
7144
+ const stream = createReadStream(path2);
7145
+ stream.on("data", (chunk) => hash.update(chunk));
7146
+ stream.on("error", reject);
7147
+ stream.on("end", resolveDone);
7148
+ });
7149
+ return hash.digest("hex");
7150
+ }
7151
+ var branchName = (value) => {
7152
+ if (typeof value !== "string" || !/^[A-Za-z0-9](?:[A-Za-z0-9._/-]{0,126}[A-Za-z0-9])?$/.test(value) || value.includes("..") || value.includes("//") || value.startsWith("-")) {
7153
+ throw new Error("bootstrap bundle branch is malformed");
7154
+ }
7155
+ return value;
7156
+ };
7157
+ function parseBootstrapBundleManifest(value) {
7158
+ if (!value || typeof value !== "object" || Array.isArray(value))
7159
+ throw new Error("bootstrap bundle manifest must be an object");
7160
+ const source = value;
7161
+ const allowed = ["format", "kind", "branch", "revision", "sha256", "bytes", "createdAt"];
7162
+ const unknown = Object.keys(source).filter((key) => !allowed.includes(key));
7163
+ if (unknown.length)
7164
+ throw new Error(`bootstrap bundle manifest contains unknown field ${unknown[0]}`);
7165
+ if (source.format !== BOOTSTRAP_BUNDLE_FORMAT || source.kind !== BOOTSTRAP_BUNDLE_KIND) {
7166
+ throw new Error("bootstrap bundle manifest format is unsupported");
7167
+ }
7168
+ branchName(source.branch);
7169
+ if (typeof source.revision !== "string" || !/^[a-f0-9]{40}$/.test(source.revision)) {
7170
+ throw new Error("bootstrap bundle revision must be an exact lowercase Git commit");
7171
+ }
7172
+ if (typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256)) {
7173
+ throw new Error("bootstrap bundle digest is malformed");
7174
+ }
7175
+ if (!Number.isSafeInteger(source.bytes) || Number(source.bytes) < 1 || Number(source.bytes) > MAX_BOOTSTRAP_BUNDLE_BYTES) {
7176
+ throw new Error("bootstrap bundle size is outside the supported boundary");
7177
+ }
7178
+ if (typeof source.createdAt !== "string" || Number.isNaN(Date.parse(source.createdAt))) {
7179
+ throw new Error("bootstrap bundle creation time is malformed");
7180
+ }
7181
+ return source;
7182
+ }
7183
+
7123
7184
  // src/deployment.ts
7124
7185
  class DeploymentError extends Error {
7125
7186
  code;
@@ -7165,24 +7226,24 @@ function persistCapacityCalibration(args) {
7165
7226
  if (!isAbsolute2(args.directory)) {
7166
7227
  throw new DeploymentError("PIPELINE_FAILED", "capacity evidence directory must be an absolute Agent-owned path");
7167
7228
  }
7168
- if (!existsSync3(args.directory)) {
7169
- const parent = lstatSync(dirname(args.directory));
7229
+ if (!existsSync4(args.directory)) {
7230
+ const parent = lstatSync2(dirname(args.directory));
7170
7231
  if (!parent.isDirectory() || parent.isSymbolicLink()) {
7171
7232
  throw new DeploymentError("PIPELINE_FAILED", "capacity evidence parent must be a real Agent-owned directory");
7172
7233
  }
7173
- mkdirSync2(args.directory, { mode: 448 });
7234
+ mkdirSync3(args.directory, { mode: 448 });
7174
7235
  }
7175
- const stats = lstatSync(args.directory);
7236
+ const stats = lstatSync2(args.directory);
7176
7237
  if (!stats.isDirectory() || stats.isSymbolicLink()) {
7177
7238
  throw new DeploymentError("PIPELINE_FAILED", "capacity evidence directory must be a real Agent-owned directory");
7178
7239
  }
7179
- const identity = createHash4("sha256").update(`${args.deploymentKey}\x00${args.profile}\x00${args.revision}`).digest("hex").slice(0, 24);
7240
+ const identity = createHash5("sha256").update(`${args.deploymentKey}\x00${args.profile}\x00${args.revision}`).digest("hex").slice(0, 24);
7180
7241
  const path2 = join2(args.directory, `${identity}-${args.measuredAtTs}-${randomUUID().slice(0, 8)}.json`);
7181
7242
  const next = `${path2}.next`;
7182
7243
  const recommendedCoordinate = {
7183
7244
  FZ_CONCURRENCY_LIMIT: String(args.calibration.recommendedConcurrency)
7184
7245
  };
7185
- writeFileSync2(next, `${JSON.stringify({
7246
+ writeFileSync3(next, `${JSON.stringify({
7186
7247
  format: 1,
7187
7248
  kind: "forgezero-node-capacity-calibration",
7188
7249
  deploymentKey: args.deploymentKey,
@@ -7194,22 +7255,29 @@ function persistCapacityCalibration(args) {
7194
7255
  calibration: args.calibration
7195
7256
  }, null, 2)}
7196
7257
  `, { mode: 384, flag: "wx" });
7197
- chmodSync3(next, 384);
7198
- renameSync2(next, path2);
7258
+ chmodSync4(next, 384);
7259
+ renameSync3(next, path2);
7199
7260
  return path2;
7200
7261
  }
7201
7262
  function createDeploymentManager(options) {
7202
- try {
7203
- gitNetworkTarget(options.repository);
7204
- } catch (cause) {
7205
- throw new DeploymentError("SOURCE_FAILED", cause instanceof GitEgressError ? cause.message : "The Git source is malformed.");
7263
+ const bootstrapBundle = options.bootstrapBundle ? { path: options.bootstrapBundle.path, manifest: parseBootstrapBundleManifest(options.bootstrapBundle.manifest) } : undefined;
7264
+ if (bootstrapBundle) {
7265
+ if (!isAbsolute2(bootstrapBundle.path) || bootstrapBundle.path !== options.repository || bootstrapBundle.manifest.branch !== options.branch) {
7266
+ throw new DeploymentError("SOURCE_FAILED", "The bootstrap bundle source coordinates disagree.");
7267
+ }
7268
+ } else {
7269
+ try {
7270
+ gitNetworkTarget(options.repository);
7271
+ } catch (cause) {
7272
+ throw new DeploymentError("SOURCE_FAILED", cause instanceof GitEgressError ? cause.message : "The Git source is malformed.");
7273
+ }
7206
7274
  }
7207
7275
  const queue = createQueue({ width: options.width ?? 4 });
7208
7276
  const activeRevisions = new Map;
7209
7277
  const exec = options.exec ?? spawnExact;
7210
7278
  const projectExec = options.projectExec ?? exec;
7211
7279
  const now = options.now ?? Date.now;
7212
- const readDefinition = options.readDefinition ?? ((path2) => JSON.parse(readFileSync2(path2, "utf8")));
7280
+ const readDefinition = options.readDefinition ?? ((path2) => JSON.parse(readFileSync3(path2, "utf8")));
7213
7281
  const credentialsDirectory = process.env.CREDENTIALS_DIRECTORY;
7214
7282
  const gitCredentialPath = options.gitCredentialPath ?? (credentialsDirectory ? join2(credentialsDirectory, "git-deploy-key") : undefined);
7215
7283
  const knownHostsPath = options.knownHostsPath ?? "/etc/forgezero/git/known_hosts";
@@ -7221,13 +7289,13 @@ function createDeploymentManager(options) {
7221
7289
  if (content.length > 16385 || /\r|\0/.test(content)) {
7222
7290
  throw new DeploymentError("SOURCE_FAILED", "The pinned Git host keys are malformed.");
7223
7291
  }
7224
- if (existsSync3(knownHostsPath) && readFileSync2(knownHostsPath, "utf8") === content)
7292
+ if (existsSync4(knownHostsPath) && readFileSync3(knownHostsPath, "utf8") === content)
7225
7293
  return;
7226
- mkdirSync2(dirname(knownHostsPath), { recursive: true, mode: 448 });
7294
+ mkdirSync3(dirname(knownHostsPath), { recursive: true, mode: 448 });
7227
7295
  const next = `${knownHostsPath}.${process.pid}.${randomUUID()}.next`;
7228
- writeFileSync2(next, content, { mode: 384, flag: "wx" });
7229
- renameSync2(next, knownHostsPath);
7230
- chmodSync3(knownHostsPath, 384);
7296
+ writeFileSync3(next, content, { mode: 384, flag: "wx" });
7297
+ renameSync3(next, knownHostsPath);
7298
+ chmodSync4(knownHostsPath, 384);
7231
7299
  };
7232
7300
  const gitBaseEnvironment = () => ({
7233
7301
  GIT_TERMINAL_PROMPT: "0",
@@ -7264,6 +7332,8 @@ function createDeploymentManager(options) {
7264
7332
  return httpsGitVersion;
7265
7333
  };
7266
7334
  const gitEnvironment = async () => {
7335
+ if (bootstrapBundle)
7336
+ return gitBaseEnvironment();
7267
7337
  let target;
7268
7338
  try {
7269
7339
  target = await resolvePinnedGitTarget(options.repository, options.resolveGitHost);
@@ -7285,22 +7355,25 @@ function createDeploymentManager(options) {
7285
7355
  ["http.curloptResolve", curlResolveValue(target)]
7286
7356
  ]);
7287
7357
  }
7288
- if (auth.kind === "vault-token") {
7358
+ if (auth.kind === "vault-token" || auth.kind === "ephemeral-token") {
7289
7359
  if (!https)
7290
7360
  throw new DeploymentError("SOURCE_FAILED", "Vault Git tokens may only be sent to HTTPS sources.");
7291
- if (!/^[A-Z_][A-Z0-9_]{0,127}$/.test(auth.secret)) {
7361
+ if (auth.kind === "vault-token" && !/^[A-Z_][A-Z0-9_]{0,127}$/.test(auth.secret)) {
7292
7362
  throw new DeploymentError("SOURCE_FAILED", "The Git token secret name is invalid.");
7293
7363
  }
7294
- if (!options.cache) {
7364
+ if (auth.kind === "vault-token" && !options.cache) {
7295
7365
  throw new DeploymentError("SECRET_MISSING", `Git source credential ${auth.secret} is unavailable.`);
7296
7366
  }
7297
7367
  const username = auth.username ?? "x-access-token";
7298
7368
  if (!/^[A-Za-z0-9._@+-]{1,128}$/.test(username)) {
7299
7369
  throw new DeploymentError("SOURCE_FAILED", "The Git token username is invalid.");
7300
7370
  }
7301
- const token = await options.cache.get(auth.secret);
7371
+ if (auth.kind === "ephemeral-token" && auth.expiresAtTs <= Date.now() + 30000) {
7372
+ throw new DeploymentError("SOURCE_FAILED", "The one-claim Git credential is expired or too close to expiry.");
7373
+ }
7374
+ const token = auth.kind === "ephemeral-token" ? auth.token : await options.cache.get(auth.secret);
7302
7375
  if (!token || token.length > 8192 || /[\r\n\0]/.test(token)) {
7303
- throw new DeploymentError("SOURCE_FAILED", `Git source credential ${auth.secret} is malformed.`);
7376
+ throw new DeploymentError("SOURCE_FAILED", "The Git source credential is malformed.");
7304
7377
  }
7305
7378
  const origin = new URL(options.repository).origin;
7306
7379
  return withGitConfig(gitBaseEnvironment(), [
@@ -7344,11 +7417,26 @@ function createDeploymentManager(options) {
7344
7417
  await checked({ argv: ["/usr/bin/test", "-d", releasesDirectory] }, "SOURCE_FAILED");
7345
7418
  await checked({ argv: ["/usr/bin/test", "-w", releasesDirectory] }, "SOURCE_FAILED");
7346
7419
  await checked({ argv: ["/usr/bin/install", "-d", "-m", "0770", release] }, "SOURCE_FAILED");
7420
+ if (bootstrapBundle) {
7421
+ let metadata;
7422
+ try {
7423
+ metadata = lstatSync2(bootstrapBundle.path);
7424
+ } catch {
7425
+ throw new DeploymentError("SOURCE_FAILED", "The attended bootstrap bundle is missing.");
7426
+ }
7427
+ if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.nlink !== 1 || metadata.size !== bootstrapBundle.manifest.bytes || await sha256File(bootstrapBundle.path) !== bootstrapBundle.manifest.sha256) {
7428
+ throw new DeploymentError("SOURCE_FAILED", "The attended bootstrap bundle failed its manifest check.");
7429
+ }
7430
+ await checked({ argv: ["git", "bundle", "verify", bootstrapBundle.path], env }, "SOURCE_FAILED");
7431
+ }
7347
7432
  await checked({
7348
7433
  argv: ["git", "clone", "--quiet", "--no-checkout", "--branch", options.branch, "--depth", "100", options.repository, release],
7349
7434
  env
7350
7435
  }, "SOURCE_FAILED");
7351
- if (request.revision) {
7436
+ if (bootstrapBundle && request.revision && request.revision !== bootstrapBundle.manifest.revision) {
7437
+ throw new DeploymentError("BAD_REVISION", "The requested revision is not the attended bootstrap bundle revision.");
7438
+ }
7439
+ if (request.revision && !bootstrapBundle) {
7352
7440
  let present = await exec({
7353
7441
  argv: ["git", "cat-file", "-e", `${request.revision}^{commit}`],
7354
7442
  cwd: release,
@@ -7371,17 +7459,18 @@ function createDeploymentManager(options) {
7371
7459
  env
7372
7460
  }, "SOURCE_FAILED");
7373
7461
  }
7462
+ const desiredRevision = request.revision ?? bootstrapBundle?.manifest.revision ?? `origin/${options.branch}`;
7374
7463
  await checked({
7375
- argv: ["git", "checkout", "--quiet", "--detach", request.revision ?? `origin/${options.branch}`],
7464
+ argv: ["git", "checkout", "--quiet", "--detach", desiredRevision],
7376
7465
  cwd: release,
7377
7466
  env
7378
7467
  }, "SOURCE_FAILED");
7379
7468
  const head = (await checked({ argv: ["git", "rev-parse", "HEAD"], cwd: release, env }, "SOURCE_FAILED")).output.trim();
7380
- if (request.revision && head !== request.revision) {
7381
- throw new DeploymentError("SOURCE_FAILED", `Git checked out ${head}, not requested ${request.revision}.`);
7469
+ if ((request.revision || bootstrapBundle) && head !== desiredRevision) {
7470
+ throw new DeploymentError("SOURCE_FAILED", `Git checked out ${head}, not requested ${desiredRevision}.`);
7382
7471
  }
7383
7472
  const planPath = join2(release, DEPLOY_PLAN_FILE);
7384
- if (existsSync3(planPath)) {
7473
+ if (existsSync4(planPath)) {
7385
7474
  const plan = parseDeploymentPlan(readDefinition(planPath));
7386
7475
  for (const workflow of Object.values(plan.spec.workflows))
7387
7476
  for (const stage2 of Object.values(workflow.stages)) {
@@ -7416,10 +7505,28 @@ function createDeploymentManager(options) {
7416
7505
  const inputValues = {};
7417
7506
  for (const [name, definition2] of Object.entries(plan.spec.inputs ?? {})) {
7418
7507
  const raw = options.environment?.[name];
7419
- if (raw === undefined)
7508
+ if (raw === undefined) {
7509
+ if (definition2.default !== undefined)
7510
+ inputValues[name] = definition2.default;
7420
7511
  continue;
7512
+ }
7421
7513
  inputValues[name] = definition2.type === "boolean" ? raw === "true" : definition2.type === "integer" || definition2.type === "number" ? Number(raw) : raw;
7422
7514
  }
7515
+ const resolvedTargets = Object.entries(plan.spec.targets).filter(([, target]) => target.selector.profiles.includes(options.profile)).map(([name, target]) => {
7516
+ const desired = typeof target.cardinality.desired === "number" ? target.cardinality.desired : target.cardinality.desired.$ref.startsWith("inputs.") ? inputValues[target.cardinality.desired.$ref.slice("inputs.".length)] : undefined;
7517
+ if (!Number.isSafeInteger(desired) || Number(desired) < target.cardinality.minimum || Number(desired) > target.cardinality.maximum) {
7518
+ throw new DeploymentError("PIPELINE_FAILED", `target ${name} desired cardinality did not resolve to a bounded integer`);
7519
+ }
7520
+ return {
7521
+ name,
7522
+ profiles: [...target.selector.profiles],
7523
+ confidentialCompute: target.selector.confidentialCompute ?? "disabled",
7524
+ labels: { ...target.selector.labels ?? {} },
7525
+ minimum: target.cardinality.minimum,
7526
+ desired: Number(desired),
7527
+ maximum: target.cardinality.maximum
7528
+ };
7529
+ });
7423
7530
  const projectExec2 = options.projectExec ?? spawnExact;
7424
7531
  const materializePlanArgv = (argv2) => argv2.map((argument) => {
7425
7532
  if (argument === "${FZ_RELEASE}")
@@ -7498,7 +7605,7 @@ function createDeploymentManager(options) {
7498
7605
  const component2 = plan.spec.components[String(resolved.component ?? "")];
7499
7606
  const mounts = component2?.kind === "database" ? [component2.storage] : component2?.storage ?? [];
7500
7607
  for (const mount of mounts) {
7501
- const candidate = existsSync3(mount.path) ? mount.path : dirname(mount.path);
7608
+ const candidate = existsSync4(mount.path) ? mount.path : dirname(mount.path);
7502
7609
  const stats = statfsSync(candidate);
7503
7610
  const freePercent = Number(stats.bavail) / Number(stats.blocks) * 100;
7504
7611
  const minimum = mount.class === "ephemeral" ? 0 : mount.minimumFreePercent ?? 0;
@@ -7579,11 +7686,12 @@ function createDeploymentManager(options) {
7579
7686
  branch: options.branch,
7580
7687
  revision: head,
7581
7688
  definitionDigest: plan.sourceDigest,
7582
- definitionVersion: plan.version,
7689
+ definitionVersion: 2,
7583
7690
  profile: options.profile,
7584
7691
  release,
7585
7692
  ok: true,
7586
- phases: [phase]
7693
+ phases: [phase],
7694
+ resolvedTargets
7587
7695
  };
7588
7696
  }
7589
7697
  const definition = parseDeployDefinition(readDefinition(join2(release, ".fz", "deploy.json")));
@@ -7696,7 +7804,7 @@ function createDeploymentManager(options) {
7696
7804
  branch: options.branch,
7697
7805
  revision: head,
7698
7806
  definitionDigest,
7699
- definitionVersion: definition.version,
7807
+ definitionVersion: 2,
7700
7808
  profile: options.profile,
7701
7809
  release,
7702
7810
  ok: true,
@@ -7706,6 +7814,8 @@ function createDeploymentManager(options) {
7706
7814
  };
7707
7815
  return {
7708
7816
  async latestRevision() {
7817
+ if (bootstrapBundle)
7818
+ return bootstrapBundle.manifest.revision;
7709
7819
  const result = await checked({
7710
7820
  argv: ["git", "ls-remote", "--exit-code", options.repository, `refs/heads/${options.branch}`],
7711
7821
  env: await gitEnvironment()
@@ -7750,7 +7860,7 @@ function createDeploymentManager(options) {
7750
7860
  }
7751
7861
 
7752
7862
  // src/control.ts
7753
- import { chmodSync as chmodSync4, existsSync as existsSync4, unlinkSync as unlinkSync3 } from "fs";
7863
+ import { chmodSync as chmodSync5, existsSync as existsSync5, unlinkSync as unlinkSync3 } from "fs";
7754
7864
  import { connect, createServer as createServer2 } from "net";
7755
7865
  var DEFAULT_CONTROL_SOCKET = "/run/forgezero/control.sock";
7756
7866
  var MAX_REQUEST_BYTES = 16 * 1024;
@@ -7807,7 +7917,7 @@ async function handleControl(manager, request) {
7807
7917
  }
7808
7918
  }
7809
7919
  function startControlServer(manager, socketPath = DEFAULT_CONTROL_SOCKET) {
7810
- if (existsSync4(socketPath))
7920
+ if (existsSync5(socketPath))
7811
7921
  unlinkSync3(socketPath);
7812
7922
  const server = createServer2((socket) => {
7813
7923
  let buffer = "";
@@ -7829,7 +7939,7 @@ function startControlServer(manager, socketPath = DEFAULT_CONTROL_SOCKET) {
7829
7939
  });
7830
7940
  socket.on("error", () => socket.destroy());
7831
7941
  });
7832
- server.listen(socketPath, () => chmodSync4(socketPath, 384));
7942
+ server.listen(socketPath, () => chmodSync5(socketPath, 384));
7833
7943
  return server;
7834
7944
  }
7835
7945
  function requestControl(request, socketPath = DEFAULT_CONTROL_SOCKET) {
@@ -7868,6 +7978,7 @@ async function postSigned(options, operation, body) {
7868
7978
  }
7869
7979
  async function deployClaim(options, claim) {
7870
7980
  const manager = options.manager ?? options.managerFor?.(claim);
7981
+ const claimOwnedManager = !options.manager && claim.source.auth?.kind === "ephemeral-token";
7871
7982
  if (!manager)
7872
7983
  throw new Error("deployment pull has no manager for this claim");
7873
7984
  const setTimer = options.setTimer ?? ((callback, ms) => setTimeout(callback, ms));
@@ -7918,6 +8029,8 @@ async function deployClaim(options, claim) {
7918
8029
  stopped = true;
7919
8030
  clearTimer(timer);
7920
8031
  await renewal;
8032
+ if (claimOwnedManager)
8033
+ await manager.stop();
7921
8034
  }
7922
8035
  if (claimLost)
7923
8036
  throw claimLost;
@@ -7977,6 +8090,11 @@ async function pullDeploymentOnce(options) {
7977
8090
  definitionDigest: result.definitionDigest,
7978
8091
  definitionVersion: result.definitionVersion,
7979
8092
  profile: result.profile,
8093
+ ...result.resolvedTargets ? { resolvedTargets: result.resolvedTargets.map((target) => ({
8094
+ ...target,
8095
+ profiles: [...target.profiles],
8096
+ labels: { ...target.labels }
8097
+ })) } : {},
7980
8098
  ...result.capacity ? { capacity: {
7981
8099
  recommendedConcurrency: result.capacity.recommendedConcurrency,
7982
8100
  stopReason: result.capacity.stopReason,
@@ -8026,14 +8144,14 @@ function startDeploymentPull(options) {
8026
8144
 
8027
8145
  // src/guest-enrolment.ts
8028
8146
  import {
8029
- chmodSync as chmodSync5,
8030
- existsSync as existsSync5,
8031
- mkdirSync as mkdirSync3,
8032
- readFileSync as readFileSync3,
8033
- renameSync as renameSync3,
8147
+ chmodSync as chmodSync6,
8148
+ existsSync as existsSync6,
8149
+ mkdirSync as mkdirSync4,
8150
+ readFileSync as readFileSync4,
8151
+ renameSync as renameSync4,
8034
8152
  statSync,
8035
8153
  unlinkSync as unlinkSync4,
8036
- writeFileSync as writeFileSync3
8154
+ writeFileSync as writeFileSync4
8037
8155
  } from "fs";
8038
8156
  import { dirname as dirname2 } from "path";
8039
8157
  function privateNetworkAttachmentFromEnvironment(env = process.env) {
@@ -8063,7 +8181,7 @@ var validBinding = (value, expectedNodeKey) => {
8063
8181
  return ["nodeKey", "computeReference", "projectKey", "environmentKey"].every((key) => typeof row2[key] === "string" && row2[key].length > 0) && (row2.realm === "platform" || row2.realm === "tenant" && typeof row2.tenantSlug === "string" && row2.tenantSlug.length > 0) && (!expectedNodeKey || row2.nodeKey === expectedNodeKey);
8064
8182
  };
8065
8183
  function loadGuestBinding(path2, expectedNodeKey) {
8066
- if (!existsSync5(path2))
8184
+ if (!existsSync6(path2))
8067
8185
  return null;
8068
8186
  const mode = statSync(path2).mode & 511;
8069
8187
  if ((mode & 63) !== 0) {
@@ -8071,7 +8189,7 @@ function loadGuestBinding(path2, expectedNodeKey) {
8071
8189
  }
8072
8190
  let parsed;
8073
8191
  try {
8074
- parsed = JSON.parse(readFileSync3(path2, "utf8"));
8192
+ parsed = JSON.parse(readFileSync4(path2, "utf8"));
8075
8193
  } catch {
8076
8194
  throw new Error(`guest enrolment state at ${path2} is malformed`);
8077
8195
  }
@@ -8081,15 +8199,15 @@ function loadGuestBinding(path2, expectedNodeKey) {
8081
8199
  return parsed;
8082
8200
  }
8083
8201
  function persistGuestBinding(path2, binding) {
8084
- mkdirSync3(dirname2(path2), { recursive: true, mode: 448 });
8202
+ mkdirSync4(dirname2(path2), { recursive: true, mode: 448 });
8085
8203
  const temporary = `${path2}.next`;
8086
- writeFileSync3(temporary, `${JSON.stringify(binding)}
8204
+ writeFileSync4(temporary, `${JSON.stringify(binding)}
8087
8205
  `, { mode: 384 });
8088
- chmodSync5(temporary, 384);
8089
- renameSync3(temporary, path2);
8206
+ chmodSync6(temporary, 384);
8207
+ renameSync4(temporary, path2);
8090
8208
  }
8091
8209
  async function enrolGuestIdentity(options) {
8092
- const token = options.token?.trim() ?? (options.tokenPath ? readFileSync3(options.tokenPath, "utf8").trim() : "");
8210
+ const token = options.token?.trim() ?? (options.tokenPath ? readFileSync4(options.tokenPath, "utf8").trim() : "");
8093
8211
  if (!token.startsWith("fze_"))
8094
8212
  throw new Error("guest enrolment credential is malformed");
8095
8213
  const payload = await postSignedNode({
@@ -8426,8 +8544,8 @@ function startMigrationPull(options) {
8426
8544
  }
8427
8545
 
8428
8546
  // src/ssh-bootstrap.ts
8429
- import { createHash as createHash5 } from "crypto";
8430
- import { chmodSync as chmodSync6, mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
8547
+ import { createHash as createHash6 } from "crypto";
8548
+ import { chmodSync as chmodSync7, mkdtempSync as mkdtempSync2, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
8431
8549
  import { tmpdir as tmpdir2 } from "os";
8432
8550
  import { join as join3 } from "path";
8433
8551
  import { fileURLToPath } from "url";
@@ -8448,7 +8566,7 @@ function assertSupportedGuestImage(imageKey) {
8448
8566
  }
8449
8567
 
8450
8568
  // src/version.ts
8451
- var VERSION2 = "0.1.62";
8569
+ var VERSION2 = "0.1.64";
8452
8570
 
8453
8571
  // src/ssh-bootstrap.ts
8454
8572
  class SshBootstrapError extends Error {
@@ -8566,7 +8684,7 @@ async function pinnedKnownHost(claim, pinned, directory, exec) {
8566
8684
  if (!/^ssh-ed25519 [A-Za-z0-9+/]+={0,3}$/.test(key))
8567
8685
  throw new SshBootstrapError("HOST_KEY_INVALID");
8568
8686
  const path2 = join3(directory, "known_hosts");
8569
- writeFileSync4(path2, `${pinned.hostKeyAlias} ${key}
8687
+ writeFileSync5(path2, `${pinned.hostKeyAlias} ${key}
8570
8688
  `, { mode: 384, flag: "wx" });
8571
8689
  return path2;
8572
8690
  }
@@ -8576,11 +8694,11 @@ var verifiedBunArchive = async (directory) => {
8576
8694
  if (!response.ok)
8577
8695
  throw new SshBootstrapError("BUN_DOWNLOAD_FAILED");
8578
8696
  const bytes = new Uint8Array(await response.arrayBuffer());
8579
- if (createHash5("sha256").update(bytes).digest("hex") !== BUN_RELEASE_SHA256) {
8697
+ if (createHash6("sha256").update(bytes).digest("hex") !== BUN_RELEASE_SHA256) {
8580
8698
  throw new SshBootstrapError("BUN_CHECKSUM_MISMATCH");
8581
8699
  }
8582
8700
  const archive = join3(directory, "bun.zip");
8583
- writeFileSync4(archive, bytes, { mode: 384, flag: "wx" });
8701
+ writeFileSync5(archive, bytes, { mode: 384, flag: "wx" });
8584
8702
  return archive;
8585
8703
  };
8586
8704
  var remote = (exec, ssh, destination, code, argv2, secret = false, stdin) => checked(exec, ["ssh", ...ssh, destination, "--", ...argv2], code, { secret, ...stdin !== undefined ? { stdin } : {} });
@@ -8596,14 +8714,14 @@ async function executeSshBootstrap(claim, options) {
8596
8714
  throw new SshBootstrapError("TARGET_API_INVALID");
8597
8715
  const exec = options.exec ?? defaultExec;
8598
8716
  const directory = mkdtempSync2(join3(tmpdir2(), "forgezero-ssh-bootstrap-"));
8599
- chmodSync6(directory, 448);
8717
+ chmodSync7(directory, 448);
8600
8718
  let sshKeyPath = options.sshKeyPath;
8601
8719
  let remoteDirectory = "";
8602
8720
  let pinned;
8603
8721
  try {
8604
8722
  if (claim.target.credential.source === "vault") {
8605
8723
  sshKeyPath = join3(directory, "identity");
8606
- writeFileSync4(sshKeyPath, `${claim.target.credential.privateKey.trim()}
8724
+ writeFileSync5(sshKeyPath, `${claim.target.credential.privateKey.trim()}
8607
8725
  `, { mode: 384, flag: "wx" });
8608
8726
  }
8609
8727
  const sourceHost = claim.target.host.includes(":") ? `[${claim.target.host}]` : claim.target.host;
@@ -8630,11 +8748,11 @@ async function executeSshBootstrap(claim, options) {
8630
8748
  `
8631
8749
  };
8632
8750
  for (const [name, content] of Object.entries(files))
8633
- writeFileSync4(join3(directory, name), content, { mode: 384, flag: "wx" });
8751
+ writeFileSync5(join3(directory, name), content, { mode: 384, flag: "wx" });
8634
8752
  const fzCliPath = options.fzCliPath ?? fileURLToPath(new URL("./fz.js", import.meta.url));
8635
8753
  const fzAgentPath = options.fzAgentPath ?? fileURLToPath(new URL("./fz-agent.js", import.meta.url));
8636
8754
  for (const [source, name] of [[fzCliPath, "fz.js"], [fzAgentPath, "fz-agent.js"]]) {
8637
- if (!readFileSync4(source).length)
8755
+ if (!readFileSync5(source).length)
8638
8756
  throw new SshBootstrapError("PACKAGE_ARTIFACT_MISSING");
8639
8757
  await checked(exec, [
8640
8758
  "scp",
@@ -8798,7 +8916,7 @@ async function executeSshBootstrap(claim, options) {
8798
8916
  ]);
8799
8917
  } catch {}
8800
8918
  }
8801
- rmSync2(directory, { recursive: true, force: true });
8919
+ rmSync3(directory, { recursive: true, force: true });
8802
8920
  }
8803
8921
  }
8804
8922
  var post3 = (options, operation, body) => postSignedNode(options, `v1/node/bootstrap/${operation}`, body);
@@ -8908,14 +9026,14 @@ async function executeMetalAdmission(claim, options) {
8908
9026
  }
8909
9027
  const exec = options.exec ?? defaultExec;
8910
9028
  const directory = mkdtempSync2(join3(tmpdir2(), "forgezero-metal-admission-"));
8911
- chmodSync6(directory, 448);
9029
+ chmodSync7(directory, 448);
8912
9030
  let sshKeyPath = options.sshKeyPath;
8913
9031
  let remoteDirectory = "";
8914
9032
  let pinned;
8915
9033
  try {
8916
9034
  if (claim.target.credential.source === "vault") {
8917
9035
  sshKeyPath = join3(directory, "identity");
8918
- writeFileSync4(sshKeyPath, `${claim.target.credential.privateKey.trim()}
9036
+ writeFileSync5(sshKeyPath, `${claim.target.credential.privateKey.trim()}
8919
9037
  `, { mode: 384, flag: "wx" });
8920
9038
  }
8921
9039
  const sourceHost = claim.target.host.includes(":") ? `[${claim.target.host}]` : claim.target.host;
@@ -8932,7 +9050,7 @@ async function executeMetalAdmission(claim, options) {
8932
9050
  const fzCliPath = options.fzCliPath ?? fileURLToPath(new URL("./fz.js", import.meta.url));
8933
9051
  const fzAgentPath = options.fzAgentPath ?? fileURLToPath(new URL("./fz-agent.js", import.meta.url));
8934
9052
  for (const [source, name] of [[fzCliPath, "fz.js"], [fzAgentPath, "fz-agent.js"]]) {
8935
- if (!readFileSync4(source).length)
9053
+ if (!readFileSync5(source).length)
8936
9054
  throw new SshBootstrapError("PACKAGE_ARTIFACT_MISSING");
8937
9055
  await checked(exec, [
8938
9056
  "scp",
@@ -8963,7 +9081,7 @@ async function executeMetalAdmission(claim, options) {
8963
9081
  }
8964
9082
  };
8965
9083
  const configPath = join3(directory, "metal.json");
8966
- writeFileSync4(configPath, `${JSON.stringify(config, null, 2)}
9084
+ writeFileSync5(configPath, `${JSON.stringify(config, null, 2)}
8967
9085
  `, { mode: 384, flag: "wx" });
8968
9086
  await checked(exec, [
8969
9087
  "scp",
@@ -9065,7 +9183,7 @@ async function executeMetalAdmission(claim, options) {
9065
9183
  ]);
9066
9184
  } catch {}
9067
9185
  }
9068
- rmSync2(directory, { recursive: true, force: true });
9186
+ rmSync3(directory, { recursive: true, force: true });
9069
9187
  }
9070
9188
  }
9071
9189
  async function pullMetalAdmissionOnce(options) {
@@ -9150,20 +9268,20 @@ function startMetalAdmissionPull(options) {
9150
9268
  }
9151
9269
 
9152
9270
  // src/metal-helper-socket.ts
9153
- import { chmodSync as chmodSync7, existsSync as existsSync7, unlinkSync as unlinkSync6 } from "fs";
9271
+ import { chmodSync as chmodSync8, existsSync as existsSync8, unlinkSync as unlinkSync6 } from "fs";
9154
9272
  import { connect as connect2, createServer as createServer3 } from "net";
9155
9273
 
9156
9274
  // src/metal-provision.ts
9157
- import { createHash as createHash6 } from "crypto";
9275
+ import { createHash as createHash7 } from "crypto";
9158
9276
  import {
9159
- existsSync as existsSync6,
9160
- mkdirSync as mkdirSync4,
9161
- readFileSync as readFileSync5,
9277
+ existsSync as existsSync7,
9278
+ mkdirSync as mkdirSync5,
9279
+ readFileSync as readFileSync6,
9162
9280
  readdirSync,
9163
- rmSync as rmSync3,
9281
+ rmSync as rmSync4,
9164
9282
  statSync as statSync2,
9165
9283
  unlinkSync as unlinkSync5,
9166
- writeFileSync as writeFileSync5
9284
+ writeFileSync as writeFileSync6
9167
9285
  } from "fs";
9168
9286
  import { dirname as dirname3, isAbsolute as isAbsolute3, join as join4 } from "path";
9169
9287
  import { isIP as isIP2 } from "net";
@@ -9293,8 +9411,8 @@ function membersOfLinuxList(value, label) {
9293
9411
  throw new MetalProvisionError(`${label} list overlaps itself`);
9294
9412
  return members;
9295
9413
  }
9296
- var guestNameFor = (computeKey) => `fzg-${createHash6("sha256").update(computeKey).digest("hex").slice(0, 16)}`;
9297
- var tapNameFor = (computeKey) => `fzt${createHash6("sha256").update(computeKey).digest("hex").slice(0, 12)}`;
9414
+ var guestNameFor = (computeKey) => `fzg-${createHash7("sha256").update(computeKey).digest("hex").slice(0, 16)}`;
9415
+ var tapNameFor = (computeKey) => `fzt${createHash7("sha256").update(computeKey).digest("hex").slice(0, 12)}`;
9298
9416
  var macForAddress = (address) => {
9299
9417
  const octets = address.split(".").map(Number);
9300
9418
  if (octets.length !== 4 || octets.some((value) => !Number.isInteger(value) || value < 0 || value > 255)) {
@@ -9374,9 +9492,9 @@ function validateMetalProfile(profile) {
9374
9492
  }
9375
9493
  }
9376
9494
  var readManifests = (stateDir) => {
9377
- if (!existsSync6(stateDir))
9495
+ if (!existsSync7(stateDir))
9378
9496
  return [];
9379
- return readdirSync(stateDir).filter((name) => name.endsWith(".json")).map((name) => JSON.parse(readFileSync5(join4(stateDir, name), "utf8")));
9497
+ return readdirSync(stateDir).filter((name) => name.endsWith(".json")).map((name) => JSON.parse(readFileSync6(join4(stateDir, name), "utf8")));
9380
9498
  };
9381
9499
  function allocateAddress(profile, computeKey, rows) {
9382
9500
  const existing = rows.find((row2) => row2.computeKey === computeKey);
@@ -9384,7 +9502,7 @@ function allocateAddress(profile, computeKey, rows) {
9384
9502
  return existing.address;
9385
9503
  const used = new Set(rows.map((row2) => row2.address));
9386
9504
  const width = profile.addressEnd - profile.addressStart + 1;
9387
- const start = createHash6("sha256").update(computeKey).digest().readUInt16BE(0) % width;
9505
+ const start = createHash7("sha256").update(computeKey).digest().readUInt16BE(0) % width;
9388
9506
  for (let offset = 0;offset < width; offset += 1) {
9389
9507
  const last = profile.addressStart + (start + offset) % width;
9390
9508
  const address = `${profile.subnetPrefix}.${last}`;
@@ -9480,8 +9598,6 @@ function guestBootstrapOperations(profile, attested = Boolean(profile.confidenti
9480
9598
  "FZ_AGENT_USER=forgezero-agent",
9481
9599
  "FZ_SOCKET_PATH=/run/forgezero/vault.sock",
9482
9600
  "FZ_SEED_CREDENTIAL_PATH=/etc/forgezero/creds/agent-seed.cred",
9483
- "FZ_GIT_CREDENTIAL_PATH=/etc/forgezero/creds/git-deploy-key.cred",
9484
- "FZ_GIT_PUBLIC_KEY_PATH=/etc/forgezero/git/deploy.pub",
9485
9601
  "FZ_DEPLOY_ROOT=/opt/forgezero",
9486
9602
  `FZ_DEPLOY_PULL=${hasEnrolment}`,
9487
9603
  `FZ_AGENT_EGRESS_ENFORCE=${hasEnrolment}`,
@@ -9561,7 +9677,7 @@ async function provisionMetalGuest(profile, claim, exec) {
9561
9677
  if (digest !== image.sha256)
9562
9678
  throw new MetalProvisionError("configured image checksum mismatch");
9563
9679
  for (const path2 of [profile.stateDir, profile.seedDir, profile.unitDir])
9564
- mkdirSync4(path2, { recursive: true, mode: 448 });
9680
+ mkdirSync5(path2, { recursive: true, mode: 448 });
9565
9681
  const manifests = readManifests(profile.stateDir);
9566
9682
  if (claim.access) {
9567
9683
  if (!/^[a-z_][a-z0-9_-]{0,31}$/.test(claim.access.sshUser)) {
@@ -9591,7 +9707,7 @@ async function provisionMetalGuest(profile, claim, exec) {
9591
9707
  allowedMemoryNodes: cpuPool.memoryNodes,
9592
9708
  phase: "allocating"
9593
9709
  };
9594
- const save = () => writeFileSync5(manifestPath, `${JSON.stringify(manifest, null, 2)}
9710
+ const save = () => writeFileSync6(manifestPath, `${JSON.stringify(manifest, null, 2)}
9595
9711
  `, { mode: 384 });
9596
9712
  save();
9597
9713
  const lv = `/dev/${profile.volumeGroup}/${name}`;
@@ -9605,9 +9721,9 @@ async function provisionMetalGuest(profile, claim, exec) {
9605
9721
  }
9606
9722
  const seedBase = join4(profile.seedDir, name);
9607
9723
  const init = cloudInit(profile, claim, manifest);
9608
- writeFileSync5(`${seedBase}-user-data`, init.userData, { mode: 384 });
9609
- writeFileSync5(`${seedBase}-meta-data`, init.metaData, { mode: 384 });
9610
- writeFileSync5(`${seedBase}-network-config`, init.networkConfig, { mode: 384 });
9724
+ writeFileSync6(`${seedBase}-user-data`, init.userData, { mode: 384 });
9725
+ writeFileSync6(`${seedBase}-meta-data`, init.metaData, { mode: 384 });
9726
+ writeFileSync6(`${seedBase}-network-config`, init.networkConfig, { mode: 384 });
9611
9727
  const seed = `${seedBase}-seed.iso`;
9612
9728
  await checked2(exec, [
9613
9729
  "cloud-localds",
@@ -9640,8 +9756,8 @@ async function provisionMetalGuest(profile, claim, exec) {
9640
9756
  }
9641
9757
  const service = `forgezero-guest@${name}.service`;
9642
9758
  const unitPath = join4(profile.unitDir, service);
9643
- mkdirSync4(dirname3(unitPath), { recursive: true });
9644
- writeFileSync5(unitPath, guestUnit(spec), { mode: 420 });
9759
+ mkdirSync5(dirname3(unitPath), { recursive: true });
9760
+ writeFileSync6(unitPath, guestUnit(spec), { mode: 420 });
9645
9761
  await checked2(exec, ["systemctl", "daemon-reload"]);
9646
9762
  if (prior?.phase === "running") {
9647
9763
  await checked2(exec, ["systemctl", "restart", service]);
@@ -9658,10 +9774,10 @@ function legacyPlatformManifest(profile, claim, name) {
9658
9774
  return;
9659
9775
  const legacyDir = profile.legacyStateDir ?? "/etc/forgezero/guests";
9660
9776
  const legacyPath = join4(legacyDir, `${name}.conf`);
9661
- if (!existsSync6(legacyPath))
9777
+ if (!existsSync7(legacyPath))
9662
9778
  return;
9663
9779
  const values = new Map;
9664
- for (const line of readFileSync5(legacyPath, "utf8").split(/\r?\n/)) {
9780
+ for (const line of readFileSync6(legacyPath, "utf8").split(/\r?\n/)) {
9665
9781
  if (!line || line.startsWith("#"))
9666
9782
  continue;
9667
9783
  const match = /^([A-Z][A-Z0-9_]*)=([^\s'"`$;|&<>]+)$/.exec(line);
@@ -9695,7 +9811,7 @@ async function removeMetalGuest(profile, claim, exec) {
9695
9811
  const service = `forgezero-guest@${name}.service`;
9696
9812
  const unitPath = join4(profile.unitDir, service);
9697
9813
  const lv = `/dev/${profile.volumeGroup}/${name}`;
9698
- const manifest = existsSync6(manifestPath) ? JSON.parse(readFileSync5(manifestPath, "utf8")) : legacyPlatformManifest(profile, claim, name);
9814
+ const manifest = existsSync7(manifestPath) ? JSON.parse(readFileSync6(manifestPath, "utf8")) : legacyPlatformManifest(profile, claim, name);
9699
9815
  if (!manifest) {
9700
9816
  const seedBase = join4(profile.seedDir, name);
9701
9817
  const localArtifacts = [
@@ -9705,7 +9821,7 @@ async function removeMetalGuest(profile, claim, exec) {
9705
9821
  `${seedBase}-meta-data`,
9706
9822
  `${seedBase}-network-config`
9707
9823
  ];
9708
- if (localArtifacts.some(existsSync6)) {
9824
+ if (localArtifacts.some(existsSync7)) {
9709
9825
  throw new MetalProvisionError("guest artifacts exist without owned inventory");
9710
9826
  }
9711
9827
  const [volume, active] = await Promise.all([
@@ -9722,7 +9838,7 @@ async function removeMetalGuest(profile, claim, exec) {
9722
9838
  if (!currentIdentity && !legacyPlatformIdentity) {
9723
9839
  throw new MetalProvisionError("compute identity conflicts with host inventory");
9724
9840
  }
9725
- if (existsSync6(unitPath))
9841
+ if (existsSync7(unitPath))
9726
9842
  await checked2(exec, ["systemctl", "disable", "--now", service]);
9727
9843
  else if (legacyPlatformIdentity)
9728
9844
  await checked2(exec, ["systemctl", "disable", "--now", service]);
@@ -9739,15 +9855,15 @@ async function removeMetalGuest(profile, claim, exec) {
9739
9855
  join4(profile.seedDir, `${name}-network-config`),
9740
9856
  manifestPath
9741
9857
  ])
9742
- if (existsSync6(path2))
9858
+ if (existsSync7(path2))
9743
9859
  unlinkSync5(path2);
9744
9860
  if (legacyPlatformIdentity) {
9745
9861
  const legacyConfig = join4(profile.legacyStateDir ?? "/etc/forgezero/guests", `${name}.conf`);
9746
9862
  const legacyDropIn = join4(profile.unitDir, `${service}.d`);
9747
- if (existsSync6(legacyConfig))
9863
+ if (existsSync7(legacyConfig))
9748
9864
  unlinkSync5(legacyConfig);
9749
- if (existsSync6(legacyDropIn))
9750
- rmSync3(legacyDropIn, { recursive: true });
9865
+ if (existsSync7(legacyDropIn))
9866
+ rmSync4(legacyDropIn, { recursive: true });
9751
9867
  }
9752
9868
  await checked2(exec, ["systemctl", "daemon-reload"]);
9753
9869
  return {};
@@ -9785,7 +9901,7 @@ command exceeded ${COMMAND_TIMEOUT_MS}ms`.trim() : stderr
9785
9901
  function startMetalHelper(options) {
9786
9902
  validateMetalProfile(options.profile);
9787
9903
  const socketPath = options.socketPath ?? DEFAULT_METAL_HELPER_SOCKET;
9788
- if (existsSync7(socketPath))
9904
+ if (existsSync8(socketPath))
9789
9905
  unlinkSync6(socketPath);
9790
9906
  let tail = Promise.resolve();
9791
9907
  const server = createServer3((socket) => {
@@ -9832,7 +9948,7 @@ function startMetalHelper(options) {
9832
9948
  });
9833
9949
  socket.on("error", () => socket.destroy());
9834
9950
  });
9835
- server.listen(socketPath, () => chmodSync7(socketPath, 432));
9951
+ server.listen(socketPath, () => chmodSync8(socketPath, 432));
9836
9952
  return {
9837
9953
  server,
9838
9954
  async stop() {
@@ -9872,7 +9988,7 @@ function requestMetalProvision(claim, socketPath = DEFAULT_METAL_HELPER_SOCKET)
9872
9988
  }
9873
9989
 
9874
9990
  // src/deployment-runner.ts
9875
- import { chmodSync as chmodSync8, existsSync as existsSync8, realpathSync, unlinkSync as unlinkSync7 } from "fs";
9991
+ import { chmodSync as chmodSync9, existsSync as existsSync9, realpathSync, unlinkSync as unlinkSync7 } from "fs";
9876
9992
  import { isAbsolute as isAbsolute4, resolve, sep } from "path";
9877
9993
  import { connect as connect3, createServer as createServer4 } from "net";
9878
9994
  var DEFAULT_DEPLOYMENT_RUNNER_SOCKET = "/run/forgezero-deploy/runner.sock";
@@ -9989,7 +10105,7 @@ function startDeploymentRunner(options) {
9989
10105
  const root = resolve(options.root);
9990
10106
  const home = resolve(options.home);
9991
10107
  const socketPath = options.socketPath ?? DEFAULT_DEPLOYMENT_RUNNER_SOCKET;
9992
- if (existsSync8(socketPath))
10108
+ if (existsSync9(socketPath))
9993
10109
  unlinkSync7(socketPath);
9994
10110
  const active = new Set;
9995
10111
  const server = createServer4((socket) => {
@@ -10034,7 +10150,7 @@ function startDeploymentRunner(options) {
10034
10150
  const ready = new Promise((resolveReady, rejectReady) => {
10035
10151
  server.once("error", rejectReady);
10036
10152
  server.listen(socketPath, () => {
10037
- chmodSync8(socketPath, options.socketMode ?? 432);
10153
+ chmodSync9(socketPath, options.socketMode ?? 432);
10038
10154
  server.off("error", rejectReady);
10039
10155
  resolveReady();
10040
10156
  });
@@ -10085,7 +10201,7 @@ function requestDeploymentCommand(input, socketPath = DEFAULT_DEPLOYMENT_RUNNER_
10085
10201
  }
10086
10202
 
10087
10203
  // src/snp-attestation.ts
10088
- import { existsSync as existsSync9 } from "fs";
10204
+ import { existsSync as existsSync10 } from "fs";
10089
10205
 
10090
10206
  // ../runtime/dist/snp.js
10091
10207
  var REPORT_BYTES = 1184;
@@ -10178,7 +10294,7 @@ function createSnpAttestationSource(options = {}) {
10178
10294
  const python = options.python ?? "python3";
10179
10295
  const timeoutMs = options.timeoutMs ?? 1e4;
10180
10296
  const spawn = options.spawn ?? Bun.spawn;
10181
- const exists = options.exists ?? existsSync9;
10297
+ const exists = options.exists ?? existsSync10;
10182
10298
  return {
10183
10299
  name: "linux-sev-guest",
10184
10300
  async report(nonce) {
@@ -10264,7 +10380,7 @@ function startNodeAttestation(options) {
10264
10380
  }
10265
10381
 
10266
10382
  // src/metal-isolation.ts
10267
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
10383
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
10268
10384
  import { join as join5 } from "path";
10269
10385
  var members = (list) => list.split(",").flatMap((part) => {
10270
10386
  const [first, last = first] = part.split("-").map(Number);
@@ -10342,16 +10458,16 @@ async function applyMetalIsolation(profile, exec = defaultExec2) {
10342
10458
  validateMetalProfile(profile);
10343
10459
  await requireGuestsInSlice(exec);
10344
10460
  const unitDir = profile.unitDir;
10345
- mkdirSync5(unitDir, { recursive: true });
10346
- writeFileSync6(join5(unitDir, "forgezero-guests.slice"), metalGuestSliceUnit(profile), { mode: 420 });
10461
+ mkdirSync6(unitDir, { recursive: true });
10462
+ writeFileSync7(join5(unitDir, "forgezero-guests.slice"), metalGuestSliceUnit(profile), { mode: 420 });
10347
10463
  for (const unit of ["system.slice", "user.slice"]) {
10348
10464
  const directory = join5(unitDir, `${unit}.d`);
10349
- mkdirSync5(directory, { recursive: true });
10350
- writeFileSync6(join5(directory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "slice"), { mode: 420 });
10465
+ mkdirSync6(directory, { recursive: true });
10466
+ writeFileSync7(join5(directory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "slice"), { mode: 420 });
10351
10467
  }
10352
10468
  const initDirectory = join5(unitDir, "init.scope.d");
10353
- mkdirSync5(initDirectory, { recursive: true });
10354
- writeFileSync6(join5(initDirectory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "scope"), { mode: 420 });
10469
+ mkdirSync6(initDirectory, { recursive: true });
10470
+ writeFileSync7(join5(initDirectory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "scope"), { mode: 420 });
10355
10471
  await checked3(exec, ["systemctl", "daemon-reload"]);
10356
10472
  await requireGuestsInSlice(exec);
10357
10473
  const properties = [`AllowedCPUs=${profile.housekeepingCpus}`];
@@ -10386,7 +10502,7 @@ async function closeServerWithin(server, timeoutMs) {
10386
10502
  }
10387
10503
 
10388
10504
  // src/lifecycle-helper.ts
10389
- import { chmodSync as chmodSync9, existsSync as existsSync10, readFileSync as readFileSync6, unlinkSync as unlinkSync8 } from "fs";
10505
+ import { chmodSync as chmodSync10, existsSync as existsSync11, readFileSync as readFileSync7, unlinkSync as unlinkSync8 } from "fs";
10390
10506
  import { connect as connect4, createConnection, createServer as createServer5, isIP as isIP3 } from "net";
10391
10507
  var DEFAULT_LIFECYCLE_HELPER_SOCKET = "/run/forgezero-lifecycle/helper.sock";
10392
10508
  var MAX_REQUEST_BYTES4 = 16 * 1024;
@@ -10430,7 +10546,7 @@ function validateLifecycleProfile(profile) {
10430
10546
  }
10431
10547
  }
10432
10548
  function loadLifecycleProfile(path2) {
10433
- const profile = JSON.parse(readFileSync6(path2, "utf8"));
10549
+ const profile = JSON.parse(readFileSync7(path2, "utf8"));
10434
10550
  validateLifecycleProfile(profile);
10435
10551
  return profile;
10436
10552
  }
@@ -10527,7 +10643,7 @@ async function executeLifecycleAction(profile, claim, exec = spawnLifecycleComma
10527
10643
  function startLifecycleHelper(options) {
10528
10644
  validateLifecycleProfile(options.profile);
10529
10645
  const socketPath = options.socketPath ?? DEFAULT_LIFECYCLE_HELPER_SOCKET;
10530
- if (existsSync10(socketPath))
10646
+ if (existsSync11(socketPath))
10531
10647
  unlinkSync8(socketPath);
10532
10648
  let tail = Promise.resolve();
10533
10649
  const server = createServer5((socket) => {
@@ -10568,7 +10684,7 @@ function startLifecycleHelper(options) {
10568
10684
  });
10569
10685
  socket.on("error", () => socket.destroy());
10570
10686
  });
10571
- server.listen(socketPath, () => chmodSync9(socketPath, 432));
10687
+ server.listen(socketPath, () => chmodSync10(socketPath, 432));
10572
10688
  return {
10573
10689
  server,
10574
10690
  async stop() {
@@ -10608,7 +10724,7 @@ function requestLifecycleAction(claim, socketPath = DEFAULT_LIFECYCLE_HELPER_SOC
10608
10724
  }
10609
10725
 
10610
10726
  // src/warp-config.ts
10611
- import { chmodSync as chmodSync10, mkdirSync as mkdirSync6, renameSync as renameSync4, symlinkSync as symlinkSync2, unlinkSync as unlinkSync9, writeFileSync as writeFileSync7 } from "fs";
10727
+ import { chmodSync as chmodSync11, mkdirSync as mkdirSync7, renameSync as renameSync5, symlinkSync as symlinkSync2, unlinkSync as unlinkSync9, writeFileSync as writeFileSync8 } from "fs";
10612
10728
  var xml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
10613
10729
  function renderWarpMdm(options) {
10614
10730
  if (!/^[a-z0-9][a-z0-9-]{0,62}$/i.test(options.organization))
@@ -10629,12 +10745,12 @@ function renderWarpMdm(options) {
10629
10745
  function materializeWarpMdm(options) {
10630
10746
  const runtimePath = options.runtimePath ?? "/run/forgezero-warp/mdm.xml";
10631
10747
  const servicePath = options.servicePath ?? "/var/lib/cloudflare-warp/mdm.xml";
10632
- mkdirSync6(runtimePath.replace(/\/[^/]+$/, ""), { recursive: true, mode: 448 });
10633
- mkdirSync6(servicePath.replace(/\/[^/]+$/, ""), { recursive: true, mode: 448 });
10748
+ mkdirSync7(runtimePath.replace(/\/[^/]+$/, ""), { recursive: true, mode: 448 });
10749
+ mkdirSync7(servicePath.replace(/\/[^/]+$/, ""), { recursive: true, mode: 448 });
10634
10750
  const next = `${runtimePath}.next`;
10635
- writeFileSync7(next, renderWarpMdm(options), { mode: 384 });
10636
- chmodSync10(next, 384);
10637
- renameSync4(next, runtimePath);
10751
+ writeFileSync8(next, renderWarpMdm(options), { mode: 384 });
10752
+ chmodSync11(next, 384);
10753
+ renameSync5(next, runtimePath);
10638
10754
  try {
10639
10755
  unlinkSync9(servicePath);
10640
10756
  } catch (cause) {
@@ -10646,7 +10762,7 @@ function materializeWarpMdm(options) {
10646
10762
  }
10647
10763
 
10648
10764
  // src/mesh-connector.ts
10649
- import { constants, closeSync, fstatSync, openSync, readFileSync as readFileSync7 } from "fs";
10765
+ import { constants, closeSync, fstatSync, openSync, readFileSync as readFileSync8 } from "fs";
10650
10766
  import { join as join6 } from "path";
10651
10767
  var TOKEN = /^[A-Za-z0-9._-]{40,16384}$/;
10652
10768
  function readMeshConnectorCredential(name = "CF_WARP_CONNECTOR_TOKEN") {
@@ -10663,7 +10779,7 @@ function readMeshConnectorCredential(name = "CF_WARP_CONNECTOR_TOKEN") {
10663
10779
  if (!metadata.isFile() || metadata.nlink !== 1 || metadata.size < 40 || metadata.size > 16384) {
10664
10780
  throw new Error("Mesh connector systemd credential is malformed");
10665
10781
  }
10666
- const token = readFileSync7(descriptor, "utf8").trim();
10782
+ const token = readFileSync8(descriptor, "utf8").trim();
10667
10783
  if (!TOKEN.test(token))
10668
10784
  throw new Error("Mesh connector systemd credential is malformed");
10669
10785
  return token;
@@ -10689,20 +10805,20 @@ async function configureMeshConnector(input) {
10689
10805
  }
10690
10806
 
10691
10807
  // src/agent-update.ts
10692
- import { createHash as createHash7, timingSafeEqual, randomUUID as randomUUID2 } from "crypto";
10808
+ import { createHash as createHash8, timingSafeEqual, randomUUID as randomUUID2 } from "crypto";
10693
10809
  import {
10694
- chmodSync as chmodSync11,
10810
+ chmodSync as chmodSync12,
10695
10811
  closeSync as closeSync2,
10696
- existsSync as existsSync11,
10812
+ existsSync as existsSync12,
10697
10813
  fsyncSync,
10698
- mkdirSync as mkdirSync7,
10814
+ mkdirSync as mkdirSync8,
10699
10815
  openSync as openSync2,
10700
- readFileSync as readFileSync8,
10816
+ readFileSync as readFileSync9,
10701
10817
  readlinkSync,
10702
- renameSync as renameSync5,
10703
- rmSync as rmSync4,
10818
+ renameSync as renameSync6,
10819
+ rmSync as rmSync5,
10704
10820
  symlinkSync as symlinkSync3,
10705
- writeFileSync as writeFileSync8
10821
+ writeFileSync as writeFileSync9
10706
10822
  } from "fs";
10707
10823
  import { dirname as dirname4, join as join7, resolve as resolve2 } from "path";
10708
10824
  var DEFAULT_AGENT_RELEASE_ROOT = "/opt/forgezero/agent";
@@ -10784,9 +10900,9 @@ var checked4 = async (run2, input, label) => {
10784
10900
  return result;
10785
10901
  };
10786
10902
  async function validateReleaseDirectory(directory, release, run2) {
10787
- chmodSync11(directory, 493);
10788
- chmodSync11(join7(directory, "dist"), 493);
10789
- const manifest = JSON.parse(readFileSync8(join7(directory, "package.json"), "utf8"));
10903
+ chmodSync12(directory, 493);
10904
+ chmodSync12(join7(directory, "dist"), 493);
10905
+ const manifest = JSON.parse(readFileSync9(join7(directory, "package.json"), "utf8"));
10790
10906
  if (manifest.name !== release.package || manifest.version !== release.version) {
10791
10907
  throw new Error("agent update manifest does not match the selected release");
10792
10908
  }
@@ -10794,11 +10910,11 @@ async function validateReleaseDirectory(directory, release, run2) {
10794
10910
  const cli = join7(directory, "dist", "fz.js");
10795
10911
  const gitSsh = join7(directory, "dist", "fz-git-ssh.js");
10796
10912
  for (const binary of [agent, cli, gitSsh]) {
10797
- if (!readFileSync8(binary, "utf8").startsWith(`#!/usr/bin/env bun
10913
+ if (!readFileSync9(binary, "utf8").startsWith(`#!/usr/bin/env bun
10798
10914
  `)) {
10799
10915
  throw new Error("agent update artifact is not a self-contained Bun executable");
10800
10916
  }
10801
- chmodSync11(binary, 493);
10917
+ chmodSync12(binary, 493);
10802
10918
  }
10803
10919
  const version = (await checked4(run2, { command: agent, args: ["--version"] }, "agent update smoke test")).output.trim();
10804
10920
  if (version !== release.version)
@@ -10817,7 +10933,7 @@ async function stageAgentRelease(releaseInput, options) {
10817
10933
  const archive = join7(stage2, "agent.tgz");
10818
10934
  const unpacked = join7(stage2, "unpacked");
10819
10935
  const run2 = options.run ?? command;
10820
- mkdirSync7(unpacked, { recursive: true, mode: 448 });
10936
+ mkdirSync8(unpacked, { recursive: true, mode: 448 });
10821
10937
  try {
10822
10938
  const response = await (options.fetch ?? globalThis.fetch)(release.tarball, {
10823
10939
  redirect: "error",
@@ -10833,10 +10949,10 @@ async function stageAgentRelease(releaseInput, options) {
10833
10949
  throw new Error("agent update tarball is empty or exceeds the size limit");
10834
10950
  }
10835
10951
  const expected = Buffer.from(release.integrity.slice("sha512-".length), "base64");
10836
- const actual = createHash7("sha512").update(bytes).digest();
10952
+ const actual = createHash8("sha512").update(bytes).digest();
10837
10953
  if (!timingSafeEqual(actual, expected))
10838
10954
  throw new Error("agent update integrity mismatch");
10839
- writeFileSync8(archive, bytes, { mode: 384, flag: "wx" });
10955
+ writeFileSync9(archive, bytes, { mode: 384, flag: "wx" });
10840
10956
  for (const [member, relative] of [
10841
10957
  ["package/package.json", "package.json"],
10842
10958
  ["package/dist/fz-agent.js", "dist/fz-agent.js"],
@@ -10848,23 +10964,23 @@ async function stageAgentRelease(releaseInput, options) {
10848
10964
  args: ["-xOzf", archive, member]
10849
10965
  }, `agent update extraction of ${member}`);
10850
10966
  const destination = join7(unpacked, relative);
10851
- mkdirSync7(dirname4(destination), { recursive: true, mode: 448 });
10852
- writeFileSync8(destination, extracted.output, { mode: 384, flag: "wx" });
10967
+ mkdirSync8(dirname4(destination), { recursive: true, mode: 448 });
10968
+ writeFileSync9(destination, extracted.output, { mode: 384, flag: "wx" });
10853
10969
  }
10854
10970
  await validateReleaseDirectory(unpacked, release, run2);
10855
- if (!existsSync11(finalDirectory)) {
10856
- renameSync5(unpacked, finalDirectory);
10971
+ if (!existsSync12(finalDirectory)) {
10972
+ renameSync6(unpacked, finalDirectory);
10857
10973
  syncReleaseDirectory(finalDirectory);
10858
10974
  } else
10859
10975
  await validateReleaseDirectory(finalDirectory, release, run2);
10860
- if (!existsSync11(currentLink)) {
10976
+ if (!existsSync12(currentLink)) {
10861
10977
  throw new Error("agent update requires an active immutable release to roll back to");
10862
10978
  }
10863
10979
  const previousTarget = readlinkSync(currentLink);
10864
10980
  if (previousTarget !== join7("versions", options.currentVersion)) {
10865
10981
  throw new Error("agent update current release does not match the running version");
10866
10982
  }
10867
- if (!existsSync11(join7(root, previousTarget))) {
10983
+ if (!existsSync12(join7(root, previousTarget))) {
10868
10984
  throw new Error("agent update rollback release is missing");
10869
10985
  }
10870
10986
  return {
@@ -10876,44 +10992,44 @@ async function stageAgentRelease(releaseInput, options) {
10876
10992
  currentLink
10877
10993
  };
10878
10994
  } finally {
10879
- rmSync4(stage2, { recursive: true, force: true });
10995
+ rmSync5(stage2, { recursive: true, force: true });
10880
10996
  }
10881
10997
  }
10882
10998
  function selectAgentRelease(staged) {
10883
10999
  const next = join7(dirname4(staged.currentLink), `.current.${randomUUID2()}.next`);
10884
11000
  try {
10885
11001
  symlinkSync3(staged.nextTarget, next);
10886
- renameSync5(next, staged.currentLink);
11002
+ renameSync6(next, staged.currentLink);
10887
11003
  syncPath(dirname4(staged.currentLink));
10888
11004
  } finally {
10889
- rmSync4(next, { force: true });
11005
+ rmSync5(next, { force: true });
10890
11006
  }
10891
11007
  }
10892
11008
  function restoreAgentRelease(staged) {
10893
11009
  const next = join7(dirname4(staged.currentLink), `.current.${randomUUID2()}.rollback`);
10894
11010
  try {
10895
11011
  symlinkSync3(staged.previousTarget, next);
10896
- renameSync5(next, staged.currentLink);
11012
+ renameSync6(next, staged.currentLink);
10897
11013
  syncPath(dirname4(staged.currentLink));
10898
11014
  } finally {
10899
- rmSync4(next, { force: true });
11015
+ rmSync5(next, { force: true });
10900
11016
  }
10901
11017
  }
10902
11018
 
10903
11019
  // src/agent-update-helper.ts
10904
11020
  import { randomUUID as randomUUID3 } from "crypto";
10905
11021
  import {
10906
- chmodSync as chmodSync12,
11022
+ chmodSync as chmodSync13,
10907
11023
  closeSync as closeSync3,
10908
- existsSync as existsSync12,
11024
+ existsSync as existsSync13,
10909
11025
  fsyncSync as fsyncSync2,
10910
- mkdirSync as mkdirSync8,
11026
+ mkdirSync as mkdirSync9,
10911
11027
  openSync as openSync3,
10912
- readFileSync as readFileSync9,
10913
- renameSync as renameSync6,
10914
- rmSync as rmSync5,
11028
+ readFileSync as readFileSync10,
11029
+ renameSync as renameSync7,
11030
+ rmSync as rmSync6,
10915
11031
  unlinkSync as unlinkSync10,
10916
- writeFileSync as writeFileSync9
11032
+ writeFileSync as writeFileSync10
10917
11033
  } from "fs";
10918
11034
  import { connect as connect5, createServer as createServer6 } from "net";
10919
11035
  import { dirname as dirname5, join as join8, resolve as resolve3 } from "path";
@@ -11011,22 +11127,22 @@ function validateJournal(value, root) {
11011
11127
  return journal;
11012
11128
  }
11013
11129
  function readJournal(path2, root) {
11014
- if (!existsSync12(path2))
11130
+ if (!existsSync13(path2))
11015
11131
  return;
11016
- return validateJournal(JSON.parse(readFileSync9(path2, "utf8")), root);
11132
+ return validateJournal(JSON.parse(readFileSync10(path2, "utf8")), root);
11017
11133
  }
11018
11134
  function writeAtomic(path2, value, mode) {
11019
- mkdirSync8(dirname5(path2), { recursive: true, mode: 493 });
11135
+ mkdirSync9(dirname5(path2), { recursive: true, mode: 493 });
11020
11136
  const next = `${path2}.${randomUUID3()}.next`;
11021
11137
  let file;
11022
11138
  try {
11023
11139
  file = openSync3(next, "wx", mode);
11024
- writeFileSync9(file, `${JSON.stringify(value)}
11140
+ writeFileSync10(file, `${JSON.stringify(value)}
11025
11141
  `);
11026
11142
  fsyncSync2(file);
11027
11143
  closeSync3(file);
11028
11144
  file = undefined;
11029
- renameSync6(next, path2);
11145
+ renameSync7(next, path2);
11030
11146
  const directory = openSync3(dirname5(path2), "r");
11031
11147
  try {
11032
11148
  fsyncSync2(directory);
@@ -11036,7 +11152,7 @@ function writeAtomic(path2, value, mode) {
11036
11152
  } finally {
11037
11153
  if (file !== undefined)
11038
11154
  closeSync3(file);
11039
- rmSync5(next, { force: true });
11155
+ rmSync6(next, { force: true });
11040
11156
  }
11041
11157
  }
11042
11158
  var publicReceipt = (journal) => {
@@ -11069,9 +11185,9 @@ function writeUpdateState(journalPath, receiptPath, journal) {
11069
11185
  }
11070
11186
  function readAgentUpdateReceipt(path2 = AGENT_UPDATE_RECEIPT) {
11071
11187
  try {
11072
- if (!existsSync12(path2))
11188
+ if (!existsSync13(path2))
11073
11189
  return;
11074
- return validateReceipt(JSON.parse(readFileSync9(path2, "utf8")));
11190
+ return validateReceipt(JSON.parse(readFileSync10(path2, "utf8")));
11075
11191
  } catch {
11076
11192
  return;
11077
11193
  }
@@ -11225,7 +11341,7 @@ async function recoverInterruptedAgentUpdate(options = {}) {
11225
11341
  return publicReceipt(journal);
11226
11342
  }
11227
11343
  const staged = stagedFromJournal(journal, root);
11228
- if (!existsSync12(join8(root, journal.previousTarget))) {
11344
+ if (!existsSync13(join8(root, journal.previousTarget))) {
11229
11345
  throw new Error("Agent update rollback release is missing");
11230
11346
  }
11231
11347
  const run2 = options.run ?? runCommand;
@@ -11255,9 +11371,9 @@ async function recoverInterruptedAgentUpdate(options = {}) {
11255
11371
  }
11256
11372
  function startAgentUpdateHelper(options = {}) {
11257
11373
  const socketPath = options.socketPath ?? DEFAULT_AGENT_UPDATE_SOCKET;
11258
- if (existsSync12(socketPath))
11374
+ if (existsSync13(socketPath))
11259
11375
  unlinkSync10(socketPath);
11260
- mkdirSync8(dirname5(socketPath), { recursive: true, mode: 488 });
11376
+ mkdirSync9(dirname5(socketPath), { recursive: true, mode: 488 });
11261
11377
  const setTimer = options.setTimer ?? ((callback, ms) => setTimeout(callback, ms));
11262
11378
  const receiptPath = options.receiptPath ?? AGENT_UPDATE_RECEIPT;
11263
11379
  const journalPath = options.journalPath ?? AGENT_UPDATE_JOURNAL;
@@ -11336,7 +11452,7 @@ function startAgentUpdateHelper(options = {}) {
11336
11452
  });
11337
11453
  socket.on("error", () => socket.destroy());
11338
11454
  });
11339
- server.listen(socketPath, () => chmodSync12(socketPath, 432));
11455
+ server.listen(socketPath, () => chmodSync13(socketPath, 432));
11340
11456
  return server;
11341
11457
  }
11342
11458
  function requestAgentUpdate(request, socketPath = DEFAULT_AGENT_UPDATE_SOCKET, timeoutMs = 90000) {
@@ -11366,13 +11482,13 @@ function requestAgentUpdate(request, socketPath = DEFAULT_AGENT_UPDATE_SOCKET, t
11366
11482
  }
11367
11483
 
11368
11484
  // src/agent-heartbeat.ts
11369
- import { readFileSync as readFileSync10 } from "fs";
11485
+ import { readFileSync as readFileSync11 } from "fs";
11370
11486
  var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
11371
11487
  var remoteOutcome4 = (cause) => cause instanceof SignedNodeHttpError && cause.status < 500 ? "refused" : cause instanceof SignedNodeHttpError ? "retryable" : "failed";
11372
11488
 
11373
11489
  class AgentUpdateRefusedError extends Error {
11374
11490
  }
11375
- function observeAgentHost(version = VERSION2, mode = "enrolled", osRelease = readFileSync10("/etc/os-release", "utf8"), architecture = process.arch) {
11491
+ function observeAgentHost(version = VERSION2, mode = "enrolled", osRelease = readFileSync11("/etc/os-release", "utf8"), architecture = process.arch) {
11376
11492
  const values = Object.fromEntries(osRelease.split(`
11377
11493
  `).flatMap((line) => {
11378
11494
  const separator = line.indexOf("=");
@@ -11490,34 +11606,34 @@ function startAgentHeartbeat(options) {
11490
11606
  }
11491
11607
 
11492
11608
  // src/software-helper.ts
11493
- import { chmodSync as chmodSync13, existsSync as existsSync15, mkdirSync as mkdirSync11, unlinkSync as unlinkSync11 } from "fs";
11609
+ import { chmodSync as chmodSync14, existsSync as existsSync16, mkdirSync as mkdirSync12, unlinkSync as unlinkSync11 } from "fs";
11494
11610
  import { connect as connect6, createServer as createServer7 } from "net";
11495
11611
  import { dirname as dirname8 } from "path";
11496
11612
 
11497
11613
  // src/service-supervisor.ts
11498
- import { createHash as createHash8 } from "crypto";
11499
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync11, readdirSync as readdirSync2, realpathSync as realpathSync2, renameSync as renameSync7, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "fs";
11614
+ import { createHash as createHash9 } from "crypto";
11615
+ import { existsSync as existsSync14, mkdirSync as mkdirSync10, readFileSync as readFileSync12, readdirSync as readdirSync2, realpathSync as realpathSync2, renameSync as renameSync8, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "fs";
11500
11616
  import { dirname as dirname6, join as join9, resolve as resolve4, sep as sep2 } from "path";
11501
11617
  var SERVICE_STATE_DIRECTORY = "/var/lib/forgezero/services";
11502
11618
  var SERVICE_CONFIG_DIRECTORY = "/etc/forgezero/services";
11503
11619
  var SERVICE_UNIT_DIRECTORY = "/etc/systemd/system";
11504
11620
  var SERVICE_NGINX_DIRECTORY = "/etc/nginx/conf.d";
11505
- var idFor = (key) => createHash8("sha256").update(key).digest("hex").slice(0, 24);
11621
+ var idFor = (key) => createHash9("sha256").update(key).digest("hex").slice(0, 24);
11506
11622
  var statePath = (id2) => `${SERVICE_STATE_DIRECTORY}/${id2}.json`;
11507
11623
  var within2 = (root, path2) => path2 === root || path2.startsWith(`${root}${sep2}`);
11508
11624
  var defaultHost = {
11509
11625
  write(path2, content, mode) {
11510
- mkdirSync9(dirname6(path2), { recursive: true, mode: 493 });
11626
+ mkdirSync10(dirname6(path2), { recursive: true, mode: 493 });
11511
11627
  const next = `${path2}.next`;
11512
- writeFileSync10(next, content, { mode });
11513
- renameSync7(next, path2);
11628
+ writeFileSync11(next, content, { mode });
11629
+ renameSync8(next, path2);
11514
11630
  },
11515
- read: (path2) => readFileSync11(path2, "utf8"),
11516
- exists: existsSync13,
11517
- list: (path2) => existsSync13(path2) ? readdirSync2(path2) : [],
11631
+ read: (path2) => readFileSync12(path2, "utf8"),
11632
+ exists: existsSync14,
11633
+ list: (path2) => existsSync14(path2) ? readdirSync2(path2) : [],
11518
11634
  realpath: realpathSync2,
11519
- mkdir: (path2, mode) => mkdirSync9(path2, { recursive: true, mode }),
11520
- remove: (path2) => rmSync6(path2, { force: true }),
11635
+ mkdir: (path2, mode) => mkdirSync10(path2, { recursive: true, mode }),
11636
+ remove: (path2) => rmSync7(path2, { force: true }),
11521
11637
  async exec(argv2) {
11522
11638
  const child = Bun.spawn([...argv2], { stdout: "pipe", stderr: "pipe", env: {
11523
11639
  PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
@@ -11575,7 +11691,7 @@ function allocatedPorts(request, id2, previous, states) {
11575
11691
  if (previous && previous.applicationPorts.length === required && previous.applicationPorts.every((port) => port >= allocation.from && port <= allocation.to && !occupied.has(port)))
11576
11692
  return [...previous.applicationPorts];
11577
11693
  const width = allocation.to - allocation.from + 1;
11578
- const start = Number.parseInt(createHash8("sha256").update(request.key).digest("hex").slice(0, 8), 16) % width;
11694
+ const start = Number.parseInt(createHash9("sha256").update(request.key).digest("hex").slice(0, 8), 16) % width;
11579
11695
  for (let offset = 0;offset < width; offset += 1) {
11580
11696
  const first = allocation.from + (start + offset) % width;
11581
11697
  const candidate = Array.from({ length: required }, (_, index) => first + index);
@@ -11712,22 +11828,22 @@ async function activateSupervisedService(request, host = defaultHost) {
11712
11828
  }
11713
11829
 
11714
11830
  // src/container-supervisor.ts
11715
- import { createHash as createHash9 } from "crypto";
11716
- import { existsSync as existsSync14, mkdirSync as mkdirSync10, readFileSync as readFileSync12, readdirSync as readdirSync3, realpathSync as realpathSync3, renameSync as renameSync8, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "fs";
11831
+ import { createHash as createHash10 } from "crypto";
11832
+ import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync13, readdirSync as readdirSync3, realpathSync as realpathSync3, renameSync as renameSync9, rmSync as rmSync8, writeFileSync as writeFileSync12 } from "fs";
11717
11833
  import { dirname as dirname7, resolve as resolve5, sep as sep3 } from "path";
11718
11834
  var defaultHost2 = {
11719
11835
  realpath: realpathSync3,
11720
- exists: existsSync14,
11721
- read: (path2) => readFileSync12(path2, "utf8"),
11836
+ exists: existsSync15,
11837
+ read: (path2) => readFileSync13(path2, "utf8"),
11722
11838
  write(path2, content, mode) {
11723
- mkdirSync10(dirname7(path2), { recursive: true, mode: 493 });
11839
+ mkdirSync11(dirname7(path2), { recursive: true, mode: 493 });
11724
11840
  const next = `${path2}.next`;
11725
- writeFileSync11(next, content, { mode });
11726
- renameSync8(next, path2);
11841
+ writeFileSync12(next, content, { mode });
11842
+ renameSync9(next, path2);
11727
11843
  },
11728
- remove: (path2) => rmSync7(path2, { force: true }),
11729
- list: (path2) => existsSync14(path2) ? readdirSync3(path2) : [],
11730
- mkdir: (path2, mode) => mkdirSync10(path2, { recursive: true, mode }),
11844
+ remove: (path2) => rmSync8(path2, { force: true }),
11845
+ list: (path2) => existsSync15(path2) ? readdirSync3(path2) : [],
11846
+ mkdir: (path2, mode) => mkdirSync11(path2, { recursive: true, mode }),
11731
11847
  async exec(argv2) {
11732
11848
  const child = Bun.spawn([...argv2], { stdout: "pipe", stderr: "pipe", env: { PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", LANG: "C", LC_ALL: "C" } });
11733
11849
  const [stdout, stderr, exitCode] = await Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited]);
@@ -11745,7 +11861,7 @@ var defaultHost2 = {
11745
11861
  now: Date.now
11746
11862
  };
11747
11863
  var within3 = (root, path2) => path2 === root || path2.startsWith(`${root}${sep3}`);
11748
- var idFor2 = (key) => createHash9("sha256").update(key).digest("hex").slice(0, 24);
11864
+ var idFor2 = (key) => createHash10("sha256").update(key).digest("hex").slice(0, 24);
11749
11865
  var statePath2 = (id2) => `${SERVICE_STATE_DIRECTORY}/${id2}.json`;
11750
11866
  var nameFor = (id2, slot) => `forgezero-${id2}-slot${slot}`;
11751
11867
  function validateRequest(request, host) {
@@ -12010,9 +12126,9 @@ var MAX_REQUEST_BYTES6 = 128 * 1024;
12010
12126
  var MAX_PENDING_REQUESTS = 128;
12011
12127
  function startSoftwareHelper(options = {}) {
12012
12128
  const socketPath = options.socketPath ?? DEFAULT_SOFTWARE_HELPER_SOCKET;
12013
- if (existsSync15(socketPath))
12129
+ if (existsSync16(socketPath))
12014
12130
  unlinkSync11(socketPath);
12015
- mkdirSync11(dirname8(socketPath), { recursive: true, mode: 488 });
12131
+ mkdirSync12(dirname8(socketPath), { recursive: true, mode: 488 });
12016
12132
  const ensure = options.ensure ?? ensureSoftwareRequirements;
12017
12133
  const activate = options.activate ?? activateSupervisedService;
12018
12134
  const buildContainer = options.buildContainer ?? buildContainerImage;
@@ -12092,7 +12208,7 @@ function startSoftwareHelper(options = {}) {
12092
12208
  });
12093
12209
  socket.on("error", () => socket.destroy());
12094
12210
  });
12095
- server.listen(socketPath, () => chmodSync13(socketPath, 432));
12211
+ server.listen(socketPath, () => chmodSync14(socketPath, 432));
12096
12212
  return server;
12097
12213
  }
12098
12214
  function requestContainer(op, request, socketPath, timeoutMs) {
@@ -13121,20 +13237,20 @@ var METAL_SYSTEMD_CREDENTIALS = {
13121
13237
 
13122
13238
  // src/platform-bootstrap-runtime.ts
13123
13239
  import {
13124
- chmodSync as chmodSync14,
13240
+ chmodSync as chmodSync15,
13125
13241
  chownSync,
13126
13242
  copyFileSync as copyFileSync2,
13127
- existsSync as existsSync16,
13128
- lstatSync as lstatSync2,
13129
- mkdirSync as mkdirSync12,
13130
- readFileSync as readFileSync13,
13243
+ existsSync as existsSync17,
13244
+ lstatSync as lstatSync3,
13245
+ mkdirSync as mkdirSync13,
13246
+ readFileSync as readFileSync14,
13131
13247
  readdirSync as readdirSync4,
13132
13248
  realpathSync as realpathSync5,
13133
- renameSync as renameSync9,
13134
- rmSync as rmSync8,
13249
+ renameSync as renameSync10,
13250
+ rmSync as rmSync9,
13135
13251
  statSync as statSync3,
13136
13252
  symlinkSync as symlinkSync4,
13137
- writeFileSync as writeFileSync12
13253
+ writeFileSync as writeFileSync13
13138
13254
  } from "fs";
13139
13255
  import { join as join10 } from "path";
13140
13256
  var boundedInteger2 = (name, value, minimum, maximum) => {
@@ -13188,21 +13304,21 @@ var platformExec = async (argv2) => {
13188
13304
  };
13189
13305
  var replaceLink = (path2, target2) => {
13190
13306
  const pending = `${path2}.next`;
13191
- rmSync8(pending, { force: true });
13307
+ rmSync9(pending, { force: true });
13192
13308
  if (!target2) {
13193
- rmSync8(path2, { force: true });
13309
+ rmSync9(path2, { force: true });
13194
13310
  return;
13195
13311
  }
13196
13312
  symlinkSync4(target2, pending);
13197
- renameSync9(pending, path2);
13313
+ renameSync10(pending, path2);
13198
13314
  };
13199
13315
  var secureRelease = (path2, uid, gid) => {
13200
13316
  const visit = (current) => {
13201
- const metadata = lstatSync2(current);
13317
+ const metadata = lstatSync3(current);
13202
13318
  if (metadata.isSymbolicLink())
13203
13319
  throw new Error("release contains a symbolic link");
13204
13320
  chownSync(current, uid, gid);
13205
- chmodSync14(current, metadata.isDirectory() ? 365 : 292);
13321
+ chmodSync15(current, metadata.isDirectory() ? 365 : 292);
13206
13322
  if (metadata.isDirectory())
13207
13323
  for (const name of readdirSync4(current))
13208
13324
  visit(join10(current, name));
@@ -13225,9 +13341,9 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
13225
13341
  const request = options.fetch ?? fetch;
13226
13342
  const sleep2 = options.sleep ?? Bun.sleep;
13227
13343
  const slots = join10(normalized.root, "slots");
13228
- mkdirSync12(slots, { recursive: true, mode: 493 });
13344
+ mkdirSync13(slots, { recursive: true, mode: 493 });
13229
13345
  const slotFile = join10(normalized.root, ".forge-slot");
13230
- const previousSlot = existsSync16(slotFile) ? readFileSync13(slotFile, "utf8").trim() : undefined;
13346
+ const previousSlot = existsSync17(slotFile) ? readFileSync14(slotFile, "utf8").trim() : undefined;
13231
13347
  const target2 = previousSlot === "blue" ? "green" : "blue";
13232
13348
  const port = target2 === "blue" ? normalized.bluePort : normalized.greenPort;
13233
13349
  const targetLink = join10(slots, target2);
@@ -13267,26 +13383,26 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
13267
13383
  }
13268
13384
  const upstream = "/etc/nginx/conf.d/forgezero-upstream.conf";
13269
13385
  const backup = `${upstream}.forgezero-backup`;
13270
- if (existsSync16(upstream))
13386
+ if (existsSync17(upstream))
13271
13387
  copyFileSync2(upstream, backup);
13272
13388
  else
13273
- rmSync8(backup, { force: true });
13274
- writeFileSync12(upstream, `upstream forgezero { server 127.0.0.1:${port}; }
13389
+ rmSync9(backup, { force: true });
13390
+ writeFileSync13(upstream, `upstream forgezero { server 127.0.0.1:${port}; }
13275
13391
  `, { mode: 420 });
13276
13392
  const test = await exec(["/usr/sbin/nginx", "-t"]);
13277
13393
  const reload = test.exitCode === 0 ? await exec(["/usr/sbin/nginx", "-s", "reload"]) : test;
13278
13394
  if (reload.exitCode !== 0) {
13279
- if (existsSync16(backup))
13280
- renameSync9(backup, upstream);
13395
+ if (existsSync17(backup))
13396
+ renameSync10(backup, upstream);
13281
13397
  else
13282
- rmSync8(upstream, { force: true });
13398
+ rmSync9(upstream, { force: true });
13283
13399
  await exec(["/usr/sbin/nginx", "-t"]);
13284
13400
  await exec(["/usr/sbin/nginx", "-s", "reload"]);
13285
13401
  await stopTarget();
13286
13402
  throw new Error("nginx refused the promoted upstream");
13287
13403
  }
13288
- rmSync8(backup, { force: true });
13289
- writeFileSync12(slotFile, `${target2}
13404
+ rmSync9(backup, { force: true });
13405
+ writeFileSync13(slotFile, `${target2}
13290
13406
  `, { mode: 420 });
13291
13407
  if (previousSlot && previousSlot !== target2) {
13292
13408
  await sleep2(normalized.drainDeadlineMs);
@@ -13295,12 +13411,12 @@ async function activatePlatformRelease(config, requestedRelease, options = {}) {
13295
13411
  const old = readdirSync4(releases, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join10(releases, entry.name)).sort((left, right) => statSync3(right).mtimeMs - statSync3(left).mtimeMs).slice(normalized.keepReleases);
13296
13412
  for (const path2 of old)
13297
13413
  if (path2 !== release)
13298
- rmSync8(path2, { recursive: true, force: true });
13414
+ rmSync9(path2, { recursive: true, force: true });
13299
13415
  return { release, slot: target2 };
13300
13416
  }
13301
13417
 
13302
13418
  // src/recovery-host.ts
13303
- import { existsSync as existsSync17, readFileSync as readFileSync14, statSync as statSync4 } from "fs";
13419
+ import { existsSync as existsSync18, readFileSync as readFileSync15, statSync as statSync4 } from "fs";
13304
13420
  import { hostname } from "os";
13305
13421
  import { join as join11 } from "path";
13306
13422
  var execute2 = async (argv2) => {
@@ -13315,18 +13431,18 @@ var checked7 = async (run2, argv2, expected = 0) => {
13315
13431
  return result;
13316
13432
  };
13317
13433
  var activeSlot = () => {
13318
- const slot = readFileSync14("/opt/forgezero/.forge-slot", "utf8").trim();
13434
+ const slot = readFileSync15("/opt/forgezero/.forge-slot", "utf8").trim();
13319
13435
  if (slot !== "blue" && slot !== "green")
13320
13436
  throw new Error("invalid active slot");
13321
13437
  return slot;
13322
13438
  };
13323
13439
  var exactState = (path2, expected) => {
13324
- if (!readFileSync14(path2, "utf8").split(`
13440
+ if (!readFileSync15(path2, "utf8").split(`
13325
13441
  `).includes(expected))
13326
13442
  throw new Error(`state mismatch: ${expected}`);
13327
13443
  };
13328
13444
  var nonEmpty = (path2) => {
13329
- if (!existsSync17(path2) || !statSync4(path2).isFile() || statSync4(path2).size < 1)
13445
+ if (!existsSync18(path2) || !statSync4(path2).isFile() || statSync4(path2).size < 1)
13330
13446
  throw new Error(`required file missing: ${path2}`);
13331
13447
  };
13332
13448
  var option = (args, name) => {
@@ -13422,18 +13538,18 @@ async function runRecoveryHost(args, run2 = execute2) {
13422
13538
  }
13423
13539
 
13424
13540
  // src/platform-fleet-verification.ts
13425
- import { lstatSync as lstatSync5 } from "fs";
13541
+ import { lstatSync as lstatSync6 } from "fs";
13426
13542
 
13427
13543
  // src/bootstrap.ts
13428
13544
  import {
13429
- chmodSync as chmodSync16,
13430
- existsSync as existsSync19,
13431
- lstatSync as lstatSync4,
13432
- mkdirSync as mkdirSync14,
13433
- readFileSync as readFileSync16,
13434
- renameSync as renameSync11,
13435
- rmSync as rmSync10,
13436
- writeFileSync as writeFileSync14
13545
+ chmodSync as chmodSync17,
13546
+ existsSync as existsSync20,
13547
+ lstatSync as lstatSync5,
13548
+ mkdirSync as mkdirSync15,
13549
+ readFileSync as readFileSync17,
13550
+ renameSync as renameSync12,
13551
+ rmSync as rmSync11,
13552
+ writeFileSync as writeFileSync15
13437
13553
  } from "fs";
13438
13554
  import { dirname as dirname10 } from "path";
13439
13555
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -13505,7 +13621,11 @@ function agentEgressUnit(options) {
13505
13621
  const user = options.user ?? "forgezero";
13506
13622
  if (!/^[a-z_][a-z0-9_-]{0,30}$/.test(user))
13507
13623
  throw new Error("invalid Agent service user");
13508
- const deploymentEnabled = Boolean(options.repository || options.pullDeployments);
13624
+ const bootstrapBundleEnabled = Boolean(options.bootstrapBundlePath && options.bootstrapBundleManifestPath);
13625
+ if (Boolean(options.bootstrapBundlePath) !== Boolean(options.bootstrapBundleManifestPath) || bootstrapBundleEnabled && ![options.bootstrapBundlePath, options.bootstrapBundleManifestPath].every((path2) => path2.startsWith("/") && !/[\r\n\0:]/.test(path2))) {
13626
+ throw new Error("bootstrap bundle and manifest must be supplied together as absolute paths");
13627
+ }
13628
+ const deploymentEnabled = Boolean(options.repository || bootstrapBundleEnabled || options.pullDeployments);
13509
13629
  const runnerLoopbackPorts = normalizeEgressTcpPorts(options.runnerLoopbackPorts ?? []);
13510
13630
  const runnerPublicTcpPorts = normalizeEgressTcpPorts(options.runnerPublicTcpPorts ?? DEFAULT_RUNNER_PUBLIC_TCP_PORTS);
13511
13631
  if (deploymentEnabled && runnerPublicTcpPorts.length < 1) {
@@ -13911,17 +14031,16 @@ function agentUnit(options) {
13911
14031
  if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
13912
14032
  throw new Error("migration pull and lifecycle profile must be supplied together");
13913
14033
  }
13914
- const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath && options.bootstrapTargetTelemetryEndpoint);
13915
- if ([
13916
- options.pullBootstrap,
13917
- options.bootstrapSshCredentialPath,
13918
- options.bootstrapSshPublicKeyPath,
13919
- options.bootstrapTargetTelemetryEndpoint
13920
- ].some(Boolean) && !bootstrapEnabled) {
13921
- throw new Error("bootstrap pull, SSH credential, public key and target telemetry endpoint must be supplied together");
14034
+ const bootstrapIdentityEnabled = Boolean(options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath);
14035
+ if (Boolean(options.bootstrapSshCredentialPath) !== Boolean(options.bootstrapSshPublicKeyPath)) {
14036
+ throw new Error("bootstrap SSH credential and public key paths must be supplied together");
14037
+ }
14038
+ const bootstrapEnabled = Boolean(options.pullBootstrap);
14039
+ if (bootstrapEnabled && (!bootstrapIdentityEnabled || !options.bootstrapTargetTelemetryEndpoint) || !bootstrapEnabled && options.bootstrapTargetTelemetryEndpoint) {
14040
+ throw new Error("bootstrap pull, SSH identity and target telemetry endpoint must be supplied together");
13922
14041
  }
13923
14042
  const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
13924
- const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
14043
+ const bootstrapSshCredentialPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
13925
14044
  const warpValues = [
13926
14045
  options.warpOrganization,
13927
14046
  options.warpClientIdCredentialPath,
@@ -13975,6 +14094,8 @@ function agentUnit(options) {
13975
14094
  options.gitPublicKeyPath ? `FZ_GIT_PUBLIC_KEY_FILE=${options.gitPublicKeyPath}` : null,
13976
14095
  options.repository ? `FZ_DEPLOY_REPO=${options.repository}` : null,
13977
14096
  options.branch ? `FZ_DEPLOY_BRANCH=${options.branch}` : null,
14097
+ options.bootstrapBundlePath ? `FZ_BOOTSTRAP_BUNDLE=${options.bootstrapBundlePath}` : null,
14098
+ options.bootstrapBundleManifestPath ? `FZ_BOOTSTRAP_BUNDLE_MANIFEST=${options.bootstrapBundleManifestPath}` : null,
13978
14099
  options.profile ? `FZ_DEPLOY_PROFILE=${options.profile}` : null,
13979
14100
  options.repository && options.branch ? `FZ_DEPLOY_KEY=${options.project ?? "platform"}:${options.environment ?? "production"}` : null,
13980
14101
  deploymentEnabled ? `FZ_DEPLOY_ROOT=${deployRoot}` : null,
@@ -14158,15 +14279,13 @@ function planProvision(options) {
14158
14279
  if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
14159
14280
  throw new Error("migration pull and lifecycle profile must be supplied together");
14160
14281
  }
14161
- const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath && options.bootstrapTargetTelemetryEndpoint);
14162
- if ([
14163
- options.pullBootstrap,
14164
- options.bootstrapSshCredentialPath,
14165
- options.bootstrapSshPublicKeyPath,
14166
- options.bootstrapSshSourcePath,
14167
- options.bootstrapTargetTelemetryEndpoint
14168
- ].some(Boolean) && !bootstrapEnabled) {
14169
- throw new Error("bootstrap pull, SSH credential, public key and target telemetry endpoint must be supplied together");
14282
+ const bootstrapIdentityEnabled = Boolean(options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath);
14283
+ if (Boolean(options.bootstrapSshCredentialPath) !== Boolean(options.bootstrapSshPublicKeyPath) || options.bootstrapSshSourcePath && !bootstrapIdentityEnabled) {
14284
+ throw new Error("bootstrap SSH credential and public key paths must be supplied together");
14285
+ }
14286
+ const bootstrapEnabled = Boolean(options.pullBootstrap);
14287
+ if (bootstrapEnabled && (!bootstrapIdentityEnabled || !options.bootstrapTargetTelemetryEndpoint) || !bootstrapEnabled && options.bootstrapTargetTelemetryEndpoint) {
14288
+ throw new Error("bootstrap pull, SSH identity and target telemetry endpoint must be supplied together");
14170
14289
  }
14171
14290
  const warpValues = [
14172
14291
  options.warpOrganization,
@@ -14176,13 +14295,13 @@ function planProvision(options) {
14176
14295
  const warpEnabled = warpValues.every(Boolean);
14177
14296
  if (warpValues.some(Boolean) && !warpEnabled)
14178
14297
  throw new Error("WARP configuration must be supplied together");
14179
- const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath && options.enrolStatePath);
14180
- if (Boolean(options.enrolTokenCredentialPath) !== Boolean(options.enrolStatePath) || options.enrolTokenSourcePath && !enrolmentEnabled) {
14181
- throw new Error("direct enrolment credential and state paths must be supplied together");
14298
+ const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath);
14299
+ if (options.enrolTokenCredentialPath && !options.enrolStatePath || options.enrolTokenSourcePath && (!options.enrolTokenCredentialPath || !options.enrolStatePath)) {
14300
+ throw new Error("direct enrolment credential requires its durable state path");
14182
14301
  }
14183
14302
  const enrolTokenSourcePath = options.enrolTokenSourcePath ? systemdPath(options.enrolTokenSourcePath, "enrolment source") : undefined;
14184
14303
  const enrolTokenCredentialPath = enrolmentEnabled ? systemdPath(options.enrolTokenCredentialPath, "enrolment credential") : undefined;
14185
- const enrolStatePath = enrolmentEnabled ? systemdPath(options.enrolStatePath, "enrolment state") : undefined;
14304
+ const enrolStatePath = options.enrolStatePath ? systemdPath(options.enrolStatePath, "enrolment state") : undefined;
14186
14305
  const enrolStateDir = enrolStatePath?.replace(/\/[^/]+$/, "");
14187
14306
  const sourceBinPath = options.sourceBinPath ? systemdPath(options.sourceBinPath, "agent source binary") : undefined;
14188
14307
  const binPath = options.binPath ? systemdPath(options.binPath, "agent binary") : undefined;
@@ -14194,8 +14313,8 @@ function planProvision(options) {
14194
14313
  const gitPublicKeyDir = gitPublicKeyPath?.replace(/\/[^/]+$/, "");
14195
14314
  const lifecycleProfilePath = lifecycleEnabled ? systemdPath(options.lifecycleProfilePath, "lifecycle profile") : undefined;
14196
14315
  const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
14197
- const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
14198
- const bootstrapSshPublicKeyPath = bootstrapEnabled ? systemdPath(options.bootstrapSshPublicKeyPath, "bootstrap SSH public key") : undefined;
14316
+ const bootstrapSshCredentialPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
14317
+ const bootstrapSshPublicKeyPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshPublicKeyPath, "bootstrap SSH public key") : undefined;
14199
14318
  const bootstrapSshSourcePath = options.bootstrapSshSourcePath ? systemdPath(options.bootstrapSshSourcePath, "bootstrap SSH private-key source") : undefined;
14200
14319
  const bootstrapSshPublicKeyDir = bootstrapSshPublicKeyPath?.replace(/\/[^/]+$/, "");
14201
14320
  const warpClientIdCredentialPath = warpEnabled ? systemdPath(options.warpClientIdCredentialPath, "WARP client-id credential") : undefined;
@@ -14295,7 +14414,7 @@ function planProvision(options) {
14295
14414
  step2("Git deploy identity directory", { kind: "directories", directories: [{ path: gitPublicKeyDir, mode: 493, owner: "root", group: "root" }] }),
14296
14415
  step2("unique encrypted Git deploy identity", { kind: "ensure-git-identity", credential: gitCredentialPath, publicKey: gitPublicKeyPath })
14297
14416
  ] : [],
14298
- ...bootstrapEnabled ? [
14417
+ ...bootstrapIdentityEnabled ? [
14299
14418
  step2("bootstrap SSH public identity directory", { kind: "directories", directories: [
14300
14419
  { path: bootstrapSshPublicKeyDir, mode: 493, owner: "root", group: "root" }
14301
14420
  ] }),
@@ -14331,6 +14450,10 @@ function planProvision(options) {
14331
14450
  { argv: ["/usr/bin/rm", "-f", "/etc/systemd/system/forgezero-deploy-runner.socket"] },
14332
14451
  { argv: ["/usr/bin/systemctl", "daemon-reload"] }
14333
14452
  ] })] : [],
14453
+ ...enrolStatePath && !enrolmentEnabled ? [step2("retire consumed enrolment unit", { kind: "commands", commands: [
14454
+ { argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-agent-enrol.service"], acceptedExitCodes: [0, 1, 5] },
14455
+ { argv: ["/usr/bin/rm", "-f", ENROLMENT_UNIT_PATH] }
14456
+ ] })] : [],
14334
14457
  step2("enable and converge services", { kind: "commands", commands: [
14335
14458
  { argv: ["/usr/bin/systemctl", "enable", ...enabledUnits] },
14336
14459
  { argv: ["/usr/bin/systemctl", "reset-failed", "forgezero-agent.service"], acceptedExitCodes: [0, 1] },
@@ -14357,19 +14480,19 @@ function planProvision(options) {
14357
14480
  }
14358
14481
 
14359
14482
  // src/cli/agent-install.ts
14360
- import { randomBytes as randomBytes5 } from "crypto";
14483
+ import { randomBytes as randomBytes6 } from "crypto";
14361
14484
  import {
14362
- chmodSync as chmodSync15,
14485
+ chmodSync as chmodSync16,
14363
14486
  copyFileSync as copyFileSync3,
14364
- existsSync as existsSync18,
14365
- lstatSync as lstatSync3,
14366
- mkdirSync as mkdirSync13,
14367
- readFileSync as readFileSync15,
14487
+ existsSync as existsSync19,
14488
+ lstatSync as lstatSync4,
14489
+ mkdirSync as mkdirSync14,
14490
+ readFileSync as readFileSync16,
14368
14491
  realpathSync as realpathSync6,
14369
- renameSync as renameSync10,
14370
- rmSync as rmSync9,
14492
+ renameSync as renameSync11,
14493
+ rmSync as rmSync10,
14371
14494
  symlinkSync as symlinkSync5,
14372
- writeFileSync as writeFileSync13
14495
+ writeFileSync as writeFileSync14
14373
14496
  } from "fs";
14374
14497
  import { dirname as dirname9 } from "path";
14375
14498
  async function readCapabilities(run2) {
@@ -14430,52 +14553,52 @@ var runProvisionOperation = async (operation) => {
14430
14553
  }
14431
14554
  if (operation.kind === "install-runtime") {
14432
14555
  const release = `/opt/forgezero/agent/versions/${operation.version}`;
14433
- mkdirSync13(`${release}/dist`, { recursive: true, mode: 493 });
14434
- mkdirSync13(dirname9(operation.binary), { recursive: true, mode: 493 });
14556
+ mkdirSync14(`${release}/dist`, { recursive: true, mode: 493 });
14557
+ mkdirSync14(dirname9(operation.binary), { recursive: true, mode: 493 });
14435
14558
  copyFileSync3(operation.source, `${release}/dist/fz-agent.js`);
14436
- chmodSync15(`${release}/dist/fz-agent.js`, 493);
14559
+ chmodSync16(`${release}/dist/fz-agent.js`, 493);
14437
14560
  const gitSshSource = `${dirname9(operation.source)}/fz-git-ssh.js`;
14438
- if (!existsSync18(gitSshSource))
14561
+ if (!existsSync19(gitSshSource))
14439
14562
  return { stdout: "packaged fz-git-ssh.js is missing", exitCode: 1 };
14440
14563
  copyFileSync3(gitSshSource, `${release}/dist/fz-git-ssh.js`);
14441
- chmodSync15(`${release}/dist/fz-git-ssh.js`, 493);
14564
+ chmodSync16(`${release}/dist/fz-git-ssh.js`, 493);
14442
14565
  const pending = "/opt/forgezero/agent/current.next";
14443
- rmSync9(pending, { force: true });
14566
+ rmSync10(pending, { force: true });
14444
14567
  symlinkSync5(`versions/${operation.version}`, pending);
14445
- renameSync10(pending, "/opt/forgezero/agent/current");
14446
- rmSync9(operation.binary, { force: true });
14568
+ renameSync11(pending, "/opt/forgezero/agent/current");
14569
+ rmSync10(operation.binary, { force: true });
14447
14570
  symlinkSync5("/opt/forgezero/agent/current/dist/fz-agent.js", operation.binary);
14448
14571
  const gitSshBinary = "/usr/local/lib/forgezero/agent/fz-git-ssh";
14449
- rmSync9(gitSshBinary, { force: true });
14572
+ rmSync10(gitSshBinary, { force: true });
14450
14573
  symlinkSync5("/opt/forgezero/agent/current/dist/fz-git-ssh.js", gitSshBinary);
14451
14574
  return { stdout: "", exitCode: 0 };
14452
14575
  }
14453
14576
  if (operation.kind === "ensure-seed") {
14454
- if (existsSync18(operation.credential) && lstatSync3(operation.credential).size > 0)
14577
+ if (existsSync19(operation.credential) && lstatSync4(operation.credential).size > 0)
14455
14578
  return { stdout: "", exitCode: 0 };
14456
- const seed = randomBytes5(32).toString("base64url");
14579
+ const seed = randomBytes6(32).toString("base64url");
14457
14580
  const result = await fixed(["/usr/bin/systemd-creds", "encrypt", "--name=agent-seed", "-", operation.credential], seed);
14458
14581
  if (result.exitCode === 0)
14459
- chmodSync15(operation.credential, 256);
14582
+ chmodSync16(operation.credential, 256);
14460
14583
  return result;
14461
14584
  }
14462
14585
  if (operation.kind === "ensure-git-identity") {
14463
14586
  const key = "/run/forgezero-git-deploy-key";
14464
14587
  const publicKey = `${key}.pub`;
14465
14588
  try {
14466
- if (!existsSync18(operation.credential) || lstatSync3(operation.credential).size < 1) {
14467
- rmSync9(key, { force: true });
14468
- rmSync9(publicKey, { force: true });
14589
+ if (!existsSync19(operation.credential) || lstatSync4(operation.credential).size < 1) {
14590
+ rmSync10(key, { force: true });
14591
+ rmSync10(publicKey, { force: true });
14469
14592
  let result = await fixed(["/usr/bin/ssh-keygen", "-q", "-t", "ed25519", "-N", "", "-C", "forgezero-compute", "-f", key]);
14470
14593
  if (result.exitCode !== 0)
14471
14594
  return result;
14472
14595
  result = await fixed(["/usr/bin/systemd-creds", "encrypt", "--name=git-deploy-key", key, operation.credential]);
14473
14596
  if (result.exitCode !== 0)
14474
14597
  return result;
14475
- chmodSync15(operation.credential, 256);
14598
+ chmodSync16(operation.credential, 256);
14476
14599
  }
14477
- if (!existsSync18(operation.publicKey) || lstatSync3(operation.publicKey).size < 1) {
14478
- if (!existsSync18(key)) {
14600
+ if (!existsSync19(operation.publicKey) || lstatSync4(operation.publicKey).size < 1) {
14601
+ if (!existsSync19(key)) {
14479
14602
  const decrypted = await fixed(["/usr/bin/systemd-creds", "decrypt", "--name=git-deploy-key", operation.credential, key]);
14480
14603
  if (decrypted.exitCode !== 0)
14481
14604
  return decrypted;
@@ -14483,30 +14606,30 @@ var runProvisionOperation = async (operation) => {
14483
14606
  const derived = await fixed(["/usr/bin/ssh-keygen", "-y", "-f", key]);
14484
14607
  if (derived.exitCode !== 0)
14485
14608
  return derived;
14486
- writeFileSync13(operation.publicKey, `${derived.stdout.trim()} forgezero-compute
14609
+ writeFileSync14(operation.publicKey, `${derived.stdout.trim()} forgezero-compute
14487
14610
  `, { mode: 292 });
14488
14611
  }
14489
14612
  return { stdout: "", exitCode: 0 };
14490
14613
  } finally {
14491
- rmSync9(key, { force: true });
14492
- rmSync9(publicKey, { force: true });
14614
+ rmSync10(key, { force: true });
14615
+ rmSync10(publicKey, { force: true });
14493
14616
  }
14494
14617
  }
14495
14618
  if (operation.kind === "ensure-bootstrap-ssh-identity") {
14496
14619
  const key = "/run/forgezero-bootstrap-ssh-key";
14497
14620
  const generatedPublicKey = `${key}.pub`;
14498
14621
  try {
14499
- if (!existsSync18(operation.credential) || lstatSync3(operation.credential).size < 1) {
14500
- rmSync9(key, { force: true });
14501
- rmSync9(generatedPublicKey, { force: true });
14622
+ if (!existsSync19(operation.credential) || lstatSync4(operation.credential).size < 1) {
14623
+ rmSync10(key, { force: true });
14624
+ rmSync10(generatedPublicKey, { force: true });
14502
14625
  let result;
14503
14626
  if (operation.source) {
14504
- const source = existsSync18(operation.source) ? lstatSync3(operation.source) : undefined;
14627
+ const source = existsSync19(operation.source) ? lstatSync4(operation.source) : undefined;
14505
14628
  if (!source?.isFile() || source.isSymbolicLink() || source.uid !== 0 || source.nlink !== 1 || (source.mode & 63) !== 0 || source.size < 32 || source.size > 16 * 1024) {
14506
14629
  return { stdout: "bootstrap SSH private-key source is missing or unsafe", exitCode: 1 };
14507
14630
  }
14508
14631
  copyFileSync3(operation.source, key);
14509
- chmodSync15(key, 384);
14632
+ chmodSync16(key, 384);
14510
14633
  } else {
14511
14634
  result = await fixed(["/usr/bin/ssh-keygen", "-q", "-t", "ed25519", "-N", "", "-C", "forgezero-bootstrap-runner", "-f", key]);
14512
14635
  if (result.exitCode !== 0)
@@ -14519,48 +14642,48 @@ var runProvisionOperation = async (operation) => {
14519
14642
  result = await fixed(["/usr/bin/systemd-creds", "encrypt", "--name=bootstrap-ssh-key", key, operation.credential]);
14520
14643
  if (result.exitCode !== 0)
14521
14644
  return result;
14522
- chmodSync15(operation.credential, 256);
14645
+ chmodSync16(operation.credential, 256);
14523
14646
  }
14524
- if (!existsSync18(operation.publicKey) || lstatSync3(operation.publicKey).size < 1) {
14525
- if (!existsSync18(key)) {
14647
+ if (!existsSync19(operation.publicKey) || lstatSync4(operation.publicKey).size < 1) {
14648
+ if (!existsSync19(key)) {
14526
14649
  const decrypted = await fixed(["/usr/bin/systemd-creds", "decrypt", "--name=bootstrap-ssh-key", operation.credential, key]);
14527
14650
  if (decrypted.exitCode !== 0)
14528
14651
  return decrypted;
14529
- chmodSync15(key, 384);
14652
+ chmodSync16(key, 384);
14530
14653
  }
14531
14654
  const derived = await fixed(["/usr/bin/ssh-keygen", "-y", "-f", key]);
14532
14655
  if (derived.exitCode !== 0 || !/^ssh-ed25519 [A-Za-z0-9+/]+={0,3}\s*$/.test(derived.stdout)) {
14533
14656
  return { stdout: "bootstrap SSH public key derivation failed", exitCode: 1 };
14534
14657
  }
14535
- mkdirSync13(dirname9(operation.publicKey), { recursive: true, mode: 493 });
14536
- writeFileSync13(operation.publicKey, `${derived.stdout.trim()} forgezero-bootstrap-runner
14658
+ mkdirSync14(dirname9(operation.publicKey), { recursive: true, mode: 493 });
14659
+ writeFileSync14(operation.publicKey, `${derived.stdout.trim()} forgezero-bootstrap-runner
14537
14660
  `, { mode: 292 });
14538
- chmodSync15(operation.publicKey, 292);
14661
+ chmodSync16(operation.publicKey, 292);
14539
14662
  }
14540
14663
  if (operation.source)
14541
- rmSync9(operation.source, { force: true });
14664
+ rmSync10(operation.source, { force: true });
14542
14665
  return { stdout: "", exitCode: 0 };
14543
14666
  } finally {
14544
- rmSync9(key, { force: true });
14545
- rmSync9(generatedPublicKey, { force: true });
14667
+ rmSync10(key, { force: true });
14668
+ rmSync10(generatedPublicKey, { force: true });
14546
14669
  }
14547
14670
  }
14548
14671
  if (operation.kind === "ensure-enrolment") {
14549
- if (existsSync18(operation.state) && lstatSync3(operation.state).size > 0 || existsSync18(operation.credential) && lstatSync3(operation.credential).size > 0)
14672
+ if (existsSync19(operation.state) && lstatSync4(operation.state).size > 0 || existsSync19(operation.credential) && lstatSync4(operation.credential).size > 0)
14550
14673
  return { stdout: "", exitCode: 0 };
14551
- if (!existsSync18(operation.source))
14674
+ if (!existsSync19(operation.source))
14552
14675
  return { stdout: "enrolment source is missing", exitCode: 1 };
14553
14676
  const result = await fixed(["/usr/bin/systemd-creds", "encrypt", "--name=enrol-token", operation.source, operation.credential]);
14554
14677
  if (result.exitCode === 0) {
14555
- chmodSync15(operation.credential, 256);
14556
- rmSync9(operation.source, { force: true });
14678
+ chmodSync16(operation.credential, 256);
14679
+ rmSync10(operation.source, { force: true });
14557
14680
  }
14558
14681
  return result;
14559
14682
  }
14560
14683
  if (operation.kind === "wait-socket") {
14561
14684
  for (let attempt = 0;attempt < operation.attempts; attempt += 1) {
14562
14685
  try {
14563
- if (lstatSync3(operation.path).isSocket())
14686
+ if (lstatSync4(operation.path).isSocket())
14564
14687
  return { stdout: "", exitCode: 0 };
14565
14688
  } catch {}
14566
14689
  await Bun.sleep(operation.intervalMs);
@@ -14568,7 +14691,7 @@ var runProvisionOperation = async (operation) => {
14568
14691
  return { stdout: `socket did not become ready: ${operation.path}`, exitCode: 1 };
14569
14692
  }
14570
14693
  if (operation.kind === "verify-file")
14571
- return existsSync18(operation.path) && lstatSync3(operation.path).size > 0 ? { stdout: "", exitCode: 0 } : { stdout: "required file is empty", exitCode: 1 };
14694
+ return existsSync19(operation.path) && lstatSync4(operation.path).size > 0 ? { stdout: "", exitCode: 0 } : { stdout: "required file is empty", exitCode: 1 };
14572
14695
  if (operation.kind === "verify-egress") {
14573
14696
  const active = await fixed(["/usr/bin/systemctl", "is-active", "forgezero-agent-egress.service"]);
14574
14697
  if (active.exitCode !== 0)
@@ -14590,25 +14713,25 @@ var runProvisionOperation = async (operation) => {
14590
14713
  }
14591
14714
  }
14592
14715
  if (operation.kind === "install-warp") {
14593
- const os = readFileSync15("/etc/os-release", "utf8");
14716
+ const os = readFileSync16("/etc/os-release", "utf8");
14594
14717
  if (!/^ID=ubuntu$/m.test(os) || !/^VERSION_ID="?26\.04"?$/m.test(os))
14595
14718
  return { stdout: "unsupported WARP host OS", exitCode: 1 };
14596
14719
  const response = await fetch("https://pkg.cloudflareclient.com/pubkey.gpg", { signal: AbortSignal.timeout(30000) });
14597
14720
  if (!response.ok)
14598
14721
  return { stdout: `WARP key HTTP ${response.status}`, exitCode: 1 };
14599
- mkdirSync13("/usr/share/keyrings", { recursive: true, mode: 493 });
14600
- mkdirSync13("/etc/apt/sources.list.d", { recursive: true, mode: 493 });
14601
- mkdirSync13("/etc/systemd/system/warp-svc.service.d", { recursive: true, mode: 493 });
14722
+ mkdirSync14("/usr/share/keyrings", { recursive: true, mode: 493 });
14723
+ mkdirSync14("/etc/apt/sources.list.d", { recursive: true, mode: 493 });
14724
+ mkdirSync14("/etc/systemd/system/warp-svc.service.d", { recursive: true, mode: 493 });
14602
14725
  const key = "/run/cloudflare-warp-key.gpg";
14603
- writeFileSync13(key, new Uint8Array(await response.arrayBuffer()), { mode: 384 });
14726
+ writeFileSync14(key, new Uint8Array(await response.arrayBuffer()), { mode: 384 });
14604
14727
  let result = await fixed(["/usr/bin/gpg", "--batch", "--yes", "--dearmor", "-o", "/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg", key]);
14605
- rmSync9(key, { force: true });
14728
+ rmSync10(key, { force: true });
14606
14729
  if (result.exitCode !== 0)
14607
14730
  return result;
14608
14731
  const codename = os.match(/^VERSION_CODENAME=(.+)$/m)?.[1]?.replace(/^"|"$/g, "");
14609
14732
  if (!codename)
14610
14733
  return { stdout: "Ubuntu codename missing", exitCode: 1 };
14611
- writeFileSync13("/etc/apt/sources.list.d/cloudflare-client.list", `deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ ${codename} main
14734
+ writeFileSync14("/etc/apt/sources.list.d/cloudflare-client.list", `deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ ${codename} main
14612
14735
  `, { mode: 420 });
14613
14736
  result = await fixed(["/usr/bin/apt-get", "update", "-qq"]);
14614
14737
  return result.exitCode === 0 ? fixed(["/usr/bin/apt-get", "install", "-y", "cloudflare-warp"]) : result;
@@ -14623,7 +14746,7 @@ async function localRunner(operation) {
14623
14746
  if (capability.kind === "version")
14624
14747
  return fixed(capability.argv);
14625
14748
  try {
14626
- const metadata = lstatSync3(capability.path);
14749
+ const metadata = lstatSync4(capability.path);
14627
14750
  const present = capability.nodeType === "directory" ? metadata.isDirectory() : true;
14628
14751
  return { stdout: present ? `yes
14629
14752
  ` : `no
@@ -14670,6 +14793,7 @@ var SEED_CREDENTIAL = `${CREDS}/seed-sync-root.cred`;
14670
14793
  var BACKUP_RECOVERY_CREDENTIAL = `${CREDS}/backup-recovery-root.cred`;
14671
14794
  var BOOTSTRAP_SSH_CREDENTIAL = `${CREDS}/bootstrap-ssh-key.cred`;
14672
14795
  var BOOTSTRAP_SSH_PUBLIC_KEY = "/etc/forgezero/bootstrap/runner.pub";
14796
+ var BOOTSTRAP_RELEASE_EVIDENCE = "/var/lib/forgezero/bootstrap-release.json";
14673
14797
  var LIFECYCLE_PROFILE = "/etc/forgezero/lifecycle.json";
14674
14798
  var CONTROL_SOCKET = "/run/forgezero/control.sock";
14675
14799
  var CLOUDFLARED_METRICS_ADDRESS = "127.0.0.1:20241";
@@ -14846,6 +14970,65 @@ async function bootstrapStatus(host = localBootstrapHost()) {
14846
14970
  problems.push("durable Agent enrolment state is missing");
14847
14971
  return { initialized: problems.length === 0, kind: state.kind, profile: state.profile, services, problems };
14848
14972
  }
14973
+ function planBootstrapAgentInstall(config, phase, context) {
14974
+ const { capabilities, hasBinding, hasEnrolCredential, initialBundle } = context;
14975
+ if (phase === "bootstrap") {
14976
+ if (config.kind !== "platform" || hasBinding || !initialBundle) {
14977
+ throw new Error("release-one Agent install requires an unbound platform host and verified bootstrap bundle");
14978
+ }
14979
+ } else if (phase === "enrol") {
14980
+ if (hasBinding || !hasEnrolCredential) {
14981
+ throw new Error("Agent enrolment requires one unbound host and its sealed one-use credential");
14982
+ }
14983
+ } else if (!hasBinding) {
14984
+ throw new Error("bound Agent convergence requires durable enrolment state");
14985
+ }
14986
+ const bound = phase === "bound";
14987
+ const enrolling = phase === "enrol";
14988
+ const authenticated = enrolling || bound;
14989
+ const bootstrapRunner = platformBootstrapRunner(config) || config.kind === "enrolled-compute" && Boolean(config.bootstrapRunner);
14990
+ const options = {
14991
+ capabilities,
14992
+ socketPath: DEFAULT_SOCKET,
14993
+ seedPath: "/var/lib/forgezero/node.seed",
14994
+ controlSocketPath: CONTROL_SOCKET,
14995
+ repository: phase === "bootstrap" ? initialBundle.path : bound && config.kind === "enrolled-compute" ? config.repository : undefined,
14996
+ branch: phase === "bootstrap" ? initialBundle.manifest.branch : bound && config.kind === "enrolled-compute" ? config.branch : undefined,
14997
+ bootstrapBundlePath: phase === "bootstrap" ? initialBundle.path : undefined,
14998
+ bootstrapBundleManifestPath: phase === "bootstrap" ? initialBundle.manifestPath : undefined,
14999
+ profile: config.profile,
15000
+ deployRoot: config.deployRoot ?? "/opt/forgezero",
15001
+ deploymentCredentials: config.deploymentCredentials,
15002
+ publicApiUrl: config.apiUrl,
15003
+ gitCredentialPath: authenticated && config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/creds/git-deploy-key.cred" : undefined,
15004
+ gitPublicKeyPath: authenticated && config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/git/deploy.pub" : undefined,
15005
+ generateGitIdentity: authenticated && config.kind === "enrolled-compute" && config.gitDeployKey === true,
15006
+ pullDeployments: bound,
15007
+ pullMigrations: bound && config.kind === "platform",
15008
+ pullBootstrap: bound && bootstrapRunner,
15009
+ bootstrapSshCredentialPath: authenticated && bootstrapRunner ? BOOTSTRAP_SSH_CREDENTIAL : undefined,
15010
+ bootstrapSshSourcePath: enrolling && config.kind === "enrolled-compute" ? config.bootstrapRunner?.sshPrivateKeyFile : undefined,
15011
+ bootstrapSshPublicKeyPath: authenticated && bootstrapRunner ? BOOTSTRAP_SSH_PUBLIC_KEY : undefined,
15012
+ bootstrapTargetTelemetryEndpoint: bound && bootstrapRunner ? platformBootstrapRunner(config) ? config.telemetryEndpoint : config.kind === "enrolled-compute" ? config.bootstrapRunner?.targetTelemetryEndpoint : undefined : undefined,
15013
+ lifecycleProfilePath: bound && config.kind === "platform" ? LIFECYCLE_PROFILE : undefined,
15014
+ enforceEgress: true,
15015
+ nodeHostname: config.nodeHostname,
15016
+ telemetryEndpoint: config.telemetryEndpoint,
15017
+ binPath: "/usr/local/lib/forgezero/agent/fz-agent",
15018
+ sourceBinPath: PACKAGED_AGENT_BIN,
15019
+ ...enrolling ? {
15020
+ enrolTokenCredentialPath: ENROL_CREDENTIAL,
15021
+ enrolStatePath: "/var/lib/forgezero/enrolment.json"
15022
+ } : bound ? { enrolStatePath: "/var/lib/forgezero/enrolment.json" } : {},
15023
+ ...authenticated ? {
15024
+ apiUrl: config.apiUrl,
15025
+ project: config.kind === "enrolled-compute" ? config.realm : "platform",
15026
+ environment: config.kind === "enrolled-compute" ? undefined : config.environment,
15027
+ nodeLabel: config.kind === "enrolled-compute" ? config.nodeHostname : config.computeReference
15028
+ } : {}
15029
+ };
15030
+ return planInstall(options);
15031
+ }
14849
15032
  function localBootstrapHost() {
14850
15033
  const execute3 = async (argv2, options = {}) => {
14851
15034
  const child = Bun.spawn([...argv2], { stdin: options.stdin === undefined ? "ignore" : "pipe", stdout: "pipe", stderr: "pipe" });
@@ -14862,19 +15045,19 @@ function localBootstrapHost() {
14862
15045
  };
14863
15046
  return {
14864
15047
  uid: () => process.getuid?.() ?? -1,
14865
- exists: existsSync19,
14866
- read: (path2) => readFileSync16(path2, "utf8"),
15048
+ exists: existsSync20,
15049
+ read: (path2) => readFileSync17(path2, "utf8"),
14867
15050
  write(path2, content, mode) {
14868
- mkdirSync14(dirname10(path2), { recursive: true, mode: 493 });
15051
+ mkdirSync15(dirname10(path2), { recursive: true, mode: 493 });
14869
15052
  const temporary = `${path2}.next.${process.pid}`;
14870
- writeFileSync14(temporary, content, { mode });
14871
- chmodSync16(temporary, mode);
14872
- renameSync11(temporary, path2);
15053
+ writeFileSync15(temporary, content, { mode });
15054
+ chmodSync17(temporary, mode);
15055
+ renameSync12(temporary, path2);
14873
15056
  },
14874
- mkdir: (path2, mode) => mkdirSync14(path2, { recursive: true, mode }),
14875
- remove: (path2) => rmSync10(path2, { force: true }),
15057
+ mkdir: (path2, mode) => mkdirSync15(path2, { recursive: true, mode }),
15058
+ remove: (path2) => rmSync11(path2, { force: true }),
14876
15059
  inspect(path2) {
14877
- const value = lstatSync4(path2);
15060
+ const value = lstatSync5(path2);
14878
15061
  return { regular: value.isFile(), symbolic: value.isSymbolicLink(), uid: value.uid, mode: value.mode, links: value.nlink, size: value.size };
14879
15062
  },
14880
15063
  exec: execute3,
@@ -14893,10 +15076,15 @@ function localBootstrapHost() {
14893
15076
  throw new Error(`Agent software requirements failed: ${result.output.trim()}`);
14894
15077
  return result;
14895
15078
  },
14896
- async installAgent(config) {
15079
+ async installAgent(config, phase) {
14897
15080
  const capabilities = await readCapabilities(localRunner);
14898
- const deployRoot = config.deployRoot ?? "/opt/forgezero";
14899
- const hasBinding = config.kind === "enrolled-compute" || existsSync19(ENROL_CREDENTIAL) || existsSync19("/var/lib/forgezero/enrolment.json");
15081
+ const hasBinding = existsSync20("/var/lib/forgezero/enrolment.json");
15082
+ const hasEnrolCredential = existsSync20(ENROL_CREDENTIAL);
15083
+ const initialBundle = config.kind === "platform" && !existsSync20(BOOTSTRAP_RELEASE_EVIDENCE) ? {
15084
+ path: config.bootstrapBundle.bundleFile,
15085
+ manifestPath: config.bootstrapBundle.manifestFile,
15086
+ manifest: parseBootstrapBundleManifest(JSON.parse(readFileSync17(config.bootstrapBundle.manifestFile, "utf8")))
15087
+ } : undefined;
14900
15088
  if (config.kind === "platform") {
14901
15089
  const lifecycle = config.database.role === "none" ? {
14902
15090
  apiUnits: ["forgezero@blue.service", "forgezero@green.service"],
@@ -14908,49 +15096,19 @@ function localBootstrapHost() {
14908
15096
  databaseHealthUrl: `http://${config.database.address}:8529/_api/version`,
14909
15097
  databasePorts: [8529]
14910
15098
  };
14911
- mkdirSync14(dirname10(LIFECYCLE_PROFILE), { recursive: true, mode: 493 });
14912
- writeFileSync14(LIFECYCLE_PROFILE, `${JSON.stringify(lifecycle, null, 2)}
15099
+ mkdirSync15(dirname10(LIFECYCLE_PROFILE), { recursive: true, mode: 493 });
15100
+ writeFileSync15(LIFECYCLE_PROFILE, `${JSON.stringify(lifecycle, null, 2)}
14913
15101
  `, { mode: 256 });
14914
15102
  }
14915
- const plan = planInstall({
15103
+ const plan = planBootstrapAgentInstall(config, phase, {
14916
15104
  capabilities,
14917
- socketPath: DEFAULT_SOCKET,
14918
- seedPath: "/var/lib/forgezero/node.seed",
14919
- controlSocketPath: "/run/forgezero/control.sock",
14920
- repository: config.repository,
14921
- branch: config.branch,
14922
- profile: config.kind === "platform" ? config.profile : config.profile,
14923
- deployRoot,
14924
- deploymentCredentials: config.deploymentCredentials,
14925
- publicApiUrl: config.apiUrl,
14926
- gitCredentialPath: "/etc/forgezero/creds/git-deploy-key.cred",
14927
- gitPublicKeyPath: "/etc/forgezero/git/deploy.pub",
14928
- generateGitIdentity: true,
14929
- pullDeployments: hasBinding,
14930
- pullMigrations: config.kind === "platform" && hasBinding,
14931
- pullBootstrap: platformBootstrapRunner(config) || config.kind === "enrolled-compute" && Boolean(config.bootstrapRunner),
14932
- bootstrapSshCredentialPath: platformBootstrapRunner(config) || config.kind === "enrolled-compute" && config.bootstrapRunner ? BOOTSTRAP_SSH_CREDENTIAL : undefined,
14933
- bootstrapSshSourcePath: config.kind === "enrolled-compute" ? config.bootstrapRunner?.sshPrivateKeyFile : undefined,
14934
- bootstrapSshPublicKeyPath: platformBootstrapRunner(config) || config.kind === "enrolled-compute" && config.bootstrapRunner ? BOOTSTRAP_SSH_PUBLIC_KEY : undefined,
14935
- bootstrapTargetTelemetryEndpoint: platformBootstrapRunner(config) ? config.telemetryEndpoint : config.kind === "enrolled-compute" ? config.bootstrapRunner?.targetTelemetryEndpoint : undefined,
14936
- lifecycleProfilePath: config.kind === "platform" ? LIFECYCLE_PROFILE : undefined,
14937
- enforceEgress: true,
14938
- nodeHostname: config.nodeHostname,
14939
- telemetryEndpoint: config.telemetryEndpoint,
14940
- binPath: "/usr/local/lib/forgezero/agent/fz-agent",
14941
- sourceBinPath: PACKAGED_AGENT_BIN,
14942
- ...hasBinding ? {
14943
- enrolTokenCredentialPath: ENROL_CREDENTIAL,
14944
- enrolStatePath: "/var/lib/forgezero/enrolment.json",
14945
- apiUrl: config.apiUrl,
14946
- project: config.kind === "enrolled-compute" ? config.realm : "platform",
14947
- environment: config.kind === "enrolled-compute" ? undefined : config.environment,
14948
- nodeLabel: config.kind === "enrolled-compute" ? config.nodeHostname : config.computeReference
14949
- } : {}
15105
+ hasBinding,
15106
+ hasEnrolCredential,
15107
+ initialBundle
14950
15108
  });
14951
15109
  for (const unit3 of [{ path: plan.unitPath, unit: plan.unit }, ...plan.auxiliaryUnits]) {
14952
- mkdirSync14(dirname10(unit3.path), { recursive: true, mode: 493 });
14953
- writeFileSync14(unit3.path, unit3.unit, { mode: 420 });
15110
+ mkdirSync15(dirname10(unit3.path), { recursive: true, mode: 493 });
15111
+ writeFileSync15(unit3.path, unit3.unit, { mode: 420 });
14954
15112
  }
14955
15113
  await applyPlan(plan, localRunner);
14956
15114
  return plan;
@@ -14964,7 +15122,7 @@ var localRuntime = () => ({
14964
15122
  bootstrapStatus: () => bootstrapStatus(),
14965
15123
  characterDevice(path2) {
14966
15124
  try {
14967
- return lstatSync5(path2).isCharacterDevice();
15125
+ return lstatSync6(path2).isCharacterDevice();
14968
15126
  } catch {
14969
15127
  return false;
14970
15128
  }
@@ -15002,8 +15160,8 @@ async function verifyPlatformFleetHost(expectedVersion, runtime = localRuntime()
15002
15160
  }
15003
15161
 
15004
15162
  // src/community-rehearsal-host.ts
15005
- import { createHash as createHash10 } from "crypto";
15006
- import { lstatSync as lstatSync6, mkdirSync as mkdirSync15, readFileSync as readFileSync17, rmSync as rmSync11, symlinkSync as symlinkSync6, unlinkSync as unlinkSync12, writeFileSync as writeFileSync15 } from "fs";
15163
+ import { createHash as createHash11 } from "crypto";
15164
+ import { lstatSync as lstatSync7, mkdirSync as mkdirSync16, readFileSync as readFileSync18, rmSync as rmSync12, symlinkSync as symlinkSync6, unlinkSync as unlinkSync12, writeFileSync as writeFileSync16 } from "fs";
15007
15165
  import { dirname as dirname11 } from "path";
15008
15166
  var COMMUNITY_REHEARSAL_VERSION = "3.11.14";
15009
15167
  var COMMUNITY_REHEARSAL_ROOT = "/opt/forgezero-rehearsals/community-cluster-api";
@@ -15200,10 +15358,10 @@ async function exec(argv2, stdin) {
15200
15358
  async function applyOperations(operations) {
15201
15359
  for (const operation of operations) {
15202
15360
  if (operation.kind === "remove-tree")
15203
- rmSync11(operation.path, { recursive: true, force: true });
15361
+ rmSync12(operation.path, { recursive: true, force: true });
15204
15362
  else if (operation.kind === "write") {
15205
- mkdirSync15(dirname11(operation.path), { recursive: true });
15206
- writeFileSync15(operation.path, operation.content, { mode: operation.mode });
15363
+ mkdirSync16(dirname11(operation.path), { recursive: true });
15364
+ writeFileSync16(operation.path, operation.content, { mode: operation.mode });
15207
15365
  } else if (operation.kind === "unlink") {
15208
15366
  try {
15209
15367
  unlinkSync12(operation.path);
@@ -15251,8 +15409,8 @@ async function runCommunityRehearsalHost(request) {
15251
15409
  return { ok: true, action: request.action, node: node.name };
15252
15410
  }
15253
15411
  if (request.action === "prepare") {
15254
- const metadata = lstatSync6(request.archivePath);
15255
- if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size < 1 || metadata.size > 64 * 1024 * 1024 || createHash10("sha256").update(readFileSync17(request.archivePath)).digest("hex") !== request.archiveSha256) {
15412
+ const metadata = lstatSync7(request.archivePath);
15413
+ if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size < 1 || metadata.size > 64 * 1024 * 1024 || createHash11("sha256").update(readFileSync18(request.archivePath)).digest("hex") !== request.archiveSha256) {
15256
15414
  throw new Error("community rehearsal archive is not the declared bounded release");
15257
15415
  }
15258
15416
  await ensureCommunityRehearsalArango();
@@ -15300,13 +15458,13 @@ async function runCommunityRehearsalHost(request) {
15300
15458
  }
15301
15459
 
15302
15460
  // src/supervised-app.ts
15303
- import { lstatSync as lstatSync7, readFileSync as readFileSync18 } from "fs";
15461
+ import { lstatSync as lstatSync8, readFileSync as readFileSync19 } from "fs";
15304
15462
  function readConfig(path2) {
15305
- const stat = lstatSync7(path2);
15463
+ const stat = lstatSync8(path2);
15306
15464
  if (!stat.isFile() || stat.isSymbolicLink() || stat.uid !== 0 || (stat.mode & 18) !== 0 || stat.size > 128 * 1024) {
15307
15465
  throw new Error("supervised app config is unsafe");
15308
15466
  }
15309
- const value = JSON.parse(readFileSync18(path2, "utf8"));
15467
+ const value = JSON.parse(readFileSync19(path2, "utf8"));
15310
15468
  if (value.format !== 1 || typeof value.root !== "string" || !value.root.startsWith("/") || typeof value.release !== "string" || !value.release.startsWith(`${value.root}/releases/`) || typeof value.home !== "string" || value.home !== `${value.root}/app-home` || !Array.isArray(value.argv) || value.argv.length < 1 || value.argv.length > 256 || value.argv.some((argument) => typeof argument !== "string" || !argument || argument.includes("\x00")) || !value.environment || typeof value.environment !== "object" || Array.isArray(value.environment) || Object.entries(value.environment).some(([name, item]) => !/^FZ_(?:APP_PORT|RELEASE)$/.test(name) || typeof item !== "string" || /[\r\n\0]/.test(item))) {
15311
15469
  throw new Error("supervised app config is malformed");
15312
15470
  }
@@ -15352,17 +15510,17 @@ function agentCommandArgs(args, command2) {
15352
15510
  return args.slice(index + 1);
15353
15511
  }
15354
15512
  function loadOrCreateSeed(path2) {
15355
- if (existsSync20(path2)) {
15356
- const seed2 = new Uint8Array(Buffer.from(readFileSync19(path2, "utf8").trim(), "base64url"));
15513
+ if (existsSync21(path2)) {
15514
+ const seed2 = new Uint8Array(Buffer.from(readFileSync20(path2, "utf8").trim(), "base64url"));
15357
15515
  if (seed2.length < 32) {
15358
15516
  throw new Error(`agent: the seed at ${path2} is too short to derive a key from.`);
15359
15517
  }
15360
15518
  return seed2;
15361
15519
  }
15362
- mkdirSync16(dirname12(path2), { recursive: true });
15363
- const seed = new Uint8Array(randomBytes6(32));
15364
- writeFileSync16(path2, Buffer.from(seed).toString("base64url"), { mode: 384 });
15365
- chmodSync17(path2, 384);
15520
+ mkdirSync17(dirname12(path2), { recursive: true });
15521
+ const seed = new Uint8Array(randomBytes7(32));
15522
+ writeFileSync17(path2, Buffer.from(seed).toString("base64url"), { mode: 384 });
15523
+ chmodSync18(path2, 384);
15366
15524
  return seed;
15367
15525
  }
15368
15526
  var DEFAULT_SOCKET_PATH = DEFAULT_SOCKET;
@@ -15391,9 +15549,9 @@ function loadSeedCredential(name = DEFAULT_SEED_CREDENTIAL, directory = process.
15391
15549
  if (!directory)
15392
15550
  throw new Error("agent: CREDENTIALS_DIRECTORY is missing; systemd did not load the node identity.");
15393
15551
  const path2 = `${directory}/${name}`;
15394
- if (!existsSync20(path2))
15552
+ if (!existsSync21(path2))
15395
15553
  throw new Error(`agent: the systemd credential ${name} is missing at ${path2}.`);
15396
- const seed = new Uint8Array(Buffer.from(readFileSync19(path2, "utf8").trim(), "base64url"));
15554
+ const seed = new Uint8Array(Buffer.from(readFileSync20(path2, "utf8").trim(), "base64url"));
15397
15555
  if (seed.length < 32)
15398
15556
  throw new Error(`agent: the systemd credential ${name} is too short to derive a key from.`);
15399
15557
  return seed;
@@ -15403,7 +15561,7 @@ function loadTextCredential(name, directory = process.env.CREDENTIALS_DIRECTORY)
15403
15561
  throw new Error("agent: CREDENTIALS_DIRECTORY is missing; systemd did not load the credential.");
15404
15562
  if (!/^[A-Za-z0-9_.-]+$/.test(name))
15405
15563
  throw new Error("agent: invalid systemd credential name.");
15406
- const value = readFileSync19(`${directory}/${name}`, "utf8").trim();
15564
+ const value = readFileSync20(`${directory}/${name}`, "utf8").trim();
15407
15565
  if (!value)
15408
15566
  throw new Error(`agent: systemd credential ${name} is empty.`);
15409
15567
  return value;
@@ -15528,7 +15686,7 @@ if (import.meta.main) {
15528
15686
  if (configPath !== "/etc/forgezero/deploy-activation.json" || !release) {
15529
15687
  throw new Error("platform-activate requires the fixed config and one absolute release path");
15530
15688
  }
15531
- const config = JSON.parse(readFileSync19(configPath, "utf8"));
15689
+ const config = JSON.parse(readFileSync20(configPath, "utf8"));
15532
15690
  const result = await activatePlatformRelease(config, release);
15533
15691
  console.log(`promoted ${result.release} on ${result.slot}`);
15534
15692
  process.exit(0);
@@ -15553,13 +15711,13 @@ if (import.meta.main) {
15553
15711
  if (args.length !== 1)
15554
15712
  throw new Error("project-release-check accepts no coordinates");
15555
15713
  for (const relative of ["src/index.ts", "bun.lock", "src/generated/shared/contract-manifest.json"]) {
15556
- const stat = lstatSync8(join12(process.cwd(), relative));
15714
+ const stat = lstatSync9(join12(process.cwd(), relative));
15557
15715
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size < 1) {
15558
15716
  throw new Error(`project release is missing ${relative}`);
15559
15717
  }
15560
15718
  }
15561
15719
  for (const relative of ["package.json", "bun.lock"]) {
15562
- const contents = readFileSync19(join12(process.cwd(), relative), "utf8");
15720
+ const contents = readFileSync20(join12(process.cwd(), relative), "utf8");
15563
15721
  if (/(?:workspace|file):/.test(contents)) {
15564
15722
  throw new Error(`project release ${relative} contains a local dependency`);
15565
15723
  }
@@ -15604,8 +15762,8 @@ if (import.meta.main) {
15604
15762
  keys: keys2,
15605
15763
  label: process.env.FZ_NODE_LABEL,
15606
15764
  edgeHostname: process.env.FZ_NODE_HOSTNAME,
15607
- gitDeployPublicKey: process.env.FZ_GIT_PUBLIC_KEY_FILE ? readFileSync19(process.env.FZ_GIT_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
15608
- bootstrapSshPublicKey: process.env.FZ_BOOTSTRAP_SSH_PUBLIC_KEY_FILE ? readFileSync19(process.env.FZ_BOOTSTRAP_SSH_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
15765
+ gitDeployPublicKey: process.env.FZ_GIT_PUBLIC_KEY_FILE ? readFileSync20(process.env.FZ_GIT_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
15766
+ bootstrapSshPublicKey: process.env.FZ_BOOTSTRAP_SSH_PUBLIC_KEY_FILE ? readFileSync20(process.env.FZ_BOOTSTRAP_SSH_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
15609
15767
  privateNetworkAttachment: privateNetworkAttachmentFromEnvironment()
15610
15768
  });
15611
15769
  console.log(`[agent] enrolled ${binding2.computeReference} in project ${binding2.projectKey}/${binding2.environmentKey}`);
@@ -15647,7 +15805,7 @@ if (import.meta.main) {
15647
15805
  const profilePath = args.find((arg) => arg.startsWith("--profile="))?.slice("--profile=".length);
15648
15806
  if (!profilePath)
15649
15807
  throw new Error("metal-helper requires --profile=/absolute/path.json");
15650
- const profile2 = JSON.parse(readFileSync19(profilePath, "utf8"));
15808
+ const profile2 = JSON.parse(readFileSync20(profilePath, "utf8"));
15651
15809
  const helper = startMetalHelper({
15652
15810
  profile: profile2,
15653
15811
  socketPath: process.env.FZ_METAL_HELPER_SOCKET ?? DEFAULT_METAL_HELPER_SOCKET
@@ -15797,7 +15955,7 @@ if (import.meta.main) {
15797
15955
  const profilePath = args.find((arg) => arg.startsWith("--profile="))?.slice("--profile=".length);
15798
15956
  if (!profilePath)
15799
15957
  throw new Error("metal-isolation requires --profile=/absolute/path.json");
15800
- const profile2 = JSON.parse(readFileSync19(profilePath, "utf8"));
15958
+ const profile2 = JSON.parse(readFileSync20(profilePath, "utf8"));
15801
15959
  await applyMetalIsolation(profile2);
15802
15960
  console.log("[metal-isolation] host and guest cgroup boundaries active");
15803
15961
  process.exit(0);
@@ -15842,7 +16000,7 @@ if (import.meta.main) {
15842
16000
  if (claimArg !== "-" && !claimArg.startsWith("/")) {
15843
16001
  throw new Error("metal-apply claim path must be absolute");
15844
16002
  }
15845
- const raw = readFileSync19(claimArg === "-" ? "/dev/stdin" : claimArg, "utf8");
16003
+ const raw = readFileSync20(claimArg === "-" ? "/dev/stdin" : claimArg, "utf8");
15846
16004
  if (Buffer.byteLength(raw) > 32 * 1024)
15847
16005
  throw new Error("metal-apply claim exceeds 32 KiB");
15848
16006
  const claim = JSON.parse(raw);
@@ -15867,7 +16025,7 @@ if (import.meta.main) {
15867
16025
  if (commandArgs.length !== 1 || commandArgs[0] !== "--request=-") {
15868
16026
  throw new Error("community-rehearsal requires exactly --request=-");
15869
16027
  }
15870
- const raw = readFileSync19("/dev/stdin", "utf8");
16028
+ const raw = readFileSync20("/dev/stdin", "utf8");
15871
16029
  if (Buffer.byteLength(raw) > 128 * 1024)
15872
16030
  throw new Error("community rehearsal request exceeds 128 KiB");
15873
16031
  const request = parseCommunityRehearsalHostRequest(JSON.parse(raw));
@@ -15878,7 +16036,7 @@ if (import.meta.main) {
15878
16036
  if (args.length !== 1 || args[0] !== "--request=-") {
15879
16037
  throw new Error("metal-admission-proof requires exactly --request=-");
15880
16038
  }
15881
- const raw = readFileSync19("/dev/stdin", "utf8");
16039
+ const raw = readFileSync20("/dev/stdin", "utf8");
15882
16040
  if (Buffer.byteLength(raw) > 32 * 1024)
15883
16041
  throw new Error("metal admission proof request exceeds 32 KiB");
15884
16042
  const request = JSON.parse(raw);
@@ -15896,9 +16054,9 @@ if (import.meta.main) {
15896
16054
  vcpu: cpus().length,
15897
16055
  memoryGib: Math.max(1, Math.floor(totalmem() / 1024 ** 3)),
15898
16056
  diskGib: Math.max(1, Math.floor(Number(filesystem.blocks) * Number(filesystem.bsize) / 1024 ** 3)),
15899
- kvm: existsSync20("/dev/kvm"),
15900
- snpHost: existsSync20("/dev/sev"),
15901
- helper: existsSync20(process.env.FZ_METAL_HELPER_SOCKET ?? DEFAULT_METAL_HELPER_SOCKET)
16057
+ kvm: existsSync21("/dev/kvm"),
16058
+ snpHost: existsSync21("/dev/sev"),
16059
+ helper: existsSync21(process.env.FZ_METAL_HELPER_SOCKET ?? DEFAULT_METAL_HELPER_SOCKET)
15902
16060
  }
15903
16061
  };
15904
16062
  const envelope = signRequest(keys2, keys2.ed25519.publicKey, {
@@ -15933,9 +16091,9 @@ if (import.meta.main) {
15933
16091
  metalHostname: process.env.FZ_METAL_HOSTNAME,
15934
16092
  run: (claim) => requestMetalProvision(claim, process.env.FZ_METAL_HELPER_SOCKET ?? DEFAULT_METAL_HELPER_SOCKET),
15935
16093
  metalPreflight: () => ({
15936
- snpHost: existsSync20("/dev/sev"),
15937
- kvm: existsSync20("/dev/kvm"),
15938
- helper: existsSync20(process.env.FZ_METAL_HELPER_SOCKET ?? DEFAULT_METAL_HELPER_SOCKET)
16094
+ snpHost: existsSync21("/dev/sev"),
16095
+ kvm: existsSync21("/dev/kvm"),
16096
+ helper: existsSync21(process.env.FZ_METAL_HELPER_SOCKET ?? DEFAULT_METAL_HELPER_SOCKET)
15939
16097
  }),
15940
16098
  onEvent: (event, detail) => console.log(`[metal-agent] ${event}${detail ? ` ${JSON.stringify(detail)}` : ""}`)
15941
16099
  });
@@ -16018,7 +16176,7 @@ if (import.meta.main) {
16018
16176
  const telemetry = new AgentTelemetryRuntime(createAgentTelemetry(resolveAgentTelemetryConfig()));
16019
16177
  telemetry.event("agent.started");
16020
16178
  telemetry.setDraining(false);
16021
- const attestationSource = existsSync20("/dev/sev-guest") ? createSnpAttestationSource() : undefined;
16179
+ const attestationSource = existsSync21("/dev/sev-guest") ? createSnpAttestationSource() : undefined;
16022
16180
  const running = runAgent({
16023
16181
  socketPath: process.env.FZ_SOCKET_PATH ?? DEFAULT_SOCKET_PATH,
16024
16182
  seedCredential: process.env.FZ_SEED_CREDENTIAL,
@@ -16033,8 +16191,8 @@ if (import.meta.main) {
16033
16191
  const enrolmentStatePath = process.env.FZ_ENROL_STATE_FILE ?? DEFAULT_ENROLMENT_STATE_PATH;
16034
16192
  let binding = loadGuestBinding(enrolmentStatePath, nodeKey);
16035
16193
  const enrolmentCredential = process.env.FZ_ENROL_TOKEN_CREDENTIAL;
16036
- const enrolmentCredentialAvailable = Boolean(enrolmentCredential && process.env.CREDENTIALS_DIRECTORY && existsSync20(`${process.env.CREDENTIALS_DIRECTORY}/${enrolmentCredential}`));
16037
- const legacyEnrolmentFileAvailable = Boolean(process.env.FZ_ENROL_TOKEN_FILE && existsSync20(process.env.FZ_ENROL_TOKEN_FILE));
16194
+ const enrolmentCredentialAvailable = Boolean(enrolmentCredential && process.env.CREDENTIALS_DIRECTORY && existsSync21(`${process.env.CREDENTIALS_DIRECTORY}/${enrolmentCredential}`));
16195
+ const legacyEnrolmentFileAvailable = Boolean(process.env.FZ_ENROL_TOKEN_FILE && existsSync21(process.env.FZ_ENROL_TOKEN_FILE));
16038
16196
  if (!binding && (enrolmentCredentialAvailable || legacyEnrolmentFileAvailable) && process.env.FZ_API) {
16039
16197
  binding = await enrolGuestIdentity({
16040
16198
  apiUrl: process.env.FZ_API,
@@ -16045,8 +16203,8 @@ if (import.meta.main) {
16045
16203
  keys,
16046
16204
  label: process.env.FZ_NODE_LABEL,
16047
16205
  edgeHostname: process.env.FZ_NODE_HOSTNAME,
16048
- gitDeployPublicKey: process.env.FZ_GIT_PUBLIC_KEY_FILE ? readFileSync19(process.env.FZ_GIT_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
16049
- bootstrapSshPublicKey: process.env.FZ_BOOTSTRAP_SSH_PUBLIC_KEY_FILE ? readFileSync19(process.env.FZ_BOOTSTRAP_SSH_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
16206
+ gitDeployPublicKey: process.env.FZ_GIT_PUBLIC_KEY_FILE ? readFileSync20(process.env.FZ_GIT_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
16207
+ bootstrapSshPublicKey: process.env.FZ_BOOTSTRAP_SSH_PUBLIC_KEY_FILE ? readFileSync20(process.env.FZ_BOOTSTRAP_SSH_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
16050
16208
  privateNetworkAttachment: privateNetworkAttachmentFromEnvironment()
16051
16209
  });
16052
16210
  console.log(`[agent] enrolled ${binding.computeReference} in project ${binding.projectKey}/${binding.environmentKey}`);
@@ -16112,7 +16270,7 @@ if (import.meta.main) {
16112
16270
  const bootstrapEnabled = process.env.FZ_BOOTSTRAP_PULL === "true";
16113
16271
  const bootstrapCredential = process.env.FZ_BOOTSTRAP_SSH_KEY_CREDENTIAL;
16114
16272
  const bootstrapKeyPath = bootstrapCredential && process.env.CREDENTIALS_DIRECTORY ? `${process.env.CREDENTIALS_DIRECTORY}/${bootstrapCredential}` : undefined;
16115
- if (bootstrapEnabled && (!binding || !nodeApiUrl || !process.env.FZ_API || !bootstrapKeyPath || !existsSync20(bootstrapKeyPath) || !process.env.FZ_BOOTSTRAP_TARGET_OTLP_ENDPOINT)) {
16273
+ if (bootstrapEnabled && (!binding || !nodeApiUrl || !process.env.FZ_API || !bootstrapKeyPath || !existsSync21(bootstrapKeyPath) || !process.env.FZ_BOOTSTRAP_TARGET_OTLP_ENDPOINT)) {
16116
16274
  throw new Error("agent: bootstrap runner requires enrolment, API, local SSH credential and target OTLP coordinate");
16117
16275
  }
16118
16276
  const bootstrapPull = bootstrapEnabled ? startSshBootstrapPull({
@@ -16149,12 +16307,21 @@ if (import.meta.main) {
16149
16307
  const repository = process.env.FZ_DEPLOY_REPO;
16150
16308
  const branch = process.env.FZ_DEPLOY_BRANCH;
16151
16309
  const profile = process.env.FZ_DEPLOY_PROFILE;
16310
+ const bootstrapBundlePath = process.env.FZ_BOOTSTRAP_BUNDLE;
16311
+ const bootstrapBundleManifestPath = process.env.FZ_BOOTSTRAP_BUNDLE_MANIFEST;
16312
+ if (Boolean(bootstrapBundlePath) !== Boolean(bootstrapBundleManifestPath)) {
16313
+ throw new Error("agent: bootstrap bundle and manifest coordinates must be supplied together");
16314
+ }
16315
+ const bootstrapBundle = bootstrapBundlePath && bootstrapBundleManifestPath ? {
16316
+ path: bootstrapBundlePath,
16317
+ manifest: parseBootstrapBundleManifest(JSON.parse(readFileSync20(bootstrapBundleManifestPath, "utf8")))
16318
+ } : undefined;
16152
16319
  const root = process.env.FZ_DEPLOY_ROOT;
16153
16320
  const pullEnabled = process.env.FZ_DEPLOY_PULL === "true" && Boolean(process.env.FZ_API);
16154
16321
  if (pullEnabled && !binding) {
16155
16322
  throw new Error("agent: outbound guest deployment requires a persisted tenant enrolment binding");
16156
16323
  }
16157
- if (root && (repository && branch && profile || pullEnabled)) {
16324
+ if (root && (repository && branch && profile || bootstrapBundle && branch && profile || pullEnabled)) {
16158
16325
  const managers = new Map;
16159
16326
  const managerOptions = (source, key) => ({
16160
16327
  key,
@@ -16181,7 +16348,10 @@ if (import.meta.main) {
16181
16348
  activateContainer: (request) => requestContainerActivation(request, process.env.FZ_SOFTWARE_HELPER_SOCKET ?? DEFAULT_SOFTWARE_HELPER_SOCKET),
16182
16349
  projectExec: process.env.FZ_DEPLOY_RUNNER_SOCKET ? (input) => requestDeploymentCommand(input, process.env.FZ_DEPLOY_RUNNER_SOCKET) : undefined
16183
16350
  });
16184
- const staticManager = repository && branch && profile ? createDeploymentManager(managerOptions({ repository, branch, profile }, process.env.FZ_DEPLOY_KEY ?? `${repository}:${branch}:${profile}`)) : undefined;
16351
+ const staticManager = repository && branch && profile ? createDeploymentManager(managerOptions({ repository, branch, profile }, process.env.FZ_DEPLOY_KEY ?? `${repository}:${branch}:${profile}`)) : bootstrapBundle && branch && profile ? createDeploymentManager({
16352
+ ...managerOptions({ repository: bootstrapBundle.path, branch, profile }, process.env.FZ_DEPLOY_KEY ?? `bootstrap:${bootstrapBundle.manifest.sha256}:${profile}`),
16353
+ bootstrapBundle
16354
+ }) : undefined;
16185
16355
  if (staticManager)
16186
16356
  managers.set("__static__", staticManager);
16187
16357
  const control = staticManager ? startControlServer(staticManager, process.env.FZ_CONTROL_SOCKET ?? DEFAULT_CONTROL_SOCKET) : undefined;
@@ -16196,6 +16366,9 @@ if (import.meta.main) {
16196
16366
  canClaim: () => deploymentIntake?.state === "running",
16197
16367
  manager: staticManager,
16198
16368
  managerFor: staticManager ? undefined : (claim) => {
16369
+ if (claim.source.auth?.kind === "ephemeral-token") {
16370
+ return createDeploymentManager(managerOptions(claim.source, claim.pipelineKey));
16371
+ }
16199
16372
  const cacheKey = [
16200
16373
  claim.pipelineKey,
16201
16374
  claim.source.repository,