@forgezero/agent 0.1.73 → 0.1.75

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.
Files changed (44) hide show
  1. package/README.md +565 -61
  2. package/dist/agent-heartbeat.d.ts +25 -1
  3. package/dist/agent-heartbeat.js +45 -5
  4. package/dist/bootstrap.d.ts +2 -0
  5. package/dist/bootstrap.js +22 -4
  6. package/dist/community-rehearsal-host.js +208 -9
  7. package/dist/credential-schema.d.ts +1 -1
  8. package/dist/credential-schema.js +1 -1
  9. package/dist/definition.js +206 -7
  10. package/dist/deploy-actions.d.ts +7 -0
  11. package/dist/deploy-compiler.js +191 -10
  12. package/dist/deploy-file.js +206 -7
  13. package/dist/deploy-plan-runner.d.ts +3 -0
  14. package/dist/deploy-plan-runner.js +190 -9
  15. package/dist/deploy-plan.js +187 -8
  16. package/dist/deploy-providers.d.ts +19 -0
  17. package/dist/deploy.d.ts +89 -3
  18. package/dist/deploy.js +49 -2
  19. package/dist/deployment-connectivity.d.ts +63 -0
  20. package/dist/deployment-connectivity.js +148 -0
  21. package/dist/deployment-pull.d.ts +2 -0
  22. package/dist/deployment-targets.d.ts +9 -0
  23. package/dist/deployment-targets.js +141 -0
  24. package/dist/deployment.d.ts +69 -0
  25. package/dist/fz-agent.js +1112 -302
  26. package/dist/fz.js +246 -18
  27. package/dist/index.d.ts +1 -0
  28. package/dist/metal-bootstrap.js +1 -1
  29. package/dist/metal-helper-socket.js +210 -11
  30. package/dist/metal-provision.js +210 -11
  31. package/dist/operator-bootstrap.js +22 -4
  32. package/dist/platform-bootstrap-runtime.d.ts +1 -0
  33. package/dist/platform-bootstrap-runtime.js +216 -10
  34. package/dist/platform-fleet-verification.js +523 -150
  35. package/dist/platform-genesis.js +206 -7
  36. package/dist/provision.js +465 -99
  37. package/dist/service-supervisor.d.ts +6 -0
  38. package/dist/software-helper.d.ts +3 -0
  39. package/dist/software-helper.js +465 -98
  40. package/dist/software.d.ts +4 -1
  41. package/dist/software.js +210 -8
  42. package/dist/ubuntu.js +206 -7
  43. package/dist/version.d.ts +1 -1
  44. package/package.json +12 -4
@@ -10,6 +10,22 @@ export interface AgentObservation {
10
10
  };
11
11
  architecture: string;
12
12
  mode: 'attested' | 'enrolled';
13
+ runtimeCapabilities?: {
14
+ native: true;
15
+ ociRunc: boolean;
16
+ kataQemuSnp: boolean;
17
+ };
18
+ /** Signed host evidence. Placement still reserves declared capacity; it never chases transient utilization. */
19
+ capacity?: {
20
+ logicalCpu: number;
21
+ memoryMiB: number;
22
+ storageGiB: number;
23
+ };
24
+ utilization?: {
25
+ load1mMilli: number;
26
+ memoryUsedMiB: number;
27
+ storageUsedGiB: number;
28
+ };
13
29
  update?: AgentUpdateReceipt;
14
30
  deploymentIntake?: {
15
31
  generation: number;
@@ -32,6 +48,14 @@ export interface AgentHeartbeatResponse {
32
48
  state: 'running' | 'paused';
33
49
  };
34
50
  }
