@forgezero/agent 0.1.87 → 0.1.89
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 +23 -9
- package/dist/agent-handover.d.ts +7 -1
- package/dist/agent-heartbeat.js +25 -9
- package/dist/agent-update-helper.d.ts +1 -0
- package/dist/agent-update-helper.js +16 -7
- package/dist/bootstrap.d.ts +8 -1
- package/dist/bootstrap.js +228 -54
- package/dist/capacity-calibration.d.ts +35 -5
- package/dist/capacity-calibration.js +66 -22
- package/dist/cli/agent-install.d.ts +3 -0
- package/dist/community-rehearsal-host.js +7 -2
- package/dist/control.d.ts +3 -1
- package/dist/definition.js +83 -28
- package/dist/deploy-file.js +83 -28
- package/dist/deployment.d.ts +5 -0
- package/dist/egress-policy.d.ts +4 -0
- package/dist/fz-agent.js +440 -158
- package/dist/fz.js +413 -129
- package/dist/host-maintenance.js +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/metal-bootstrap.js +1 -1
- package/dist/metal-helper-socket.js +7 -2
- package/dist/metal-provision.js +7 -2
- package/dist/operator-bootstrap.d.ts +12 -0
- package/dist/operator-bootstrap.js +372 -64
- package/dist/platform-bootstrap-runtime.d.ts +1 -0
- package/dist/platform-bootstrap-runtime.js +47 -8
- package/dist/platform-fleet-verification.js +278 -94
- package/dist/platform-genesis.js +7 -2
- package/dist/provision.d.ts +5 -1
- package/dist/provision.js +201 -78
- package/dist/recovery-host.js +1 -1
- package/dist/software-helper.js +144 -48
- package/dist/software.js +7 -2
- package/dist/ubuntu.js +7 -2
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/schema/deploy-v3.json +5 -2
package/README.md
CHANGED
|
@@ -272,10 +272,11 @@ export const selectedCapability = DEFAULT_LIFECYCLE_HELPER_SOCKET;
|
|
|
272
272
|
<a id="forgezero-agent-provision"></a>
|
|
273
273
|
## @forgezero/agent/provision
|
|
274
274
|
|
|
275
|
-
The install plan as data, with no transport — so `fz` running locally and the platform running over SSH provision an identical machine. This entry exposes
|
|
275
|
+
The install plan as data, with no transport — so `fz` running locally and the platform running over SSH provision an identical machine. This entry exposes 38 named value exports and 11 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
|
|
276
276
|
|
|
277
277
|
```text
|
|
278
278
|
import {
|
|
279
|
+
AGENT_CANDIDATE_UNIT_PATH,
|
|
279
280
|
AGENT_EGRESS_UNIT_PATH,
|
|
280
281
|
AGENT_SOCKET_PROXY_UNIT_PATH,
|
|
281
282
|
AGENT_SOCKET_UNIT_PATH,
|
|
@@ -295,8 +296,11 @@ import {
|
|
|
295
296
|
WARP_CONFIG_UNIT_PATH,
|
|
296
297
|
WARP_SERVICE_DROP_IN_PATH,
|
|
297
298
|
agentBackendSocketPath,
|
|
299
|
+
agentCandidateSocketPath,
|
|
300
|
+
agentCandidateUnit,
|
|
298
301
|
agentEgressUnit,
|
|
299
302
|
agentEnrolmentUnit,
|
|
303
|
+
agentRoutingSocketPath,
|
|
300
304
|
agentSocketProxyUnit,
|
|
301
305
|
agentSocketUnit,
|
|
302
306
|
agentUnit,
|
|
@@ -333,10 +337,10 @@ This minimal executable use imports one concrete value from this exact entry poi
|
|
|
333
337
|
|
|
334
338
|
```text
|
|
335
339
|
import {
|
|
336
|
-
|
|
340
|
+
AGENT_CANDIDATE_UNIT_PATH,
|
|
337
341
|
} from '@forgezero/agent/provision';
|
|
338
342
|
|
|
339
|
-
export const selectedCapability =
|
|
343
|
+
export const selectedCapability = AGENT_CANDIDATE_UNIT_PATH;
|
|
340
344
|
```
|
|
341
345
|
|
|
342
346
|
<a id="forgezero-agent-platform-genesis"></a>
|
|
@@ -534,15 +538,18 @@ export const selectedCapability = heartbeatAgentOnce;
|
|
|
534
538
|
<a id="forgezero-agent-agent-update"></a>
|
|
535
539
|
## @forgezero/agent/agent-update
|
|
536
540
|
|
|
537
|
-
Verified Agent update planning against the platform release coordinate. This entry exposes
|
|
541
|
+
Verified Agent update planning against the platform release coordinate. This entry exposes 11 named value exports and 4 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
|
|
538
542
|
|
|
539
543
|
```text
|
|
540
544
|
import {
|
|
545
|
+
DEFAULT_AGENT_CANDIDATE_LINK,
|
|
541
546
|
DEFAULT_AGENT_RELEASE_ROOT,
|
|
542
547
|
DEFAULT_AGENT_UPDATE_SOCKET,
|
|
543
548
|
MAX_AGENT_TARBALL_BYTES,
|
|
549
|
+
clearAgentCandidate,
|
|
544
550
|
compareVersions,
|
|
545
551
|
restoreAgentRelease,
|
|
552
|
+
selectAgentCandidate,
|
|
546
553
|
selectAgentRelease,
|
|
547
554
|
stageAgentRelease,
|
|
548
555
|
validateAgentRelease,
|
|
@@ -562,10 +569,10 @@ This minimal executable use imports one concrete value from this exact entry poi
|
|
|
562
569
|
|
|
563
570
|
```text
|
|
564
571
|
import {
|
|
565
|
-
|
|
572
|
+
DEFAULT_AGENT_CANDIDATE_LINK,
|
|
566
573
|
} from '@forgezero/agent/agent-update';
|
|
567
574
|
|
|
568
|
-
export const selectedCapability =
|
|
575
|
+
export const selectedCapability = DEFAULT_AGENT_CANDIDATE_LINK;
|
|
569
576
|
```
|
|
570
577
|
|
|
571
578
|
<a id="forgezero-agent-agent-update-helper"></a>
|
|
@@ -709,7 +716,7 @@ await readBootstrapBundle(built.bundlePath, built.manifestPath);
|
|
|
709
716
|
<a id="forgezero-agent-operator-bootstrap"></a>
|
|
710
717
|
## @forgezero/agent/operator-bootstrap
|
|
711
718
|
|
|
712
|
-
Pinned-host operator transport that stages owner-only handoffs and invokes the same typed platform bootstrap through the caller-approved SSH agent. This entry exposes
|
|
719
|
+
Pinned-host operator transport that stages owner-only handoffs and invokes the same typed platform bootstrap through the caller-approved SSH agent. This entry exposes 24 named value exports and 17 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
|
|
713
720
|
|
|
714
721
|
```text
|
|
715
722
|
import {
|
|
@@ -718,6 +725,10 @@ import {
|
|
|
718
725
|
applyOperatorPlatformFleetBootstrap,
|
|
719
726
|
collectOperatorGuestHostKeys,
|
|
720
727
|
createOperatorSshHop,
|
|
728
|
+
operatorMetalClusterBootstrapCode,
|
|
729
|
+
operatorPackagedAgentPath,
|
|
730
|
+
operatorPackagedFzCliPath,
|
|
731
|
+
operatorPackagedGitSshPath,
|
|
721
732
|
operatorPlatformFleetCoordinates,
|
|
722
733
|
planOperatorMetalBootstrap,
|
|
723
734
|
planOperatorPlatformBootstrap,
|
|
@@ -726,6 +737,7 @@ import {
|
|
|
726
737
|
readOperatorMetalBootstrapRequest,
|
|
727
738
|
readOperatorPlatformBootstrapFleetRequest,
|
|
728
739
|
readOperatorPlatformBootstrapRequest,
|
|
740
|
+
redactOperatorSecretDiagnostic,
|
|
729
741
|
verifyOperatorPlatformFleet,
|
|
730
742
|
withOperatorGuestTransport,
|
|
731
743
|
writeOperatorGuestHostKeyEvidence,
|
|
@@ -800,7 +812,7 @@ console.log(plan.argv);
|
|
|
800
812
|
<a id="forgezero-agent-capacity-calibration"></a>
|
|
801
813
|
## @forgezero/agent/capacity-calibration
|
|
802
814
|
|
|
803
|
-
One-time target-local safe-concurrency calibration with private evidence. This entry exposes 3 named value exports and
|
|
815
|
+
One-time target-local safe-concurrency calibration with private evidence. This entry exposes 3 named value exports and 5 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
|
|
804
816
|
|
|
805
817
|
```text
|
|
806
818
|
import {
|
|
@@ -813,6 +825,7 @@ import type {
|
|
|
813
825
|
CapacityCalibration,
|
|
814
826
|
CapacityCalibrationOptions,
|
|
815
827
|
CapacityStage,
|
|
828
|
+
HostCapacitySample,
|
|
816
829
|
ValidatedCapacityCalibrationOptions,
|
|
817
830
|
} from '@forgezero/agent/capacity-calibration';
|
|
818
831
|
```
|
|
@@ -1560,11 +1573,12 @@ export const selectedCapability = MetalProvisionError;
|
|
|
1560
1573
|
<a id="forgezero-agent-platform-bootstrap-runtime"></a>
|
|
1561
1574
|
## @forgezero/agent/platform-bootstrap-runtime
|
|
1562
1575
|
|
|
1563
|
-
Pure platform bootstrap validation, render and resumable phase-state contracts. This entry exposes
|
|
1576
|
+
Pure platform bootstrap validation, render and resumable phase-state contracts. This entry exposes 11 named value exports and 14 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
|
|
1564
1577
|
|
|
1565
1578
|
```text
|
|
1566
1579
|
import {
|
|
1567
1580
|
activatePlatformRelease,
|
|
1581
|
+
assertReleaseLinksContained,
|
|
1568
1582
|
planLocalOtlpProof,
|
|
1569
1583
|
planPlatformActivation,
|
|
1570
1584
|
platformApiCredentialSpecs,
|
package/dist/agent-handover.d.ts
CHANGED
|
@@ -6,8 +6,14 @@ export interface AgentCandidateReady {
|
|
|
6
6
|
bound: boolean;
|
|
7
7
|
vault: 'ready' | 'partial' | 'unavailable' | 'unbound';
|
|
8
8
|
}
|
|
9
|
+
export interface AgentCandidateExpectation {
|
|
10
|
+
version: string;
|
|
11
|
+
nodeKey: string;
|
|
12
|
+
bound: boolean;
|
|
13
|
+
vault: readonly AgentCandidateReady['vault'][];
|
|
14
|
+
}
|
|
9
15
|
/** New proxy connections resolve the new backend; established streams are untouched. */
|
|
10
16
|
export declare function switchAgentSocketRoute(route: string, backend: string): void;
|
|
11
17
|
/** Root-observable readiness appears only after identity, binding and Vault initialization. */
|
|
12
18
|
export declare function startAgentCandidateReady(ready: AgentCandidateReady, socketPath?: string): Server;
|
|
13
|
-
export declare function probeAgentCandidate(expected:
|
|
19
|
+
export declare function probeAgentCandidate(expected: AgentCandidateExpectation, socketPath?: string, timeoutMs?: number): Promise<boolean>;
|
package/dist/agent-heartbeat.js
CHANGED
|
@@ -284,7 +284,7 @@ function probeAgentCandidate(expected, socketPath = DEFAULT_AGENT_CANDIDATE_READ
|
|
|
284
284
|
return;
|
|
285
285
|
try {
|
|
286
286
|
const value = JSON.parse(buffer.slice(0, newline));
|
|
287
|
-
finish(value.version === expected.version &&
|
|
287
|
+
finish(value.version === expected.version && value.nodeKey === expected.nodeKey && value.bound === expected.bound && expected.vault.includes(value.vault));
|
|
288
288
|
} catch {
|
|
289
289
|
finish(false);
|
|
290
290
|
}
|
|
@@ -487,13 +487,18 @@ var restartAgent = async (target, run) => {
|
|
|
487
487
|
}
|
|
488
488
|
};
|
|
489
489
|
var candidateUnit = (target) => target === "compute" ? "forgezero-agent-candidate.service" : "forgezero-metal-agent-candidate.service";
|
|
490
|
-
var startCandidate = async (staged, target, run, probe = (expected) => probeAgentCandidate(expected, DEFAULT_AGENT_CANDIDATE_READY_SOCKET)) => {
|
|
490
|
+
var startCandidate = async (staged, target, expectedNodeKey, run, probe = (expected) => probeAgentCandidate(expected, DEFAULT_AGENT_CANDIDATE_READY_SOCKET)) => {
|
|
491
491
|
selectAgentCandidate(staged);
|
|
492
492
|
if (!await runOk(run, "/usr/bin/systemctl", ["restart", candidateUnit(target)])) {
|
|
493
493
|
clearAgentCandidate(dirname3(staged.currentLink));
|
|
494
494
|
throw new Error(`systemd could not start ${candidateUnit(target)}`);
|
|
495
495
|
}
|
|
496
|
-
if (!await probe({
|
|
496
|
+
if (!await probe({
|
|
497
|
+
version: staged.version,
|
|
498
|
+
nodeKey: expectedNodeKey,
|
|
499
|
+
bound: true,
|
|
500
|
+
vault: target === "compute" ? ["ready"] : ["unbound"]
|
|
501
|
+
})) {
|
|
497
502
|
await run({ command: "/usr/bin/systemctl", args: ["stop", candidateUnit(target)] });
|
|
498
503
|
clearAgentCandidate(dirname3(staged.currentLink));
|
|
499
504
|
throw new Error("the candidate Agent did not prove its identity and durable state");
|
|
@@ -572,8 +577,9 @@ async function activateAgentRelease(staged, options = {}) {
|
|
|
572
577
|
writeUpdateState(journalPath, receiptPath, journal);
|
|
573
578
|
let selectionAttempted = false;
|
|
574
579
|
try {
|
|
575
|
-
if (!options.candidatePrepared)
|
|
576
|
-
|
|
580
|
+
if (!options.candidatePrepared) {
|
|
581
|
+
throw new Error("direct activation requires a separately identity-bound candidate preparation");
|
|
582
|
+
}
|
|
577
583
|
if (target === "compute")
|
|
578
584
|
switchAgentSocketRoute(paths.route, paths.candidate);
|
|
579
585
|
selectionAttempted = true;
|
|
@@ -763,6 +769,9 @@ function startAgentUpdateHelper(options = {}) {
|
|
|
763
769
|
}
|
|
764
770
|
if (request.op !== "prepare")
|
|
765
771
|
throw new Error("unknown update operation");
|
|
772
|
+
if (typeof request.expectedNodeKey !== "string" || request.expectedNodeKey.length < 16 || Buffer.byteLength(request.expectedNodeKey, "utf8") > 512 || /[\0\r\n]/.test(request.expectedNodeKey)) {
|
|
773
|
+
throw new Error("Agent update expected node identity is invalid");
|
|
774
|
+
}
|
|
766
775
|
if (busy)
|
|
767
776
|
throw new Error("another Agent update or recovery is already active");
|
|
768
777
|
const prior = readJournal(journalPath, releaseRoot);
|
|
@@ -775,8 +784,8 @@ function startAgentUpdateHelper(options = {}) {
|
|
|
775
784
|
currentVersion: request.currentVersion,
|
|
776
785
|
root: releaseRoot
|
|
777
786
|
});
|
|
778
|
-
await startCandidate(staged, request.target, runCommand);
|
|
779
|
-
pending = { staged, target: request.target, attemptId };
|
|
787
|
+
await startCandidate(staged, request.target, request.expectedNodeKey, runCommand);
|
|
788
|
+
pending = { staged, target: request.target, attemptId, expectedNodeKey: request.expectedNodeKey };
|
|
780
789
|
ownsBusy = false;
|
|
781
790
|
setTimer(() => {
|
|
782
791
|
if (pending?.attemptId !== attemptId)
|
|
@@ -908,7 +917,7 @@ async function postSignedNode(options, path, body) {
|
|
|
908
917
|
}
|
|
909
918
|
|
|
910
919
|
// src/version.ts
|
|
911
|
-
var VERSION3 = "0.1.
|
|
920
|
+
var VERSION3 = "0.1.89";
|
|
912
921
|
|
|
913
922
|
// src/agent-heartbeat.ts
|
|
914
923
|
function readAgentHostMetrics() {
|
|
@@ -1009,7 +1018,14 @@ async function heartbeatAgentOnce(options) {
|
|
|
1009
1018
|
let candidatePrepared = false;
|
|
1010
1019
|
let drained = false;
|
|
1011
1020
|
try {
|
|
1012
|
-
const update2 = options.applyUpdate ?? ((phase, next, current, attemptId) => requestAgentUpdate(phase === "prepare" ? {
|
|
1021
|
+
const update2 = options.applyUpdate ?? ((phase, next, current, attemptId) => requestAgentUpdate(phase === "prepare" ? {
|
|
1022
|
+
op: "prepare",
|
|
1023
|
+
target: options.updateTarget ?? "compute",
|
|
1024
|
+
release: next,
|
|
1025
|
+
currentVersion: current,
|
|
1026
|
+
expectedNodeKey: options.nodeKey,
|
|
1027
|
+
attemptId
|
|
1028
|
+
} : {
|
|
1013
1029
|
op: phase,
|
|
1014
1030
|
target: options.updateTarget ?? "compute",
|
|
1015
1031
|
currentVersion: current,
|
|
@@ -284,7 +284,7 @@ function probeAgentCandidate(expected, socketPath = DEFAULT_AGENT_CANDIDATE_READ
|
|
|
284
284
|
return;
|
|
285
285
|
try {
|
|
286
286
|
const value = JSON.parse(buffer.slice(0, newline));
|
|
287
|
-
finish(value.version === expected.version &&
|
|
287
|
+
finish(value.version === expected.version && value.nodeKey === expected.nodeKey && value.bound === expected.bound && expected.vault.includes(value.vault));
|
|
288
288
|
} catch {
|
|
289
289
|
finish(false);
|
|
290
290
|
}
|
|
@@ -487,13 +487,18 @@ var restartAgent = async (target, run) => {
|
|
|
487
487
|
}
|
|
488
488
|
};
|
|
489
489
|
var candidateUnit = (target) => target === "compute" ? "forgezero-agent-candidate.service" : "forgezero-metal-agent-candidate.service";
|
|
490
|
-
var startCandidate = async (staged, target, run, probe = (expected) => probeAgentCandidate(expected, DEFAULT_AGENT_CANDIDATE_READY_SOCKET)) => {
|
|
490
|
+
var startCandidate = async (staged, target, expectedNodeKey, run, probe = (expected) => probeAgentCandidate(expected, DEFAULT_AGENT_CANDIDATE_READY_SOCKET)) => {
|
|
491
491
|
selectAgentCandidate(staged);
|
|
492
492
|
if (!await runOk(run, "/usr/bin/systemctl", ["restart", candidateUnit(target)])) {
|
|
493
493
|
clearAgentCandidate(dirname3(staged.currentLink));
|
|
494
494
|
throw new Error(`systemd could not start ${candidateUnit(target)}`);
|
|
495
495
|
}
|
|
496
|
-
if (!await probe({
|
|
496
|
+
if (!await probe({
|
|
497
|
+
version: staged.version,
|
|
498
|
+
nodeKey: expectedNodeKey,
|
|
499
|
+
bound: true,
|
|
500
|
+
vault: target === "compute" ? ["ready"] : ["unbound"]
|
|
501
|
+
})) {
|
|
497
502
|
await run({ command: "/usr/bin/systemctl", args: ["stop", candidateUnit(target)] });
|
|
498
503
|
clearAgentCandidate(dirname3(staged.currentLink));
|
|
499
504
|
throw new Error("the candidate Agent did not prove its identity and durable state");
|
|
@@ -572,8 +577,9 @@ async function activateAgentRelease(staged, options = {}) {
|
|
|
572
577
|
writeUpdateState(journalPath, receiptPath, journal);
|
|
573
578
|
let selectionAttempted = false;
|
|
574
579
|
try {
|
|
575
|
-
if (!options.candidatePrepared)
|
|
576
|
-
|
|
580
|
+
if (!options.candidatePrepared) {
|
|
581
|
+
throw new Error("direct activation requires a separately identity-bound candidate preparation");
|
|
582
|
+
}
|
|
577
583
|
if (target === "compute")
|
|
578
584
|
switchAgentSocketRoute(paths.route, paths.candidate);
|
|
579
585
|
selectionAttempted = true;
|
|
@@ -763,6 +769,9 @@ function startAgentUpdateHelper(options = {}) {
|
|
|
763
769
|
}
|
|
764
770
|
if (request.op !== "prepare")
|
|
765
771
|
throw new Error("unknown update operation");
|
|
772
|
+
if (typeof request.expectedNodeKey !== "string" || request.expectedNodeKey.length < 16 || Buffer.byteLength(request.expectedNodeKey, "utf8") > 512 || /[\0\r\n]/.test(request.expectedNodeKey)) {
|
|
773
|
+
throw new Error("Agent update expected node identity is invalid");
|
|
774
|
+
}
|
|
766
775
|
if (busy)
|
|
767
776
|
throw new Error("another Agent update or recovery is already active");
|
|
768
777
|
const prior = readJournal(journalPath, releaseRoot);
|
|
@@ -775,8 +784,8 @@ function startAgentUpdateHelper(options = {}) {
|
|
|
775
784
|
currentVersion: request.currentVersion,
|
|
776
785
|
root: releaseRoot
|
|
777
786
|
});
|
|
778
|
-
await startCandidate(staged, request.target, runCommand);
|
|
779
|
-
pending = { staged, target: request.target, attemptId };
|
|
787
|
+
await startCandidate(staged, request.target, request.expectedNodeKey, runCommand);
|
|
788
|
+
pending = { staged, target: request.target, attemptId, expectedNodeKey: request.expectedNodeKey };
|
|
780
789
|
ownsBusy = false;
|
|
781
790
|
setTimer(() => {
|
|
782
791
|
if (pending?.attemptId !== attemptId)
|
package/dist/bootstrap.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ProvisionPlan } from './cli/agent-install';
|
|
2
|
-
import type
|
|
2
|
+
import { type Capabilities } from './provision';
|
|
3
3
|
import { type SoftwareCommandResult, type SoftwareRequirement } from './software';
|
|
4
4
|
import { type PlatformSharedEnvironment } from './platform-bootstrap-runtime';
|
|
5
5
|
import { type BootstrapBundleManifest } from './bootstrap-bundle';
|
|
@@ -191,6 +191,13 @@ export declare const BOOTSTRAP_STATE_PATH = "/var/lib/forgezero/bootstrap.json";
|
|
|
191
191
|
export declare function validateBootstrapConfig(value: BootstrapConfig): BootstrapConfig;
|
|
192
192
|
export declare function planBootstrap(input: BootstrapConfig, initialized?: boolean): BootstrapPlan;
|
|
193
193
|
export declare function bootstrapIdentityDigest(config: BootstrapConfig): string;
|
|
194
|
+
export declare const legacyBootstrapIdentityDigest: (config: BootstrapConfig) => string;
|
|
195
|
+
/** Exact prior-release identities accepted only while generation one is still unenrolled. */
|
|
196
|
+
export declare function interruptedPlatformReleaseIdentityDigests(config: PlatformBootstrapConfig, releaseEvidence: {
|
|
197
|
+
revision: string;
|
|
198
|
+
sha256: string;
|
|
199
|
+
branch: string;
|
|
200
|
+
}, bootstrapManifest?: BootstrapBundleManifest): readonly string[];
|
|
194
201
|
export declare function bootstrapStatus(host?: BootstrapHost): Promise<BootstrapStatus>;
|
|
195
202
|
export declare function applyBootstrap(input: BootstrapConfig, host?: BootstrapHost, secrets?: BootstrapSecrets, dependencies?: {
|
|
196
203
|
fetcher?: typeof fetch;
|