@forgezero/agent 0.1.87 → 0.1.88
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/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.js +1 -1
- package/dist/fz-agent.js +25 -9
- package/dist/fz.js +1 -1
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.js +1 -1
- package/dist/platform-fleet-verification.js +17 -8
- package/dist/provision.js +17 -8
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
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.88";
|
|
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.js
CHANGED
package/dist/fz-agent.js
CHANGED
|
@@ -9378,7 +9378,7 @@ async function writeAndCloseProcessInput(input, value) {
|
|
|
9378
9378
|
}
|
|
9379
9379
|
|
|
9380
9380
|
// src/version.ts
|
|
9381
|
-
var VERSION2 = "0.1.
|
|
9381
|
+
var VERSION2 = "0.1.88";
|
|
9382
9382
|
|
|
9383
9383
|
// src/ssh-bootstrap.ts
|
|
9384
9384
|
class SshBootstrapError extends Error {
|
|
@@ -11987,7 +11987,7 @@ function probeAgentCandidate(expected, socketPath = DEFAULT_AGENT_CANDIDATE_READ
|
|
|
11987
11987
|
return;
|
|
11988
11988
|
try {
|
|
11989
11989
|
const value = JSON.parse(buffer.slice(0, newline));
|
|
11990
|
-
finish(value.version === expected.version &&
|
|
11990
|
+
finish(value.version === expected.version && value.nodeKey === expected.nodeKey && value.bound === expected.bound && expected.vault.includes(value.vault));
|
|
11991
11991
|
} catch {
|
|
11992
11992
|
finish(false);
|
|
11993
11993
|
}
|
|
@@ -12190,13 +12190,18 @@ var restartAgent = async (target2, run2) => {
|
|
|
12190
12190
|
}
|
|
12191
12191
|
};
|
|
12192
12192
|
var candidateUnit = (target2) => target2 === "compute" ? "forgezero-agent-candidate.service" : "forgezero-metal-agent-candidate.service";
|
|
12193
|
-
var startCandidate = async (staged, target2, run2, probe = (expected) => probeAgentCandidate(expected, DEFAULT_AGENT_CANDIDATE_READY_SOCKET)) => {
|
|
12193
|
+
var startCandidate = async (staged, target2, expectedNodeKey, run2, probe = (expected) => probeAgentCandidate(expected, DEFAULT_AGENT_CANDIDATE_READY_SOCKET)) => {
|
|
12194
12194
|
selectAgentCandidate(staged);
|
|
12195
12195
|
if (!await runOk(run2, "/usr/bin/systemctl", ["restart", candidateUnit(target2)])) {
|
|
12196
12196
|
clearAgentCandidate(dirname8(staged.currentLink));
|
|
12197
12197
|
throw new Error(`systemd could not start ${candidateUnit(target2)}`);
|
|
12198
12198
|
}
|
|
12199
|
-
if (!await probe({
|
|
12199
|
+
if (!await probe({
|
|
12200
|
+
version: staged.version,
|
|
12201
|
+
nodeKey: expectedNodeKey,
|
|
12202
|
+
bound: true,
|
|
12203
|
+
vault: target2 === "compute" ? ["ready"] : ["unbound"]
|
|
12204
|
+
})) {
|
|
12200
12205
|
await run2({ command: "/usr/bin/systemctl", args: ["stop", candidateUnit(target2)] });
|
|
12201
12206
|
clearAgentCandidate(dirname8(staged.currentLink));
|
|
12202
12207
|
throw new Error("the candidate Agent did not prove its identity and durable state");
|
|
@@ -12275,8 +12280,9 @@ async function activateAgentRelease(staged, options = {}) {
|
|
|
12275
12280
|
writeUpdateState(journalPath, receiptPath, journal);
|
|
12276
12281
|
let selectionAttempted = false;
|
|
12277
12282
|
try {
|
|
12278
|
-
if (!options.candidatePrepared)
|
|
12279
|
-
|
|
12283
|
+
if (!options.candidatePrepared) {
|
|
12284
|
+
throw new Error("direct activation requires a separately identity-bound candidate preparation");
|
|
12285
|
+
}
|
|
12280
12286
|
if (target2 === "compute")
|
|
12281
12287
|
switchAgentSocketRoute(paths.route, paths.candidate);
|
|
12282
12288
|
selectionAttempted = true;
|
|
@@ -12466,6 +12472,9 @@ function startAgentUpdateHelper(options = {}) {
|
|
|
12466
12472
|
}
|
|
12467
12473
|
if (request.op !== "prepare")
|
|
12468
12474
|
throw new Error("unknown update operation");
|
|
12475
|
+
if (typeof request.expectedNodeKey !== "string" || request.expectedNodeKey.length < 16 || Buffer.byteLength(request.expectedNodeKey, "utf8") > 512 || /[\0\r\n]/.test(request.expectedNodeKey)) {
|
|
12476
|
+
throw new Error("Agent update expected node identity is invalid");
|
|
12477
|
+
}
|
|
12469
12478
|
if (busy)
|
|
12470
12479
|
throw new Error("another Agent update or recovery is already active");
|
|
12471
12480
|
const prior = readJournal(journalPath, releaseRoot);
|
|
@@ -12478,8 +12487,8 @@ function startAgentUpdateHelper(options = {}) {
|
|
|
12478
12487
|
currentVersion: request.currentVersion,
|
|
12479
12488
|
root: releaseRoot
|
|
12480
12489
|
});
|
|
12481
|
-
await startCandidate(staged, request.target, runCommand);
|
|
12482
|
-
pending = { staged, target: request.target, attemptId };
|
|
12490
|
+
await startCandidate(staged, request.target, request.expectedNodeKey, runCommand);
|
|
12491
|
+
pending = { staged, target: request.target, attemptId, expectedNodeKey: request.expectedNodeKey };
|
|
12483
12492
|
ownsBusy = false;
|
|
12484
12493
|
setTimer(() => {
|
|
12485
12494
|
if (pending?.attemptId !== attemptId)
|
|
@@ -12636,7 +12645,14 @@ async function heartbeatAgentOnce(options) {
|
|
|
12636
12645
|
let candidatePrepared = false;
|
|
12637
12646
|
let drained = false;
|
|
12638
12647
|
try {
|
|
12639
|
-
const update2 = options.applyUpdate ?? ((phase, next, current2, attemptId) => requestAgentUpdate(phase === "prepare" ? {
|
|
12648
|
+
const update2 = options.applyUpdate ?? ((phase, next, current2, attemptId) => requestAgentUpdate(phase === "prepare" ? {
|
|
12649
|
+
op: "prepare",
|
|
12650
|
+
target: options.updateTarget ?? "compute",
|
|
12651
|
+
release: next,
|
|
12652
|
+
currentVersion: current2,
|
|
12653
|
+
expectedNodeKey: options.nodeKey,
|
|
12654
|
+
attemptId
|
|
12655
|
+
} : {
|
|
12640
12656
|
op: phase,
|
|
12641
12657
|
target: options.updateTarget ?? "compute",
|
|
12642
12658
|
currentVersion: current2,
|
package/dist/fz.js
CHANGED
package/dist/metal-bootstrap.js
CHANGED
|
@@ -366,7 +366,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
|
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
// src/version.ts
|
|
369
|
-
var VERSION = "0.1.
|
|
369
|
+
var VERSION = "0.1.88";
|
|
370
370
|
|
|
371
371
|
// src/otel-collector.ts
|
|
372
372
|
var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
|
|
@@ -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)
|
|
@@ -2803,7 +2812,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
2803
2812
|
}
|
|
2804
2813
|
|
|
2805
2814
|
// src/version.ts
|
|
2806
|
-
var VERSION3 = "0.1.
|
|
2815
|
+
var VERSION3 = "0.1.88";
|
|
2807
2816
|
|
|
2808
2817
|
// src/egress-policy.ts
|
|
2809
2818
|
import { realpathSync as realpathSync3 } from "node:fs";
|
package/dist/provision.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)
|
|
@@ -2803,7 +2812,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
2803
2812
|
}
|
|
2804
2813
|
|
|
2805
2814
|
// src/version.ts
|
|
2806
|
-
var VERSION3 = "0.1.
|
|
2815
|
+
var VERSION3 = "0.1.88";
|
|
2807
2816
|
|
|
2808
2817
|
// src/egress-policy.ts
|
|
2809
2818
|
import { realpathSync as realpathSync3 } from "node:fs";
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** One package version shared by both public binaries. Pinned to package.json by tests. */
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.88";
|