51
+ export interface AgentHostMetrics {
52
+ logicalCpu: number;
53
+ memoryBytes: number;
54
+ memoryFreeBytes: number;
55
+ storageBytes: number;
56
+ storageFreeBytes: number;
57
+ load1m: number;
58
+ }
35
59
  export interface AgentHeartbeatOptions {
36
60
  apiUrl: string;
37
61
  nodeKey: string;
@@ -58,7 +82,7 @@ export interface AgentHeartbeatOptions {
58
82
  onEvent?: (event: string, detail?: unknown) => void;
59
83
  telemetry?: AgentOperationTelemetry;
60
84
  }
61
- export declare function observeAgentHost(version?: string, mode?: AgentObservation['mode'], osRelease?: string, architecture?: NodeJS.Architecture): AgentObservation;
85
+ export declare function observeAgentHost(version?: string, mode?: AgentObservation['mode'], osRelease?: string, architecture?: NodeJS.Architecture, metrics?: AgentHostMetrics): AgentObservation;
62
86
  /** One PQ-authenticated observation and optional supervised update decision. */
63
87
  export declare function heartbeatAgentOnce(options: AgentHeartbeatOptions): Promise<AgentHeartbeatResponse>;
64
88
  /** Server-paced heartbeat; stopping waits for the active PQ request/update handoff. */
@@ -677,7 +677,8 @@ function requestAgentUpdate(request, socketPath = DEFAULT_AGENT_UPDATE_SOCKET, t
677
677
  }
678
678
 
679
679
  // src/agent-heartbeat.ts
680
- import { readFileSync as readFileSync3 } from "node:fs";
680
+ import { existsSync as existsSync3, readFileSync as readFileSync3, statfsSync } from "node:fs";
681
+ import { cpus, freemem, loadavg, totalmem } from "node:os";
681
682
 
682
683
  // src/signed-node-http.ts
683
684
  import {
@@ -749,25 +750,64 @@ async function postSignedNode(options, path, body) {
749
750
  }
750
751
 
751
752
  // src/version.ts
752
- var VERSION3 = "0.1.73";
753
+ var VERSION3 = "0.1.75";
753
754
 
754
755
  // src/agent-heartbeat.ts
756
+ function readAgentHostMetrics() {
757
+ const filesystem = statfsSync("/");
758
+ return {
759
+ logicalCpu: cpus().length,
760
+ memoryBytes: totalmem(),
761
+ memoryFreeBytes: freemem(),
762
+ storageBytes: Number(filesystem.blocks) * Number(filesystem.bsize),
763
+ storageFreeBytes: Number(filesystem.bavail) * Number(filesystem.bsize),
764
+ load1m: loadavg()[0]
765
+ };
766
+ }
755
767
  var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
756
768
  var remoteOutcome = (cause) => cause instanceof SignedNodeHttpError && cause.status < 500 ? "refused" : cause instanceof SignedNodeHttpError ? "retryable" : "failed";
757
769
 
758
770
  class AgentUpdateRefusedError extends Error {
759
771
  }
760
- function observeAgentHost(version = VERSION3, mode = "enrolled", osRelease = readFileSync3("/etc/os-release", "utf8"), architecture = process.arch) {
772
+ function observeAgentHost(version = VERSION3, mode = "enrolled", osRelease = readFileSync3("/etc/os-release", "utf8"), architecture = process.arch, metrics = readAgentHostMetrics()) {
761
773
  const values = Object.fromEntries(osRelease.split(`
762
774
  `).flatMap((line) => {
763
775
  const separator = line.indexOf("=");
764
776
  return separator > 0 ? [[line.slice(0, separator), unquote(line.slice(separator + 1))]] : [];
765
777
  }));
778
+ const osId = (values.ID ?? "unknown").toLowerCase();
779
+ const osVersion = values.VERSION_ID ?? "unknown";
780
+ const ubuntuX64 = osId === "ubuntu" && ["24.04", "26.04"].includes(osVersion) && architecture === "x64";
781
+ const enabled = (path) => {
782
+ try {
783
+ return /^(?:1|y)$/i.test(readFileSync3(path, "utf8").trim());
784
+ } catch {
785
+ return false;
786
+ }
787
+ };
788
+ const gib = 1024 ** 3;
789
+ const mib = 1024 ** 2;
790
+ const storageGiB = Math.max(1, Math.floor(metrics.storageBytes / gib));
766
791
  return {
767
792
  version,
768
- os: { id: (values.ID ?? "unknown").toLowerCase(), versionId: values.VERSION_ID ?? "unknown" },
793
+ os: { id: osId, versionId: osVersion },
769
794
  architecture,
770
- mode
795
+ mode,
796
+ runtimeCapabilities: {
797
+ native: true,
798
+ ociRunc: ubuntuX64,
799
+ kataQemuSnp: ubuntuX64 && osVersion === "26.04" && existsSync3("/dev/kvm") && existsSync3("/dev/sev") && enabled("/sys/module/kvm_amd/parameters/sev") && enabled("/sys/module/kvm_amd/parameters/sev_snp")
800
+ },
801
+ capacity: {
802
+ logicalCpu: Math.max(1, metrics.logicalCpu),
803
+ memoryMiB: Math.max(1, Math.floor(metrics.memoryBytes / mib)),
804
+ storageGiB
805
+ },
806
+ utilization: {
807
+ load1mMilli: Math.max(0, Math.round(metrics.load1m * 1000)),
808
+ memoryUsedMiB: Math.max(0, Math.floor((metrics.memoryBytes - metrics.memoryFreeBytes) / mib)),
809
+ storageUsedGiB: Math.max(0, Math.min(storageGiB, Math.ceil((metrics.storageBytes - metrics.storageFreeBytes) / gib)))
810
+ }
771
811
  };
772
812
  }
773
813
  async function heartbeatAgentOnce(options) {
@@ -35,6 +35,8 @@ export interface PlatformBootstrapConfig {
35
35
  role: DatabaseBootstrapRole;
36
36
  /** Genesis master/joiners are Agency members; elastic joiners normally use `none`. */
37
37
  agency: DatabaseAgencyParticipation;
38
+ /** Prefer this normal writable Coordinator only for explicitly read-safe operations. */
39
+ readPreferred?: boolean;
38
40
  /** Cluster mode must remain default. Bootstrap verifies it and never mutates it. */
39
41
  serverMode: 'default';
40
42
  address?: string;
package/dist/bootstrap.js CHANGED
@@ -1416,7 +1416,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1416
1416
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1417
1417
 
1418
1418
  // src/version.ts
1419
- var VERSION = "0.1.73";
1419
+ var VERSION = "0.1.75";
1420
1420
 
1421
1421
  // src/software.ts
1422
1422
  var PINNED_BUN_VERSION = "1.3.14";
@@ -3060,6 +3060,10 @@ function validatePlatformSharedEnvironment(input) {
3060
3060
  const coordinators = input.databaseCoordinators.map(privateCoordinator);
3061
3061
  if (new Set(coordinators).size !== coordinators.length)
3062
3062
  throw new Error("databaseCoordinators must be unique.");
3063
+ const readPreferred = (input.databaseReadPreferredCoordinators ?? []).map(privateCoordinator);
3064
+ if (new Set(readPreferred).size !== readPreferred.length || readPreferred.some((url) => !coordinators.includes(url))) {
3065
+ throw new Error("databaseReadPreferredCoordinators must be a unique subset of databaseCoordinators.");
3066
+ }
3063
3067
  if (input.databaseNetworkMode !== "private-lan") {
3064
3068
  throw new Error("Attended platform bootstrap supports only private-lan database networking.");
3065
3069
  }
@@ -3133,6 +3137,7 @@ function validatePlatformSharedEnvironment(input) {
3133
3137
  return {
3134
3138
  ...input,
3135
3139
  databaseCoordinators: coordinators,
3140
+ databaseReadPreferredCoordinators: readPreferred,
3136
3141
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
3137
3142
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
3138
3143
  agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
@@ -3146,6 +3151,8 @@ function renderPlatformSharedEnvironment(input) {
3146
3151
  const entries = {
3147
3152
  ARANGO_URL: value.databaseCoordinators[0],
3148
3153
  ARANGO_URLS: value.databaseCoordinators.join(","),
3154
+ ARANGO_READ_PREFERRED_URLS: (value.databaseReadPreferredCoordinators ?? []).join(","),
3155
+ ARANGO_READ_PREFERRED_FALLBACK: "balanced",
3149
3156
  ARANGO_DB: "fz",
3150
3157
  FZ_DATABASE_MODE: "platform",
3151
3158
  ARANGO_USER: value.databaseUser,
@@ -3769,6 +3776,10 @@ function validateBootstrapConfig(value) {
3769
3776
  if (!["member", "none"].includes(value.database.agency) || value.database.role === "master" && value.database.agency !== "member" || value.database.role === "none" && value.database.agency !== "none") {
3770
3777
  throw new Error("database role and Agency participation disagree");
3771
3778
  }
3779
+ value.database.readPreferred ??= false;
3780
+ if (value.database.readPreferred && (value.database.role !== "joiner" || value.database.agency !== "none")) {
3781
+ throw new Error("read-preferred database routing requires an extra non-Agency joiner");
3782
+ }
3772
3783
  if (value.database.role !== "none" && !value.database.address)
3773
3784
  throw new Error("database nodes require a private address");
3774
3785
  if (value.database.role === "joiner" && !value.database.master)
@@ -3788,6 +3799,10 @@ function validateBootstrapConfig(value) {
3788
3799
  if (runtime.softwareProfile !== value.profile || runtime.databaseRole !== value.database.role || runtime.nodeHostname !== value.nodeHostname || runtime.apiOrigin !== api.origin || runtime.databaseCoordinators.join(",") !== write.join(",")) {
3789
3800
  throw new Error("platform runtime coordinates disagree with immutable bootstrap coordinates");
3790
3801
  }
3802
+ const ownCoordinator = value.database.address ? `http://${value.database.address}:8529` : undefined;
3803
+ if (Boolean(ownCoordinator && (runtime.databaseReadPreferredCoordinators ?? []).includes(ownCoordinator)) !== value.database.readPreferred) {
3804
+ throw new Error("runtime read-preferred coordinator set disagrees with this node database routing intent");
3805
+ }
3791
3806
  if (runtime.deployProfile !== value.environment)
3792
3807
  throw new Error("runtime deployment profile disagrees with bootstrap environment");
3793
3808
  if (value.database.address !== runtime.databaseAddress || value.database.master !== runtime.databaseMaster) {
@@ -4099,6 +4114,7 @@ function bootstrapIdentity(config) {
4099
4114
  role: config.database.role,
4100
4115
  serverMode: config.database.serverMode,
4101
4116
  agency: config.database.agency,
4117
+ readPreferred: config.database.readPreferred,
4102
4118
  address: config.database.address ?? null,
4103
4119
  master: config.database.master ?? null,
4104
4120
  coordinators: config.database.coordinators
@@ -4134,7 +4150,7 @@ function parseStoredState(raw) {
4134
4150
  if (!value || typeof value !== "object" || Array.isArray(value))
4135
4151
  throw new Error("bootstrap state is malformed");
4136
4152
  const state = value;
4137
- if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && !["member", "none"].includes(state.databaseAgency ?? "")) {
4153
+ if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && (!["member", "none"].includes(state.databaseAgency ?? "") || typeof state.databaseReadPreferred !== "boolean")) {
4138
4154
  throw new Error("bootstrap state is legacy or incomplete; refusing an unbound repair");
4139
4155
  }
4140
4156
  return state;
@@ -4184,6 +4200,7 @@ function stateFor(config, cloudflare, previousCloudflareTunnelId) {
4184
4200
  environment: config.environment,
4185
4201
  databaseRole: config.database.role,
4186
4202
  databaseAgency: config.database.agency,
4203
+ databaseReadPreferred: config.database.readPreferred,
4187
4204
  databaseServerMode: config.database.serverMode,
4188
4205
  databaseAddress: config.database.address,
4189
4206
  databaseCoordinators: config.database.coordinators,
@@ -4688,7 +4705,7 @@ function strictBootstrapDocument(value) {
4688
4705
  exactKeys(root.firewall, ["enabled", "sshPort", "privateCidrs"], "firewall config");
4689
4706
  if (root.cloudflareHandoff !== undefined)
4690
4707
  exactKeys(root.cloudflareHandoff, ["handoffFile", "nodeName"], "Cloudflare handoff");
4691
- exactKeys(root.database, ["role", "agency", "serverMode", "address", "master", "coordinators"], "database config");
4708
+ exactKeys(root.database, ["role", "agency", "readPreferred", "serverMode", "address", "master", "coordinators"], "database config");
4692
4709
  exactKeys(root.enrolment, ["source"], "platform enrolment config");
4693
4710
  const runtime = exactKeys(root.runtime, [
4694
4711
  "environment",
@@ -4732,7 +4749,8 @@ function strictBootstrapDocument(value) {
4732
4749
  "backup",
4733
4750
  "cloudflare",
4734
4751
  "realtime",
4735
- "initialInventory"
4752
+ "initialInventory",
4753
+ "databaseReadPreferredCoordinators"
4736
4754
  ], "runtime environment");
4737
4755
  const environment = runtime.environment;
4738
4756
  if (environment.initialInventory !== undefined) {
@@ -4,22 +4,30 @@ import {
4
4
  accessSync,
5
5
  chmodSync,
6
6
  copyFileSync,
7
+ createReadStream,
7
8
  existsSync,
8
9
  mkdtempSync,
9
10
  mkdirSync,
10
11
  readFileSync,
11
12
  renameSync,
12
13
  rmSync,
14
+ statSync,
13
15
  symlinkSync,
14
16
  unlinkSync,
15
17
  writeFileSync
16
18
  } from "node:fs";
17
19
  import { tmpdir } from "node:os";
18
- import { join } from "node:path";
20
+ import { dirname, join } from "node:path";
19
21
  var PINNED_BUN_VERSION = "1.3.14";
20
22
  var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f";
21
23
  var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
22
24
  var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
25
+ var NERDCTL_VERSION = "2.3.5";
26
+ var BUILDKIT_VERSION = "0.32.2";
27
+ var KATA_CONTAINERS_VERSION = "4.0.0";
28
+ var NERDCTL_SHA256 = "de3206aeb7cbd5f20f5fb1f55c1e3bf2db1be567812a8a3f5e65eba2488347ee";
29
+ var BUILDKIT_SHA256 = "2975d0f651ad96ba8b80b9992ae1f9a964f4408569af5b6dc36544165c3926af";
30
+ var KATA_SHA256 = "2c3b9dfeba355582b40aee462b12916c9740654d0230f696adf719d67b063a8c";
23
31
  var OS_CATALOG = [
24
32
  {
25
33
  id: "ubuntu",
@@ -49,6 +57,8 @@ var OS_CATALOG = [
49
57
  var SOFTWARE_CATALOG = [
50
58
  { id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
51
59
  { id: "docker", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
60
+ { id: "containerd", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
61
+ { id: "kata-containers", version: "4.0.0", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
52
62
  { id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
53
63
  { id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
54
64
  { id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
@@ -57,6 +67,7 @@ var SOFTWARE_CATALOG = [
57
67
  { id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
58
68
  { id: "git", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
59
69
  { id: "docker", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
70
+ { id: "containerd", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
60
71
  { id: "nginx", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
61
72
  { id: "ufw", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
62
73
  { id: "openssh-client", version: "ubuntu-24.04", status: "active", os: "ubuntu", osVersion: "24.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
@@ -65,6 +76,8 @@ var SOFTWARE_CATALOG = [
65
76
  var UBUNTU_2604_X64 = [
66
77
  { requirement: { id: "bun", version: "1.3.14" } },
67
78
  { requirement: { id: "docker", version: "ubuntu-26.04" } },
79
+ { requirement: { id: "containerd", version: "ubuntu-26.04" } },
80
+ { requirement: { id: "kata-containers", version: "4.0.0" } },
68
81
  { requirement: { id: "nginx", version: "ubuntu-26.04" } },
69
82
  { requirement: { id: "arangodb", version: "3.11.14" } },
70
83
  { requirement: { id: "cloudflared", version: "2026.7.3" } },
@@ -76,6 +89,7 @@ var UBUNTU_2604_X64 = [
76
89
  var UBUNTU_2404_X64 = [
77
90
  { requirement: { id: "bun", version: "1.3.14" } },
78
91
  { requirement: { id: "docker", version: "ubuntu-24.04" } },
92
+ { requirement: { id: "containerd", version: "ubuntu-24.04" } },
79
93
  { requirement: { id: "nginx", version: "ubuntu-24.04" } },
80
94
  { requirement: { id: "ufw", version: "ubuntu-24.04" } },
81
95
  { requirement: { id: "openssh-client", version: "ubuntu-24.04" } },
@@ -92,6 +106,46 @@ var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
92
106
  "log-opts": { "max-size": "10m", "max-file": "3" }
93
107
  }, null, 2)}
94
108
  `;
109
+ var CONTAINERD_CONFIG_PATH = "/etc/containerd/config.toml";
110
+ var KATA_SNP_CONFIG_PATH = "/opt/kata/share/defaults/kata-containers/configuration-qemu-snp.toml";
111
+ var KATA_ACTIVE_CONFIG_PATH = "/etc/kata-containers/configuration.toml";
112
+ var CONTAINERD_CONFIG = (kata) => `version = 2
113
+ root = "/var/lib/forgezero/containerd"
114
+ state = "/run/forgezero/containerd"
115
+
116
+ [grpc]
117
+ address = "/run/containerd/containerd.sock"
118
+
119
+ [plugins."io.containerd.grpc.v1.cri".containerd]
120
+ snapshotter = "overlayfs"
121
+ default_runtime_name = "runc"
122
+
123
+ [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
124
+ runtime_type = "io.containerd.runc.v2"
125
+ ${kata ? `
126
+ [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata-qemu-snp]
127
+ runtime_type = "io.containerd.kata.v2"
128
+ [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata-qemu-snp.options]
129
+ ConfigPath = "${KATA_ACTIVE_CONFIG_PATH}"
130
+ ` : ""}`;
131
+ var BUILDKIT_UNIT = `[Unit]
132
+ Description=ForgeZero BuildKit daemon
133
+ After=network-online.target containerd.service
134
+ Wants=network-online.target
135
+ Requires=containerd.service
136
+
137
+ [Service]
138
+ Type=notify
139
+ ExecStart=/usr/local/bin/buildkitd --addr unix:///run/buildkit/buildkitd.sock --root /var/lib/forgezero/buildkit
140
+ Restart=on-failure
141
+ RestartSec=2
142
+ NoNewPrivileges=yes
143
+ ProtectSystem=strict
144
+ ReadWritePaths=/var/lib/forgezero/buildkit /run/buildkit
145
+
146
+ [Install]
147
+ WantedBy=multi-user.target
148
+ `;
95
149
  var softwareSpawnFailure = (cause, argv) => cause.code === "ENOENT" ? { exitCode: 127, output: `executable is absent: ${argv[0]}` } : undefined;
96
150
  var runSoftwareCommand = async (argv, env = {}) => {
97
151
  let child;
@@ -111,14 +165,28 @@ var runSoftwareCommand = async (argv, env = {}) => {
111
165
  return { exitCode, output: `${stdout}${stderr}` };
112
166
  };
113
167
  var run = runSoftwareCommand;
114
- var download = async (url, destination, sha256) => {
115
- const response = await fetch(url, { redirect: "follow", signal: AbortSignal.timeout(120000) });
168
+ var download = async (url, destination, sha256, maximumBytes = 512 * 1024 * 1024) => {
169
+ const response = await fetch(url, { redirect: "follow", signal: AbortSignal.timeout(30 * 60000) });
116
170
  if (!response.ok)
117
171
  throw new Error(`download failed with HTTP ${response.status}`);
118
- const bytes = new Uint8Array(await response.arrayBuffer());
119
- if (createHash("sha256").update(bytes).digest("hex") !== sha256)
172
+ const declared = Number(response.headers.get("content-length") ?? 0);
173
+ if (declared > maximumBytes)
174
+ throw new Error("download exceeds reviewed size bound");
175
+ if (existsSync(destination))
176
+ throw new Error("download destination already exists");
177
+ await Bun.write(destination, response);
178
+ chmodSync(destination, 384);
179
+ if (statSync(destination).size > maximumBytes) {
180
+ rmSync(destination, { force: true });
181
+ throw new Error("download exceeds reviewed size bound");
182
+ }
183
+ const digest = createHash("sha256");
184
+ for await (const chunk of createReadStream(destination))
185
+ digest.update(chunk);
186
+ if (digest.digest("hex") !== sha256) {
187
+ rmSync(destination, { force: true });
120
188
  throw new Error("download checksum mismatch");
121
- writeFileSync(destination, bytes, { mode: 384, flag: "wx" });
189
+ }
122
190
  };
123
191
  var successful = (result, pattern) => result.exitCode === 0 && (!pattern || pattern.test(result.output));
124
192
  var aptInstall = async (name) => {
@@ -126,6 +194,106 @@ var aptInstall = async (name) => {
126
194
  const update = await run(["/usr/bin/apt-get", "update", "-qq"], environment);
127
195
  return update.exitCode === 0 ? run(["/usr/bin/apt-get", "install", "-y", name], environment) : update;
128
196
  };
197
+ var aptInstallMany = async (names) => {
198
+ const environment = { DEBIAN_FRONTEND: "noninteractive" };
199
+ const update = await run(["/usr/bin/apt-get", "update", "-qq"], environment);
200
+ return update.exitCode === 0 ? run(["/usr/bin/apt-get", "install", "-y", ...names], environment) : update;
201
+ };
202
+ var writeOwnedPolicy = (pathValue, content, mode = 420) => {
203
+ if (existsSync(pathValue)) {
204
+ if (readFileSync(pathValue, "utf8") !== content)
205
+ throw new Error(`refusing to overwrite non-ForgeZero policy at ${pathValue}`);
206
+ return;
207
+ }
208
+ mkdirSync(dirname(pathValue), { recursive: true, mode: 493 });
209
+ writeFileSync(pathValue, content, { mode, flag: "wx" });
210
+ };
211
+ var installContainerdRuntime = async (directory, osVersion) => {
212
+ const installed = await aptInstallMany(["containerd", "runc", "containernetworking-plugins"]);
213
+ if (installed.exitCode !== 0)
214
+ return installed;
215
+ const nerdctl = join(directory, "nerdctl.tar.gz");
216
+ await download(`https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz`, nerdctl, NERDCTL_SHA256);
217
+ const nerdctlExtract = await run(["/usr/bin/tar", "-xzf", nerdctl, "-C", "/usr/local/bin", "nerdctl"]);
218
+ if (nerdctlExtract.exitCode !== 0)
219
+ return nerdctlExtract;
220
+ chmodSync("/usr/local/bin/nerdctl", 493);
221
+ const buildkit = join(directory, "buildkit.tar.gz");
222
+ await download(`https://github.com/moby/buildkit/releases/download/v${BUILDKIT_VERSION}/buildkit-v${BUILDKIT_VERSION}.linux-amd64.tar.gz`, buildkit, BUILDKIT_SHA256);
223
+ const unpacked = join(directory, "buildkit");
224
+ mkdirSync(unpacked, { mode: 448 });
225
+ const buildkitExtract = await run(["/usr/bin/tar", "-xzf", buildkit, "-C", unpacked]);
226
+ if (buildkitExtract.exitCode !== 0)
227
+ return buildkitExtract;
228
+ for (const binary of ["buildctl", "buildkitd"]) {
229
+ copyFileSync(join(unpacked, "bin", binary), `/usr/local/bin/${binary}`);
230
+ chmodSync(`/usr/local/bin/${binary}`, 493);
231
+ }
232
+ mkdirSync("/etc/containerd", { recursive: true, mode: 493 });
233
+ const expected = CONTAINERD_CONFIG(false);
234
+ const kataExpected = CONTAINERD_CONFIG(true);
235
+ if (existsSync(CONTAINERD_CONFIG_PATH) && ![expected, kataExpected].includes(readFileSync(CONTAINERD_CONFIG_PATH, "utf8"))) {
236
+ return { exitCode: 2, output: "refusing to overwrite an existing containerd configuration that differs from the reviewed ForgeZero policy" };
237
+ }
238
+ if (!existsSync(CONTAINERD_CONFIG_PATH))
239
+ writeFileSync(CONTAINERD_CONFIG_PATH, expected, { mode: 420, flag: "wx" });
240
+ writeOwnedPolicy("/etc/systemd/system/forgezero-buildkit.service", BUILDKIT_UNIT);
241
+ mkdirSync("/var/lib/forgezero/containerd", { recursive: true, mode: 448 });
242
+ mkdirSync("/var/lib/forgezero/buildkit", { recursive: true, mode: 448 });
243
+ const reload = await run(["/usr/bin/systemctl", "daemon-reload"]);
244
+ if (reload.exitCode !== 0)
245
+ return reload;
246
+ const enabled = await run(["/usr/bin/systemctl", "enable", "--now", "containerd.service", "forgezero-buildkit.service"]);
247
+ return enabled.exitCode === 0 ? { exitCode: 0, output: `containerd ${osVersion} with nerdctl ${NERDCTL_VERSION} and BuildKit ${BUILDKIT_VERSION}` } : enabled;
248
+ };
249
+ var installKataRuntime = async (directory) => {
250
+ for (const pathValue of ["/dev/kvm", "/dev/sev"])
251
+ if (!existsSync(pathValue))
252
+ return { exitCode: 2, output: `${pathValue} is required for Kata SEV-SNP` };
253
+ for (const parameter of ["/sys/module/kvm_amd/parameters/sev", "/sys/module/kvm_amd/parameters/sev_snp"]) {
254
+ if (!existsSync(parameter) || !/^(1|Y)$/i.test(readFileSync(parameter, "utf8").trim()))
255
+ return { exitCode: 2, output: `${parameter} does not enable SEV-SNP` };
256
+ }
257
+ const dependencies = await aptInstallMany(["zstd"]);
258
+ if (dependencies.exitCode !== 0)
259
+ return dependencies;
260
+ const archive = join(directory, "kata.tar.zst");
261
+ await download(`https://github.com/kata-containers/kata-containers/releases/download/${KATA_CONTAINERS_VERSION}/kata-static-${KATA_CONTAINERS_VERSION}-amd64.tar.zst`, archive, KATA_SHA256, 2200000000);
262
+ const tar = join(directory, "kata.tar");
263
+ const expanded = await run(["/usr/bin/unzstd", "--quiet", archive, "--output", tar]);
264
+ if (expanded.exitCode !== 0)
265
+ return expanded;
266
+ const extracted = await run(["/usr/bin/tar", "-xf", tar, "-C", "/"]);
267
+ if (extracted.exitCode !== 0)
268
+ return extracted;
269
+ if (!existsSync("/opt/kata/bin/containerd-shim-kata-v2") || !existsSync(KATA_SNP_CONFIG_PATH)) {
270
+ return { exitCode: 2, output: "Kata archive does not contain the reviewed QEMU SNP runtime and configuration" };
271
+ }
272
+ const snpConfig = readFileSync(KATA_SNP_CONFIG_PATH, "utf8");
273
+ if (!/^\s*confidential_guest\s*=\s*true\s*$/m.test(snpConfig) || !/^\s*sev_snp_guest\s*=\s*true\s*$/m.test(snpConfig)) {
274
+ return { exitCode: 2, output: "Kata QEMU configuration does not enable confidential SEV-SNP guests" };
275
+ }
276
+ mkdirSync("/etc/kata-containers", { recursive: true, mode: 493 });
277
+ if (existsSync(KATA_ACTIVE_CONFIG_PATH) && readFileSync(KATA_ACTIVE_CONFIG_PATH, "utf8") !== snpConfig) {
278
+ return { exitCode: 2, output: "refusing to overwrite a non-ForgeZero Kata runtime configuration" };
279
+ }
280
+ if (!existsSync(KATA_ACTIVE_CONFIG_PATH))
281
+ copyFileSync(KATA_SNP_CONFIG_PATH, KATA_ACTIVE_CONFIG_PATH);
282
+ chmodSync(KATA_ACTIVE_CONFIG_PATH, 420);
283
+ try {
284
+ unlinkSync("/usr/local/bin/containerd-shim-kata-v2");
285
+ } catch {}
286
+ symlinkSync("/opt/kata/bin/containerd-shim-kata-v2", "/usr/local/bin/containerd-shim-kata-v2");
287
+ const base = CONTAINERD_CONFIG(false);
288
+ const kata = CONTAINERD_CONFIG(true);
289
+ if (!existsSync(CONTAINERD_CONFIG_PATH) || ![base, kata].includes(readFileSync(CONTAINERD_CONFIG_PATH, "utf8"))) {
290
+ return { exitCode: 2, output: "containerd must use the reviewed ForgeZero policy before Kata is installed" };
291
+ }
292
+ if (readFileSync(CONTAINERD_CONFIG_PATH, "utf8") !== kata)
293
+ writeFileSync(CONTAINERD_CONFIG_PATH, kata, { mode: 420 });
294
+ const restarted = await run(["/usr/bin/systemctl", "restart", "containerd.service"]);
295
+ return restarted.exitCode === 0 ? { exitCode: 0, output: `Kata Containers ${KATA_CONTAINERS_VERSION} QEMU SNP` } : restarted;
296
+ };
129
297
  async function executeSoftwareOperation(operation) {
130
298
  const { software, version } = operation;
131
299
  if (![...UBUNTU_2604_X64, ...UBUNTU_2404_X64].some(({ requirement }) => requirement.id === software && requirement.version === version)) {
@@ -143,6 +311,26 @@ async function executeSoftwareOperation(operation) {
143
311
  const active = await run(["/usr/bin/systemctl", "is-active", "--quiet", "docker.service"]);
144
312
  return active.exitCode === 0 ? { exitCode: 0, output: binary.output } : active;
145
313
  }
314
+ if (software === "containerd") {
315
+ const [containerd, nerdctl, buildkit, active, builder] = await Promise.all([
316
+ run(["/usr/bin/containerd", "--version"]),
317
+ run(["/usr/local/bin/nerdctl", "--version"]),
318
+ run(["/usr/local/bin/buildkitd", "--version"]),
319
+ run(["/usr/bin/systemctl", "is-active", "--quiet", "containerd.service"]),
320
+ run(["/usr/bin/systemctl", "is-active", "--quiet", "forgezero-buildkit.service"])
321
+ ]);
322
+ const policy = existsSync(CONTAINERD_CONFIG_PATH) ? readFileSync(CONTAINERD_CONFIG_PATH, "utf8") : "";
323
+ const validPolicy = policy === CONTAINERD_CONFIG(false) || policy === CONTAINERD_CONFIG(true);
324
+ return successful(containerd, /containerd/) && successful(nerdctl, new RegExp(NERDCTL_VERSION.replaceAll(".", "\\."))) && successful(buildkit, new RegExp(BUILDKIT_VERSION.replaceAll(".", "\\."))) && active.exitCode === 0 && builder.exitCode === 0 && validPolicy ? { exitCode: 0, output: `${containerd.output}${nerdctl.output}${buildkit.output}` } : { exitCode: 1, output: "containerd, nerdctl, BuildKit, or ForgeZero storage/runtime policy is unavailable" };
325
+ }
326
+ if (software === "kata-containers") {
327
+ const shim = await run(["/opt/kata/bin/containerd-shim-kata-v2", "--version"]);
328
+ const policy = existsSync(CONTAINERD_CONFIG_PATH) ? readFileSync(CONTAINERD_CONFIG_PATH, "utf8") : "";
329
+ const activeConfig = existsSync(KATA_ACTIVE_CONFIG_PATH) ? readFileSync(KATA_ACTIVE_CONFIG_PATH, "utf8") : "";
330
+ const host = ["/dev/kvm", "/dev/sev", KATA_SNP_CONFIG_PATH, KATA_ACTIVE_CONFIG_PATH].every(existsSync) && ["/sys/module/kvm_amd/parameters/sev", "/sys/module/kvm_amd/parameters/sev_snp"].every((parameter) => existsSync(parameter) && /^(1|Y)$/i.test(readFileSync(parameter, "utf8").trim()));
331
+ const active = await run(["/usr/bin/systemctl", "is-active", "--quiet", "containerd.service"]);
332
+ return successful(shim, /kata.*4\.0\.0/i) && policy === CONTAINERD_CONFIG(true) && activeConfig === (existsSync(KATA_SNP_CONFIG_PATH) ? readFileSync(KATA_SNP_CONFIG_PATH, "utf8") : "") && host && active.exitCode === 0 ? { exitCode: 0, output: shim.output } : { exitCode: 1, output: "Kata QEMU SNP runtime, host capability, or containerd policy is unavailable" };
333
+ }
146
334
  if (software === "nginx") {
147
335
  const binary = await run(["/usr/sbin/nginx", "-v"]);
148
336
  return binary.exitCode === 0 ? run(["/usr/bin/systemctl", "is-active", "--quiet", "nginx.service"]) : binary;
@@ -194,6 +382,17 @@ async function executeSoftwareOperation(operation) {
194
382
  }
195
383
  const directory = mkdtempSync(join(tmpdir(), "forgezero-software-"));
196
384
  try {
385
+ if (software === "containerd")
386
+ return installContainerdRuntime(directory, version === "ubuntu-24.04" ? "24.04" : "26.04");
387
+ if (software === "kata-containers") {
388
+ const containerd = await executeSoftwareOperation({ kind: "check", software: "containerd", version: "ubuntu-26.04" });
389
+ if (containerd.exitCode !== 0) {
390
+ const installed2 = await installContainerdRuntime(directory, "26.04");
391
+ if (installed2.exitCode !== 0)
392
+ return installed2;
393
+ }
394
+ return installKataRuntime(directory);
395
+ }
197
396
  if (software === "cloudflare-warp") {
198
397
  const key = join(directory, "cloudflare-warp-key.gpg");
199
398
  await download("https://pkg.cloudflareclient.com/pubkey.gpg", key, "0f37fc298c98e88ee3c0ee68c95b69f1dba9eb477abe3167e13982105911264d");
@@ -275,7 +474,7 @@ function validateSoftwareRequirements(value, _options = {}) {
275
474
  if (Object.keys(row).some((key) => key !== "id" && key !== "version")) {
276
475
  throw new Error("software requirement contains an unknown field");
277
476
  }
278
- 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)) {
477
+ if (!["bun", "docker", "containerd", "kata-containers", "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)) {
279
478
  throw new Error("software requirement coordinate is invalid");
280
479
  }
281
480
  const requirement = { id: row.id, version: row.version };
@@ -321,7 +520,7 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
321
520
  import { createHash as createHash2 } from "node:crypto";
322
521
  import { lstatSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, symlinkSync as symlinkSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
323
522
  import { isIP } from "node:net";
324
- import { dirname } from "node:path";
523
+ import { dirname as dirname2 } from "node:path";
325
524
  var COMMUNITY_REHEARSAL_VERSION = "3.11.14";
326
525
  var COMMUNITY_REHEARSAL_ROOT = "/opt/forgezero-rehearsals/community-cluster-api";
327
526
  var COMMUNITY_DATABASE_ROOT = "/var/lib/forgezero-rehearsal-cluster";
@@ -539,7 +738,7 @@ async function applyOperations(operations) {
539
738
  if (operation.kind === "remove-tree")
540
739
  rmSync2(operation.path, { recursive: true, force: true });
541
740
  else if (operation.kind === "write") {
542
- mkdirSync2(dirname(operation.path), { recursive: true });
741
+ mkdirSync2(dirname2(operation.path), { recursive: true });
543
742
  writeFileSync2(operation.path, operation.content, { mode: operation.mode });
544
743
  } else if (operation.kind === "unlink") {
545
744
  try {
@@ -86,7 +86,7 @@ export declare const AGENT_CREDENTIAL_POLICY: {
86
86
  };
87
87
  };
88
88
  export interface DeploymentCredentialBinding {
89
- /** Exact environment variable requested by a checked-in pipeline step. */
89
+ /** Exact Vault/systemd logical name requested by a checked-in pipeline step. */
90
90
  name: string;
91
91
  /** Exact RAM-replica cache key; never a secret value. */
92
92
  vaultCacheKey: string;
@@ -335,7 +335,7 @@ var AGENT_CREDENTIAL_POLICY = {
335
335
  "platform-compute": { vault: true, systemdFallback: true, hiddenInput: false },
336
336
  "tenant-compute": { vault: true, systemdFallback: true, hiddenInput: false }
337
337
  };
338
- var CREDENTIAL_NAME = /^[A-Z_][A-Z0-9_]*$/;
338
+ var CREDENTIAL_NAME = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
339
339
  var SCOPE_PART2 = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
340
340
  function deploymentCredentialSchema(input) {
341
341
  if (!SCOPE_PART2.test(input.projectKey) || !SCOPE_PART2.test(input.environmentKey)) {