@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/README.md CHANGED
@@ -51,6 +51,7 @@ Every row links to the detailed explanation and named-import/example area below.
51
51
  | @forgezero/agent/agent-update | Verified Agent update planning against the platform release coordinate. | Bun/Node host | [Details + example](#forgezero-agent-agent-update) |
52
52
  | @forgezero/agent/agent-update-helper | Root-owned fixed update operations with registry origin and integrity validation. | Bun/Node host | [Details + example](#forgezero-agent-agent-update-helper) |
53
53
  | @forgezero/agent/bootstrap | Typed one-time platform bootstrap and internal API-driven enrolled-compute activation contracts. | Bun/Node host | [Details + example](#forgezero-agent-bootstrap) |
54
+ | @forgezero/agent/bootstrap-bundle | Owner-only exact-revision Git bundle and SHA-256 manifest for credential-free release generation one. | Bun/Node host | [Details + example](#forgezero-agent-bootstrap-bundle) |
54
55
  | @forgezero/agent/operator-bootstrap | Pinned-host operator transport that stages owner-only handoffs and invokes the same typed platform bootstrap through the caller-approved SSH agent. | Bun/Node host | [Details + example](#forgezero-agent-operator-bootstrap) |
55
56
  | @forgezero/agent/host-maintenance | Closed host maintenance plans for staging transaction rehearsal and fleet-fenced schema consolidation through fixed transient systemd units. | Bun/Node host | [Details + example](#forgezero-agent-host-maintenance) |
56
57
  | @forgezero/agent/capacity-calibration | One-time target-local safe-concurrency calibration with private evidence. | Bun/Node host | [Details + example](#forgezero-agent-capacity-calibration) |
@@ -82,7 +83,8 @@ bun add -g @forgezero/agent — Install the version-matched fz operator CLI and
82
83
  fz deploy init — Create a typed forgezero.deploy.ts and its canonical inert execution plan.
83
84
  fz deploy compile — Compile and validate TypeScript deployment intent into .fz/deploy.plan.json.
84
85
  fz deploy check — Refuse stale plans, unsafe provider/action coordinates and unresolved deployment blockers.
85
- fz bootstrap platform remote prepare --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.jsonDry-plan one pinned operator-to-guest bootstrap; add --apply only after review.
86
+ fz bootstrap platform bundle --root /absolute/api --branch dev --output /secure/forgezero/development/api.bundleBuild one verified release-generation-one source bundle without a Git credential.
87
+ fz bootstrap platform remote apply --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.json — Dry-plan one pinned operator-to-guest bootstrap; add --apply only after review.
86
88
  fz bootstrap platform --help — Inspect attended platform bootstrap inputs before applying them.
87
89
  fz bootstrap metal --help — Inspect the identity-only physical provisioner bootstrap.
88
90
  fz host transactions|schema-plan|schema-apply — Plan fixed supervised database maintenance; add --apply only after reviewing the exact argv and credential names.
@@ -93,7 +95,8 @@ bun add -g @forgezero/agent
93
95
  fz deploy init
94
96
  fz deploy compile
95
97
  fz deploy check
96
- fz bootstrap platform remote prepare --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.json
98
+ fz bootstrap platform bundle --root /absolute/api --branch dev --output /secure/forgezero/development/api.bundle
99
+ fz bootstrap platform remote apply --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.json
97
100
  fz bootstrap platform --help
98
101
  fz bootstrap metal --help
99
102
  fz host transactions|schema-plan|schema-apply
@@ -278,38 +281,64 @@ import type { AgentUpdateOutcome, AgentUpdateReceipt, AgentUpdateRequest, AgentU
278
281
  <a id="forgezero-agent-bootstrap"></a>
279
282
  ## @forgezero/agent/bootstrap
280
283
 
281
- Typed one-time platform bootstrap and internal API-driven enrolled-compute activation contracts. Named value imports: BOOTSTRAP_STATE_PATH, PLATFORM_BOOTSTRAP_PROFILES, applyBootstrap, bootstrapIdentityDigest, bootstrapStatus, localBootstrapHost, planBootstrap, preparePlatformBootstrap, readBootstrapConfig, resolveInstalledBootstrapKind, validateBootstrapConfig, validateEnrolledComputeBootstrapSecrets, validatePlatformBootstrapSecrets. Named type imports: BootstrapConfig, BootstrapEnvironment, BootstrapHost, BootstrapPlan, BootstrapResult, BootstrapSecrets, BootstrapStatus, BootstrapStep, DatabaseAgencyParticipation, DatabaseBootstrapRole, EnrolledComputeActivationConfig, EnrolledComputeBootstrapSecrets, InstalledBootstrapKind, PlatformBootstrapConfig, PlatformBootstrapPreparation, PlatformBootstrapProfile, PlatformBootstrapSecrets, PlatformEnrolmentSource. Import only the names used by this file.
284
+ Typed one-time platform bootstrap and internal API-driven enrolled-compute activation contracts. Named value imports: BOOTSTRAP_STATE_PATH, PLATFORM_BOOTSTRAP_PROFILES, applyBootstrap, bootstrapIdentityDigest, bootstrapStatus, localBootstrapHost, planBootstrap, planBootstrapAgentInstall, readBootstrapConfig, resolveInstalledBootstrapKind, validateBootstrapConfig, validateEnrolledComputeBootstrapSecrets, validatePlatformBootstrapSecrets. Named type imports: BootstrapAgentInstallPhase, BootstrapConfig, BootstrapEnvironment, BootstrapHost, BootstrapPlan, BootstrapResult, BootstrapSecrets, BootstrapStatus, BootstrapStep, DatabaseAgencyParticipation, DatabaseBootstrapRole, EnrolledComputeActivationConfig, EnrolledComputeBootstrapSecrets, InstalledBootstrapKind, PlatformBootstrapConfig, PlatformBootstrapProfile, PlatformBootstrapSecrets, PlatformEnrolmentSource. Import only the names used by this file.
282
285
 
283
286
  ```text
284
287
  import { BOOTSTRAP_STATE_PATH, PLATFORM_BOOTSTRAP_PROFILES, applyBootstrap, bootstrapIdentityDigest, bootstrapStatus, localBootstrapHost } from '@forgezero/agent/bootstrap';
285
- import { planBootstrap, preparePlatformBootstrap, readBootstrapConfig, resolveInstalledBootstrapKind, validateBootstrapConfig, validateEnrolledComputeBootstrapSecrets } from '@forgezero/agent/bootstrap';
288
+ import { planBootstrap, planBootstrapAgentInstall, readBootstrapConfig, resolveInstalledBootstrapKind, validateBootstrapConfig, validateEnrolledComputeBootstrapSecrets } from '@forgezero/agent/bootstrap';
286
289
  import { validatePlatformBootstrapSecrets } from '@forgezero/agent/bootstrap';
287
- import type { BootstrapConfig, BootstrapEnvironment, BootstrapHost, BootstrapPlan, BootstrapResult, BootstrapSecrets } from '@forgezero/agent/bootstrap';
288
- import type { BootstrapStatus, BootstrapStep, DatabaseAgencyParticipation, DatabaseBootstrapRole, EnrolledComputeActivationConfig, EnrolledComputeBootstrapSecrets } from '@forgezero/agent/bootstrap';
289
- import type { InstalledBootstrapKind, PlatformBootstrapConfig, PlatformBootstrapPreparation, PlatformBootstrapProfile, PlatformBootstrapSecrets, PlatformEnrolmentSource } from '@forgezero/agent/bootstrap';
290
+ import type { BootstrapAgentInstallPhase, BootstrapConfig, BootstrapEnvironment, BootstrapHost, BootstrapPlan, BootstrapResult } from '@forgezero/agent/bootstrap';
291
+ import type { BootstrapSecrets, BootstrapStatus, BootstrapStep, DatabaseAgencyParticipation, DatabaseBootstrapRole, EnrolledComputeActivationConfig } from '@forgezero/agent/bootstrap';
292
+ import type { EnrolledComputeBootstrapSecrets, InstalledBootstrapKind, PlatformBootstrapConfig, PlatformBootstrapProfile, PlatformBootstrapSecrets, PlatformEnrolmentSource } from '@forgezero/agent/bootstrap';
293
+ ```
294
+
295
+ <a id="forgezero-agent-bootstrap-bundle"></a>
296
+ ## @forgezero/agent/bootstrap-bundle
297
+
298
+ Owner-only exact-revision Git bundle and SHA-256 manifest for credential-free release generation one. Named value imports: BOOTSTRAP_BUNDLE_FORMAT, BOOTSTRAP_BUNDLE_KIND, MAX_BOOTSTRAP_BUNDLE_BYTES, buildBootstrapBundle, parseBootstrapBundleManifest, readBootstrapBundle, sha256File. Named type imports: BootstrapBundleBuildInput, BootstrapBundleBuildResult, BootstrapBundleCommand, BootstrapBundleCommandResult, BootstrapBundleManifest. Import only the names used by this file.
299
+
300
+ ```text
301
+ import { BOOTSTRAP_BUNDLE_FORMAT, BOOTSTRAP_BUNDLE_KIND, MAX_BOOTSTRAP_BUNDLE_BYTES, buildBootstrapBundle, parseBootstrapBundleManifest, readBootstrapBundle } from '@forgezero/agent/bootstrap-bundle';
302
+ import { sha256File } from '@forgezero/agent/bootstrap-bundle';
303
+ import type { BootstrapBundleBuildInput, BootstrapBundleBuildResult, BootstrapBundleCommand, BootstrapBundleCommandResult, BootstrapBundleManifest } from '@forgezero/agent/bootstrap-bundle';
304
+ ```
305
+
306
+ ## @forgezero/agent/bootstrap-bundle — Build release generation one without a deploy key
307
+
308
+ The builder refuses tracked drift, resolves one exact commit, creates an owner-only Git bundle and writes a byte-count/SHA-256 manifest. Bootstrap consumes the artifact after activation.
309
+
310
+ ```text
311
+ import { buildBootstrapBundle, readBootstrapBundle } from '@forgezero/agent/bootstrap-bundle';
312
+
313
+ const built = await buildBootstrapBundle({
314
+ repositoryRoot: '/work/forgezero-api', branch: 'main',
315
+ outputPath: '/secure/forgezero/production/api.bundle'
316
+ });
317
+ await readBootstrapBundle(built.bundlePath, built.manifestPath);
290
318
  ```
291
319
 
292
320
  <a id="forgezero-agent-operator-bootstrap"></a>
293
321
  ## @forgezero/agent/operator-bootstrap
294
322
 
295
- Pinned-host operator transport that stages owner-only handoffs and invokes the same typed platform bootstrap through the caller-approved SSH agent. Named value imports: applyOperatorMetalBootstrap, applyOperatorPlatformBootstrap, planOperatorMetalBootstrap, planOperatorPlatformBootstrap, readOperatorMetalBootstrapRequest, readOperatorPlatformBootstrapRequest. Named type imports: OperatorCommand, OperatorCommandResult, OperatorMetalBootstrapMode, OperatorMetalBootstrapPlan, OperatorMetalBootstrapRequest, OperatorPlatformBootstrapMode, OperatorPlatformBootstrapOptions, OperatorPlatformBootstrapPlan, OperatorPlatformBootstrapRequest, OperatorSshHop. Import only the names used by this file.
323
+ Pinned-host operator transport that stages owner-only handoffs and invokes the same typed platform bootstrap through the caller-approved SSH agent. Named value imports: applyOperatorMetalBootstrap, applyOperatorPlatformBootstrap, applyOperatorPlatformFleetBootstrap, createOperatorSshHop, planOperatorMetalBootstrap, planOperatorPlatformBootstrap, planOperatorPlatformFleetBootstrap, readOperatorMetalBootstrapRequest, readOperatorPlatformBootstrapFleetRequest, readOperatorPlatformBootstrapRequest, writeOperatorMetalBootstrapRequest, writeOperatorPlatformBootstrapFleetRequest, writeOperatorPlatformBootstrapRequest. Named type imports: OperatorCommand, OperatorCommandResult, OperatorMetalBootstrapMode, OperatorMetalBootstrapPlan, OperatorMetalBootstrapRequest, OperatorPlatformBootstrapFleetOutcome, OperatorPlatformBootstrapFleetPlan, OperatorPlatformBootstrapFleetRequest, OperatorPlatformBootstrapFleetResult, OperatorPlatformBootstrapMode, OperatorPlatformBootstrapOptions, OperatorPlatformBootstrapPlan, OperatorPlatformBootstrapRequest, OperatorSshHop. Import only the names used by this file.
296
324
 
297
325
  ```text
298
- import { applyOperatorMetalBootstrap, applyOperatorPlatformBootstrap, planOperatorMetalBootstrap, planOperatorPlatformBootstrap, readOperatorMetalBootstrapRequest, readOperatorPlatformBootstrapRequest } from '@forgezero/agent/operator-bootstrap';
299
- import type { OperatorCommand, OperatorCommandResult, OperatorMetalBootstrapMode, OperatorMetalBootstrapPlan, OperatorMetalBootstrapRequest, OperatorPlatformBootstrapMode } from '@forgezero/agent/operator-bootstrap';
300
- import type { OperatorPlatformBootstrapOptions, OperatorPlatformBootstrapPlan, OperatorPlatformBootstrapRequest, OperatorSshHop } from '@forgezero/agent/operator-bootstrap';
326
+ import { applyOperatorMetalBootstrap, applyOperatorPlatformBootstrap, applyOperatorPlatformFleetBootstrap, createOperatorSshHop, planOperatorMetalBootstrap, planOperatorPlatformBootstrap } from '@forgezero/agent/operator-bootstrap';
327
+ import { planOperatorPlatformFleetBootstrap, readOperatorMetalBootstrapRequest, readOperatorPlatformBootstrapFleetRequest, readOperatorPlatformBootstrapRequest, writeOperatorMetalBootstrapRequest, writeOperatorPlatformBootstrapFleetRequest } from '@forgezero/agent/operator-bootstrap';
328
+ import { writeOperatorPlatformBootstrapRequest } from '@forgezero/agent/operator-bootstrap';
329
+ import type { OperatorCommand, OperatorCommandResult, OperatorMetalBootstrapMode, OperatorMetalBootstrapPlan, OperatorMetalBootstrapRequest, OperatorPlatformBootstrapFleetOutcome } from '@forgezero/agent/operator-bootstrap';
330
+ import type { OperatorPlatformBootstrapFleetPlan, OperatorPlatformBootstrapFleetRequest, OperatorPlatformBootstrapFleetResult, OperatorPlatformBootstrapMode, OperatorPlatformBootstrapOptions, OperatorPlatformBootstrapPlan } from '@forgezero/agent/operator-bootstrap';
331
+ import type { OperatorPlatformBootstrapRequest, OperatorSshHop } from '@forgezero/agent/operator-bootstrap';
301
332
  ```
302
333
 
303
334
  ## @forgezero/agent/operator-bootstrap — Run the same typed platform bootstrap from an operator laptop
304
335
 
305
- The request contains only an explicit target IP, pinned host public key/fingerprint, the caller public-key file, SSH-agent socket and a path to the reviewed secret-free platform config. Credential values come from hidden prompts or authenticated SSH stdin and are sealed immediately as systemd credentials; the private SSH key stays in the operator agent, and the default command is a non-contacting plan.
336
+ The request contains the pinned SSH transport, reviewed platform config, and one verified local Git bundle copied identically to every genesis compute. No repository key is created. Hidden credential values are sealed immediately as systemd credentials.
306
337
 
307
338
  ```text
308
- fz bootstrap platform remote prepare \
309
- --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.json
339
+ fz bootstrap platform remote apply --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.json
310
340
  # Review, then allow the SSH-agent approval prompt:
311
- fz bootstrap platform remote prepare \
312
- --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.json --apply
341
+ fz bootstrap platform remote apply --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.json --apply
313
342
  ```
314
343
 
315
344
  <a id="forgezero-agent-host-maintenance"></a>
@@ -749,7 +749,7 @@ async function postSignedNode(options, path, body) {
749
749
  }
750
750
 
751
751
  // src/version.ts
752
- var VERSION3 = "0.1.62";
752
+ var VERSION3 = "0.1.64";
753
753
 
754
754
  // src/agent-heartbeat.ts
755
755
  var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
@@ -0,0 +1,31 @@
1
+ export declare const BOOTSTRAP_BUNDLE_FORMAT: 1;
2
+ export declare const BOOTSTRAP_BUNDLE_KIND: "forgezero-api-git-bundle";
3
+ export declare const MAX_BOOTSTRAP_BUNDLE_BYTES: number;
4
+ export interface BootstrapBundleManifest {
5
+ format: typeof BOOTSTRAP_BUNDLE_FORMAT;
6
+ kind: typeof BOOTSTRAP_BUNDLE_KIND;
7
+ branch: string;
8
+ revision: string;
9
+ sha256: string;
10
+ bytes: number;
11
+ createdAt: string;
12
+ }
13
+ export interface BootstrapBundleBuildInput {
14
+ repositoryRoot: string;
15
+ outputPath: string;
16
+ branch: string;
17
+ }
18
+ export interface BootstrapBundleBuildResult {
19
+ bundlePath: string;
20
+ manifestPath: string;
21
+ manifest: BootstrapBundleManifest;
22
+ }
23
+ export interface BootstrapBundleCommandResult {
24
+ exitCode: number;
25
+ output: string;
26
+ }
27
+ export type BootstrapBundleCommand = (argv: readonly string[]) => Promise<BootstrapBundleCommandResult>;
28
+ export declare function sha256File(path: string): Promise<string>;
29
+ export declare function parseBootstrapBundleManifest(value: unknown): BootstrapBundleManifest;
30
+ export declare function readBootstrapBundle(bundlePath: string, manifestPath?: string): Promise<BootstrapBundleBuildResult>;
31
+ export declare function buildBootstrapBundle(input: BootstrapBundleBuildInput, exec?: BootstrapBundleCommand): Promise<BootstrapBundleBuildResult>;
@@ -0,0 +1,165 @@
1
+ // @bun
2
+ // src/bootstrap-bundle.ts
3
+ import { createHash, randomBytes } from "crypto";
4
+ import {
5
+ chmodSync,
6
+ createReadStream,
7
+ existsSync,
8
+ lstatSync,
9
+ mkdirSync,
10
+ readFileSync,
11
+ renameSync,
12
+ rmSync,
13
+ writeFileSync
14
+ } from "fs";
15
+ import { dirname, isAbsolute, resolve } from "path";
16
+ var BOOTSTRAP_BUNDLE_FORMAT = 1;
17
+ var BOOTSTRAP_BUNDLE_KIND = "forgezero-api-git-bundle";
18
+ var MAX_BOOTSTRAP_BUNDLE_BYTES = 512 * 1024 * 1024;
19
+ var run = async (argv) => {
20
+ const child = Bun.spawn([...argv], { stdin: "ignore", stdout: "pipe", stderr: "pipe" });
21
+ const [stdout, stderr, exitCode] = await Promise.all([
22
+ new Response(child.stdout).text(),
23
+ new Response(child.stderr).text(),
24
+ child.exited
25
+ ]);
26
+ return { exitCode, output: `${stdout}${stderr}`.slice(0, 65536) };
27
+ };
28
+ var checked = async (exec, argv, label) => {
29
+ const result = await exec(argv);
30
+ if (result.exitCode !== 0)
31
+ throw new Error(`${label} failed${result.output.trim() ? `: ${result.output.trim()}` : ""}`);
32
+ return result.output.trim();
33
+ };
34
+ async function sha256File(path) {
35
+ const hash = createHash("sha256");
36
+ await new Promise((resolveDone, reject) => {
37
+ const stream = createReadStream(path);
38
+ stream.on("data", (chunk) => hash.update(chunk));
39
+ stream.on("error", reject);
40
+ stream.on("end", resolveDone);
41
+ });
42
+ return hash.digest("hex");
43
+ }
44
+ var branchName = (value) => {
45
+ 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("-")) {
46
+ throw new Error("bootstrap bundle branch is malformed");
47
+ }
48
+ return value;
49
+ };
50
+ function parseBootstrapBundleManifest(value) {
51
+ if (!value || typeof value !== "object" || Array.isArray(value))
52
+ throw new Error("bootstrap bundle manifest must be an object");
53
+ const source = value;
54
+ const allowed = ["format", "kind", "branch", "revision", "sha256", "bytes", "createdAt"];
55
+ const unknown = Object.keys(source).filter((key) => !allowed.includes(key));
56
+ if (unknown.length)
57
+ throw new Error(`bootstrap bundle manifest contains unknown field ${unknown[0]}`);
58
+ if (source.format !== BOOTSTRAP_BUNDLE_FORMAT || source.kind !== BOOTSTRAP_BUNDLE_KIND) {
59
+ throw new Error("bootstrap bundle manifest format is unsupported");
60
+ }
61
+ branchName(source.branch);
62
+ if (typeof source.revision !== "string" || !/^[a-f0-9]{40}$/.test(source.revision)) {
63
+ throw new Error("bootstrap bundle revision must be an exact lowercase Git commit");
64
+ }
65
+ if (typeof source.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(source.sha256)) {
66
+ throw new Error("bootstrap bundle digest is malformed");
67
+ }
68
+ if (!Number.isSafeInteger(source.bytes) || Number(source.bytes) < 1 || Number(source.bytes) > MAX_BOOTSTRAP_BUNDLE_BYTES) {
69
+ throw new Error("bootstrap bundle size is outside the supported boundary");
70
+ }
71
+ if (typeof source.createdAt !== "string" || Number.isNaN(Date.parse(source.createdAt))) {
72
+ throw new Error("bootstrap bundle creation time is malformed");
73
+ }
74
+ return source;
75
+ }
76
+ function ownerRegularFile(path, maximum, label) {
77
+ if (!isAbsolute(path) || resolve(path) !== path || /[\r\n\0]/.test(path)) {
78
+ throw new Error(`${label} path must be canonical and absolute`);
79
+ }
80
+ const value = lstatSync(path);
81
+ const uid = process.getuid?.() ?? value.uid;
82
+ if (!value.isFile() || value.isSymbolicLink() || value.uid !== uid || value.nlink !== 1 || (value.mode & 63) !== 0 || value.size < 1 || value.size > maximum) {
83
+ throw new Error(`${label} must be an owner-only regular file with one link and at most ${maximum} bytes`);
84
+ }
85
+ }
86
+ async function readBootstrapBundle(bundlePath, manifestPath = `${bundlePath}.json`) {
87
+ ownerRegularFile(bundlePath, MAX_BOOTSTRAP_BUNDLE_BYTES, "bootstrap bundle");
88
+ ownerRegularFile(manifestPath, 16 * 1024, "bootstrap bundle manifest");
89
+ let parsed;
90
+ try {
91
+ parsed = JSON.parse(readFileSync(manifestPath, "utf8"));
92
+ } catch {
93
+ throw new Error("bootstrap bundle manifest is not valid JSON");
94
+ }
95
+ const manifest = parseBootstrapBundleManifest(parsed);
96
+ const size = lstatSync(bundlePath).size;
97
+ if (size !== manifest.bytes || await sha256File(bundlePath) !== manifest.sha256) {
98
+ throw new Error("bootstrap bundle bytes do not match their manifest");
99
+ }
100
+ return { bundlePath, manifestPath, manifest };
101
+ }
102
+ async function buildBootstrapBundle(input, exec = run) {
103
+ const repositoryRoot = resolve(input.repositoryRoot);
104
+ const outputPath = resolve(input.outputPath);
105
+ const manifestPath = `${outputPath}.json`;
106
+ const branch = branchName(input.branch);
107
+ if (!isAbsolute(input.outputPath) || outputPath !== input.outputPath) {
108
+ throw new Error("bootstrap bundle output must be a canonical absolute path");
109
+ }
110
+ if (existsSync(outputPath) || existsSync(manifestPath)) {
111
+ throw new Error("bootstrap bundle output already exists");
112
+ }
113
+ const root = lstatSync(repositoryRoot);
114
+ if (!root.isDirectory() || root.isSymbolicLink())
115
+ throw new Error("bootstrap bundle source must be a real directory");
116
+ mkdirSync(dirname(outputPath), { recursive: true, mode: 448 });
117
+ const parent = lstatSync(dirname(outputPath));
118
+ const uid = process.getuid?.() ?? parent.uid;
119
+ if (!parent.isDirectory() || parent.isSymbolicLink() || parent.uid !== uid || (parent.mode & 63) !== 0) {
120
+ throw new Error("bootstrap bundle output directory must be caller-owned and owner-only");
121
+ }
122
+ const dirty = await checked(exec, ["git", "-C", repositoryRoot, "status", "--porcelain=v1", "--untracked-files=no"], "Git worktree check");
123
+ if (dirty)
124
+ throw new Error("bootstrap bundle source has tracked changes; commit the reviewed API release first");
125
+ const revision = (await checked(exec, ["git", "-C", repositoryRoot, "rev-parse", "--verify", `refs/heads/${branch}^{commit}`], "Git branch resolution")).toLowerCase();
126
+ if (!/^[a-f0-9]{40}$/.test(revision))
127
+ throw new Error("bootstrap bundle branch did not resolve to one exact commit");
128
+ const temporary = `${outputPath}.next.${process.pid}.${randomBytes(6).toString("hex")}`;
129
+ try {
130
+ await checked(exec, ["git", "-C", repositoryRoot, "bundle", "create", temporary, `refs/heads/${branch}`], "Git bundle creation");
131
+ chmodSync(temporary, 384);
132
+ const file = lstatSync(temporary);
133
+ if (!file.isFile() || file.isSymbolicLink() || file.size < 1 || file.size > MAX_BOOTSTRAP_BUNDLE_BYTES) {
134
+ throw new Error("created bootstrap bundle is outside the supported boundary");
135
+ }
136
+ await checked(exec, ["git", "bundle", "verify", temporary], "Git bundle verification");
137
+ const manifest = {
138
+ format: BOOTSTRAP_BUNDLE_FORMAT,
139
+ kind: BOOTSTRAP_BUNDLE_KIND,
140
+ branch,
141
+ revision,
142
+ sha256: await sha256File(temporary),
143
+ bytes: file.size,
144
+ createdAt: new Date().toISOString()
145
+ };
146
+ const manifestTemporary = `${manifestPath}.next.${process.pid}.${randomBytes(6).toString("hex")}`;
147
+ writeFileSync(manifestTemporary, `${JSON.stringify(manifest, null, 2)}
148
+ `, { mode: 384, flag: "wx" });
149
+ renameSync(temporary, outputPath);
150
+ renameSync(manifestTemporary, manifestPath);
151
+ return { bundlePath: outputPath, manifestPath, manifest };
152
+ } catch (cause) {
153
+ rmSync(temporary, { force: true });
154
+ throw cause;
155
+ }
156
+ }
157
+ export {
158
+ sha256File,
159
+ readBootstrapBundle,
160
+ parseBootstrapBundleManifest,
161
+ buildBootstrapBundle,
162
+ MAX_BOOTSTRAP_BUNDLE_BYTES,
163
+ BOOTSTRAP_BUNDLE_KIND,
164
+ BOOTSTRAP_BUNDLE_FORMAT
165
+ };
@@ -1,6 +1,8 @@
1
1
  import { type ProvisionPlan } from './cli/agent-install';
2
+ import type { Capabilities } from './provision';
2
3
  import { type SoftwareCommandResult, type SoftwareRequirement } from './software';
3
4
  import { type PlatformSharedEnvironment } from './platform-bootstrap-runtime';
5
+ import { type BootstrapBundleManifest } from './bootstrap-bundle';
4
6
  export declare const PLATFORM_BOOTSTRAP_PROFILES: readonly ["platform-db-api", "platform-api"];
5
7
  export type PlatformBootstrapProfile = (typeof PLATFORM_BOOTSTRAP_PROFILES)[number];
6
8
  export type DatabaseBootstrapRole = 'master' | 'joiner' | 'none';
@@ -15,8 +17,11 @@ export interface PlatformBootstrapConfig {
15
17
  computeReference: string;
16
18
  nodeHostname: string;
17
19
  apiUrl: string;
18
- repository: string;
19
- branch: string;
20
+ /** One attended local source used only for release generation one. */
21
+ bootstrapBundle: {
22
+ bundleFile: string;
23
+ manifestFile: string;
24
+ };
20
25
  deployRoot?: string;
21
26
  telemetryEndpoint: string;
22
27
  /**
@@ -76,6 +81,8 @@ export interface EnrolledComputeActivationConfig {
76
81
  profile?: string;
77
82
  deployRoot?: string;
78
83
  software?: SoftwareRequirement[];
84
+ /** Compatibility-only per-node SSH deploy key. GitHub App is the scalable default. */
85
+ gitDeployKey?: boolean;
79
86
  installCloudflared?: boolean;
80
87
  installWarp?: boolean;
81
88
  /** Optional node-specific attended Tunnel/KV/Mesh handoff, identical to platform hosts. */
@@ -136,13 +143,6 @@ export interface BootstrapResult {
136
143
  inviteUrl: string;
137
144
  };
138
145
  }
139
- export interface PlatformBootstrapPreparation {
140
- kind: 'platform';
141
- prepared: true;
142
- identityDigest: string;
143
- gitPublicKey: string;
144
- next: 'register this read-only deploy key, then run --apply concurrently on all three genesis Agency members';
145
- }
146
146
  export interface BootstrapStatus {
147
147
  initialized: boolean;
148
148
  kind?: BootstrapConfig['kind'];
@@ -151,6 +151,7 @@ export interface BootstrapStatus {
151
151
  problems: string[];
152
152
  }
153
153
  export type InstalledBootstrapKind = 'compute' | 'metal';
154
+ export type BootstrapAgentInstallPhase = 'bootstrap' | 'enrol' | 'bound';
154
155
  /** One host has one bootstrap authority. Dual state is never resolved by precedence. */
155
156
  export declare function resolveInstalledBootstrapKind(states: {
156
157
  compute: boolean;
@@ -176,23 +177,28 @@ export interface BootstrapHost {
176
177
  }): Promise<SoftwareCommandResult>;
177
178
  sleep?(milliseconds: number): Promise<void>;
178
179
  ensureSoftware(requirements: readonly SoftwareRequirement[]): Promise<unknown>;
179
- installAgent(config: BootstrapConfig): Promise<ProvisionPlan>;
180
+ installAgent(config: BootstrapConfig, phase: BootstrapAgentInstallPhase): Promise<ProvisionPlan>;
180
181
  }
181
182
  export declare const BOOTSTRAP_STATE_PATH = "/var/lib/forgezero/bootstrap.json";
182
183
  export declare function validateBootstrapConfig(value: BootstrapConfig): BootstrapConfig;
183
184
  export declare function planBootstrap(input: BootstrapConfig, initialized?: boolean): BootstrapPlan;
184
185
  export declare function bootstrapIdentityDigest(config: BootstrapConfig): string;
185
- /**
186
- * Install only the common Agent boundary and generate its machine-local Git
187
- * identity. Private repositories cannot be deployed until the owner registers
188
- * this public key, so pretending one monolithic first command can do both is a
189
- * sequencing bug. The bound intent makes the later apply resumable and refuses
190
- * a different config after this first privileged mutation.
191
- */
192
- export declare function preparePlatformBootstrap(input: PlatformBootstrapConfig, host?: BootstrapHost): Promise<PlatformBootstrapPreparation>;
193
186
  export declare function bootstrapStatus(host?: BootstrapHost): Promise<BootstrapStatus>;
194
187
  export declare function applyBootstrap(input: BootstrapConfig, host?: BootstrapHost, secrets?: BootstrapSecrets, dependencies?: {
195
188
  fetcher?: typeof fetch;
196
189
  }): Promise<BootstrapResult>;
197
190
  export declare function readBootstrapConfig(path: string): BootstrapConfig;
191
+ interface BootstrapAgentInitialBundle {
192
+ path: string;
193
+ manifestPath: string;
194
+ manifest: BootstrapBundleManifest;
195
+ }
196
+ /** Compose one real local Agent convergence without consulting mutable globals. */
197
+ export declare function planBootstrapAgentInstall(config: BootstrapConfig, phase: BootstrapAgentInstallPhase, context: {
198
+ capabilities: Capabilities;
199
+ hasBinding: boolean;
200
+ hasEnrolCredential: boolean;
201
+ initialBundle?: BootstrapAgentInitialBundle;
202
+ }): ProvisionPlan;
198
203
  export declare function localBootstrapHost(): BootstrapHost;
204
+ export {};