@omnitron-dev/omnitron 0.3.2 → 0.3.5
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/bin/pnpmfile-sdk.mjs +40 -0
- package/bin/sdk.d.mts +8 -0
- package/bin/sdk.mjs +580 -0
- package/dist/BUILD.json +2 -2
- package/dist/cli/omnitron.js +80 -14
- package/dist/cli/omnitron.js.map +1 -1
- package/dist/commands/infra.d.ts +8 -2
- package/dist/commands/infra.d.ts.map +1 -1
- package/dist/commands/infra.js +52 -59
- package/dist/commands/infra.js.map +1 -1
- package/dist/commands/node.d.ts.map +1 -1
- package/dist/commands/node.js +8 -1
- package/dist/commands/node.js.map +1 -1
- package/dist/commands/release-store.d.ts.map +1 -1
- package/dist/commands/release-store.js +6 -9
- package/dist/commands/release-store.js.map +1 -1
- package/dist/commands/release.d.ts +4 -1
- package/dist/commands/release.d.ts.map +1 -1
- package/dist/commands/release.js +125 -5
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/stack.d.ts +1 -1
- package/dist/commands/stack.d.ts.map +1 -1
- package/dist/commands/stack.js +19 -9
- package/dist/commands/stack.js.map +1 -1
- package/dist/config/project-stacks.d.ts +5 -0
- package/dist/config/project-stacks.d.ts.map +1 -0
- package/dist/config/project-stacks.js +24 -0
- package/dist/config/project-stacks.js.map +1 -0
- package/dist/config/saved.d.ts +3 -0
- package/dist/config/saved.d.ts.map +1 -1
- package/dist/config/saved.js +1 -0
- package/dist/config/saved.js.map +1 -1
- package/dist/config/types.d.ts +4 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/daemon/daemon.d.ts.map +1 -1
- package/dist/daemon/daemon.js +30 -6
- package/dist/daemon/daemon.js.map +1 -1
- package/dist/daemon/daemon.rpc-service.d.ts +0 -26
- package/dist/daemon/daemon.rpc-service.d.ts.map +1 -1
- package/dist/daemon/daemon.rpc-service.js +23 -114
- package/dist/daemon/daemon.rpc-service.js.map +1 -1
- package/dist/daemon/execution-client.d.ts +1 -1
- package/dist/daemon/execution-client.d.ts.map +1 -1
- package/dist/daemon/execution-client.js +17 -13
- package/dist/daemon/execution-client.js.map +1 -1
- package/dist/daemon/startup-config.d.ts +1 -0
- package/dist/daemon/startup-config.d.ts.map +1 -1
- package/dist/infrastructure/container-runtime.d.ts +8 -0
- package/dist/infrastructure/container-runtime.d.ts.map +1 -1
- package/dist/infrastructure/container-runtime.js +5 -0
- package/dist/infrastructure/container-runtime.js.map +1 -1
- package/dist/infrastructure/infrastructure.service.d.ts +5 -0
- package/dist/infrastructure/infrastructure.service.d.ts.map +1 -1
- package/dist/infrastructure/infrastructure.service.js +18 -8
- package/dist/infrastructure/infrastructure.service.js.map +1 -1
- package/dist/infrastructure/presets/postgres.js +1 -1
- package/dist/infrastructure/presets/postgres.js.map +1 -1
- package/dist/infrastructure/presets/registry.d.ts.map +1 -1
- package/dist/infrastructure/presets/registry.js +11 -19
- package/dist/infrastructure/presets/registry.js.map +1 -1
- package/dist/infrastructure/stack-infra-manager.d.ts +4 -0
- package/dist/infrastructure/stack-infra-manager.d.ts.map +1 -1
- package/dist/infrastructure/stack-infra-manager.js +38 -4
- package/dist/infrastructure/stack-infra-manager.js.map +1 -1
- package/dist/mcp/tool-groups/infra.tools.d.ts.map +1 -1
- package/dist/mcp/tool-groups/infra.tools.js +4 -1
- package/dist/mcp/tool-groups/infra.tools.js.map +1 -1
- package/dist/orchestrator/app-name.d.ts +33 -0
- package/dist/orchestrator/app-name.d.ts.map +1 -0
- package/dist/orchestrator/app-name.js +68 -0
- package/dist/orchestrator/app-name.js.map +1 -0
- package/dist/orchestrator/orchestrator.service.d.ts +1 -26
- package/dist/orchestrator/orchestrator.service.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator.service.js +10 -89
- package/dist/orchestrator/orchestrator.service.js.map +1 -1
- package/dist/project/stack-diagnosis.d.ts.map +1 -1
- package/dist/project/stack-diagnosis.js +2 -3
- package/dist/project/stack-diagnosis.js.map +1 -1
- package/dist/release/artifact-store.d.ts +42 -7
- package/dist/release/artifact-store.d.ts.map +1 -1
- package/dist/release/artifact-store.js +176 -62
- package/dist/release/artifact-store.js.map +1 -1
- package/dist/release/build-run.d.ts.map +1 -1
- package/dist/release/build-run.js +8 -11
- package/dist/release/build-run.js.map +1 -1
- package/dist/release/configured-store.d.ts +6 -0
- package/dist/release/configured-store.d.ts.map +1 -0
- package/dist/release/configured-store.js +54 -0
- package/dist/release/configured-store.js.map +1 -0
- package/dist/release/framework-source.d.ts +7 -0
- package/dist/release/framework-source.d.ts.map +1 -1
- package/dist/release/framework-source.js +9 -0
- package/dist/release/framework-source.js.map +1 -1
- package/dist/release/load.d.ts +3 -0
- package/dist/release/load.d.ts.map +1 -1
- package/dist/release/load.js +8 -2
- package/dist/release/load.js.map +1 -1
- package/dist/services/infrastructure.rpc-service.d.ts +6 -2
- package/dist/services/infrastructure.rpc-service.d.ts.map +1 -1
- package/dist/services/infrastructure.rpc-service.js +43 -12
- package/dist/services/infrastructure.rpc-service.js.map +1 -1
- package/dist/services/node-deploy-lease.d.ts +4 -1
- package/dist/services/node-deploy-lease.d.ts.map +1 -1
- package/dist/services/node-deploy-lease.js +29 -4
- package/dist/services/node-deploy-lease.js.map +1 -1
- package/dist/services/node-manager.service.d.ts +5 -25
- package/dist/services/node-manager.service.d.ts.map +1 -1
- package/dist/services/node-manager.service.js +150 -139
- package/dist/services/node-manager.service.js.map +1 -1
- package/dist/services/project.service.d.ts +6 -0
- package/dist/services/project.service.d.ts.map +1 -1
- package/dist/services/project.service.js +97 -30
- package/dist/services/project.service.js.map +1 -1
- package/dist/services/release.rpc-service.d.ts +2 -1
- package/dist/services/release.rpc-service.d.ts.map +1 -1
- package/dist/services/release.rpc-service.js +19 -3
- package/dist/services/release.rpc-service.js.map +1 -1
- package/dist/services/release.service.d.ts +24 -11
- package/dist/services/release.service.d.ts.map +1 -1
- package/dist/services/release.service.js +162 -39
- package/dist/services/release.service.js.map +1 -1
- package/dist/services/remote-deployer.service.d.ts +1 -1
- package/dist/services/remote-deployer.service.d.ts.map +1 -1
- package/dist/services/remote-deployer.service.js.map +1 -1
- package/dist/services/secrets.service.d.ts +6 -0
- package/dist/services/secrets.service.d.ts.map +1 -1
- package/dist/services/secrets.service.js +95 -51
- package/dist/services/secrets.service.js.map +1 -1
- package/dist/shared/dto/nodes.d.ts +2 -0
- package/dist/shared/dto/nodes.d.ts.map +1 -1
- package/dist/shared/dto/project.d.ts +1 -1
- package/dist/shared/dto/project.d.ts.map +1 -1
- package/dist/shared/dto/releases.d.ts +8 -1
- package/dist/shared/dto/releases.d.ts.map +1 -1
- package/dist/shared/dto/services.d.ts +18 -1
- package/dist/shared/dto/services.d.ts.map +1 -1
- package/dist/shared/dto/services.js.map +1 -1
- package/dist/shared/release-reading.d.ts +6 -0
- package/dist/shared/release-reading.d.ts.map +1 -1
- package/dist/shared/release-reading.js +42 -0
- package/dist/shared/release-reading.js.map +1 -1
- package/package.json +19 -16
- package/webapp/dist/BUILD.json +2 -2
- package/webapp/dist/assets/{access-48hkC2-P.js → access-BvKK9dEv.js} +1 -1
- package/webapp/dist/assets/{access-jWkHxGc7.js → access-DUjabwNX.js} +1 -1
- package/webapp/dist/assets/{alerts-CuPQ9YQJ.js → alerts-CzN6OqX5.js} +1 -1
- package/webapp/dist/assets/{apps-Drio028B.js → apps-G_Lm6m4F.js} +1 -1
- package/webapp/dist/assets/{audit-npSFJECe.js → audit-CLMpdBzG.js} +1 -1
- package/webapp/dist/assets/{containers-Bl8gAyZX.js → containers-BsFwprq4.js} +1 -1
- package/webapp/dist/assets/{dashboard-UHlet3H9.js → dashboard-BN7rKE5n.js} +1 -1
- package/webapp/dist/assets/{dashboard-builder-67PZFqhE.js → dashboard-builder-BXFxWjXP.js} +1 -1
- package/webapp/dist/assets/deploy-progress-CUUwlYtQ.js +1 -0
- package/webapp/dist/assets/deploy-release-dialog-58lGZDNk.js +1 -0
- package/webapp/dist/assets/deployments-DEzjHerg.js +1 -0
- package/webapp/dist/assets/detail-CBn9pjJU.js +2 -0
- package/webapp/dist/assets/{detail-CYS81K6Y.js → detail-Dj3gxaH0.js} +1 -1
- package/webapp/dist/assets/{detail-B4TiBfxU.js → detail-pXPk4O0E.js} +1 -1
- package/webapp/dist/assets/index-Bo9An3pJ.js +1 -0
- package/webapp/dist/assets/{logs-BqHOufk8.js → logs-DrsBbLfb.js} +1 -1
- package/webapp/dist/assets/{metrics-QIQaF7iS.js → metrics-D5cDZTEo.js} +1 -1
- package/webapp/dist/assets/{mui-y44PQOfG.js → mui-59N0OVRa.js} +1 -1
- package/webapp/dist/assets/{mui-C_hromOc.js → mui-D5m7fDCh.js} +1 -1
- package/webapp/dist/assets/{mui-BIz4LSVs.js → mui-Db5J-QUd.js} +1 -1
- package/webapp/dist/assets/{mui-B1jrje6Z.js → mui-dLCwWoMw.js} +1 -1
- package/webapp/dist/assets/{nodes-BJPk02vv.js → nodes-CwwZZuu4.js} +1 -1
- package/webapp/dist/assets/{pipelines-T6Ch9LTw.js → pipelines-BrfTjf6Y.js} +1 -1
- package/webapp/dist/assets/project.store-DuCgv6Eq.js +1 -0
- package/webapp/dist/assets/{projects-YoBzcQ24.js → projects-TBAdpCeX.js} +1 -1
- package/webapp/dist/assets/{realtime.store-CBDO4Abj.js → realtime.store-hfxZD2DU.js} +1 -1
- package/webapp/dist/assets/release-bits-BJ6i23y0.js +1 -0
- package/webapp/dist/assets/release-build-panel-BNXkwNE1.js +1 -0
- package/webapp/dist/assets/release-deployment-state-gN8ZT3aq.js +1 -0
- package/webapp/dist/assets/releases-CXXKUh-v.js +2 -0
- package/webapp/dist/assets/{stack-start-notice-BVnMOwRQ.js → stack-start-notice-D-AH_bzn.js} +1 -1
- package/webapp/dist/assets/{stacks-IZ1mfizi.js → stacks-WY_ynkON.js} +1 -1
- package/webapp/dist/assets/{system-info-BFKNM-hf.js → system-info-DXMwo6gl.js} +1 -1
- package/webapp/dist/assets/{topology-CPey5dP6.js → topology-C_Qp1RqW.js} +1 -1
- package/webapp/dist/assets/{use-stack-context-UkBsF-l7.js → use-stack-context-8yaHaN8L.js} +1 -1
- package/webapp/dist/assets/{webapp-D-UhjFhS.js → webapp-BO_WoB_z.js} +3 -3
- package/webapp/dist/console-version.json +1 -1
- package/webapp/dist/index.html +12 -12
- package/webapp/dist/assets/deploy-release-dialog-BOXcD5Wo.js +0 -1
- package/webapp/dist/assets/deployments-qV20Ak0P.js +0 -1
- package/webapp/dist/assets/detail-B85-Wees.js +0 -2
- package/webapp/dist/assets/index-uq8Dkk4c.js +0 -1
- package/webapp/dist/assets/project.store--G5pIF-J.js +0 -1
- package/webapp/dist/assets/release-bits-B2a_wbft.js +0 -1
- package/webapp/dist/assets/release-build-panel-BQmIdKW9.js +0 -1
- package/webapp/dist/assets/release-deployment-state-DRrEI2pP.js +0 -1
- package/webapp/dist/assets/release-wire-f2VuBoBF.js +0 -1
- package/webapp/dist/assets/releases-Bp3CDgmo.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-manager.service.d.ts","sourceRoot":"","sources":["../../src/services/node-manager.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAEL,KAAK,eAAe,EAIrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAElG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EACV,KAAK,EACL,WAAW,EACX,eAAe,EACf,YAAY,EACZ,eAAe,EACf,UAAU,EACV,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAMhC,YAAY,EACV,KAAK,EACL,WAAW,EACX,eAAe,EACf,YAAY,EACZ,eAAe,EACf,UAAU,EACV,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAEhC,iEAAiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;IACzB,cAAc,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACzC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/G;
|
|
1
|
+
{"version":3,"file":"node-manager.service.d.ts","sourceRoot":"","sources":["../../src/services/node-manager.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,6 +24,8 @@ const NODES_FILE = path.join(OMNITRON_HOME, 'nodes.json');
|
|
|
24
24
|
const LOCAL_NODE_ID = 'local';
|
|
25
25
|
/** DaemonStateStore key holding the operator-set check configuration. */
|
|
26
26
|
const CHECK_CONFIG_KEY = 'nodes:check-config';
|
|
27
|
+
const LEGACY_IMPORT_KEY = 'nodes:legacy-import-in-progress';
|
|
28
|
+
const NODE_SECRET_FIELDS = ['privateKey', 'passphrase', 'password'];
|
|
27
29
|
// =============================================================================
|
|
28
30
|
// Service
|
|
29
31
|
// =============================================================================
|
|
@@ -54,6 +56,7 @@ export class NodeManagerService extends EventEmitter {
|
|
|
54
56
|
* report a perfectly reachable node as unreachable.
|
|
55
57
|
*/
|
|
56
58
|
pendingSecrets = new Map();
|
|
59
|
+
repairing = new Map();
|
|
57
60
|
/** The master's mesh — see `setSlaveConnector`. */
|
|
58
61
|
mesh = null;
|
|
59
62
|
constructor(logger,
|
|
@@ -236,7 +239,6 @@ export class NodeManagerService extends EventEmitter {
|
|
|
236
239
|
sshPort: assertPort('SSH port', input.sshPort ?? 22),
|
|
237
240
|
sshUser: input.sshUser ?? 'root',
|
|
238
241
|
sshAuthMethod: input.sshAuthMethod ?? 'key',
|
|
239
|
-
...(input.sshPrivateKey && { sshPrivateKey: input.sshPrivateKey }),
|
|
240
242
|
runtime: input.runtime ?? 'node',
|
|
241
243
|
daemonPort: assertPort('daemon port', input.daemonPort ?? 9700),
|
|
242
244
|
tags: input.tags ?? [],
|
|
@@ -248,8 +250,8 @@ export class NodeManagerService extends EventEmitter {
|
|
|
248
250
|
if (!node.name.trim())
|
|
249
251
|
throw new Error('Node name is required');
|
|
250
252
|
this.assertAddressIsFree(node.host, node.daemonPort);
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
if (input.sshPrivateKey)
|
|
254
|
+
await this.assignPrivateKey(node, input.sshPrivateKey);
|
|
253
255
|
if (input.sshPassphrase) {
|
|
254
256
|
node.hasPassphrase = true;
|
|
255
257
|
await this.setSecret(id, 'passphrase', input.sshPassphrase);
|
|
@@ -258,8 +260,9 @@ export class NodeManagerService extends EventEmitter {
|
|
|
258
260
|
node.hasPassword = true;
|
|
259
261
|
await this.setSecret(id, 'password', input.sshPassword);
|
|
260
262
|
}
|
|
263
|
+
this.assertAddressIsFree(node.host, node.daemonPort);
|
|
264
|
+
this.persistNode(node, true);
|
|
261
265
|
this.nodes.set(id, node);
|
|
262
|
-
this.save();
|
|
263
266
|
this.emit('node:added', node);
|
|
264
267
|
this.logger.info({ nodeId: id, name: node.name, host: node.host }, 'Node added');
|
|
265
268
|
// Check connectivity in background — secrets are already persisted.
|
|
@@ -280,6 +283,8 @@ export class NodeManagerService extends EventEmitter {
|
|
|
280
283
|
throw new Error('Node registration is changing; retry after the current operation');
|
|
281
284
|
this.changingNodes.add(id);
|
|
282
285
|
try {
|
|
286
|
+
if (this.hasPendingCredentials(id))
|
|
287
|
+
await this.repairCredentials(previous);
|
|
283
288
|
return await this.writeNodeUpdate(previous, input);
|
|
284
289
|
}
|
|
285
290
|
finally {
|
|
@@ -298,22 +303,28 @@ export class NodeManagerService extends EventEmitter {
|
|
|
298
303
|
const id = node.id;
|
|
299
304
|
if (node.isLocal && input.host)
|
|
300
305
|
throw new Error('Cannot change host of local node');
|
|
301
|
-
|
|
302
|
-
const {
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
if (
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
306
|
+
const { sshPassphrase, sshPassword, sshPrivateKey } = input;
|
|
307
|
+
const updated = { ...node, updatedAt: new Date().toISOString() };
|
|
308
|
+
if (input.name !== undefined)
|
|
309
|
+
updated.name = input.name;
|
|
310
|
+
if (input.host !== undefined)
|
|
311
|
+
updated.host = assertNodeHost(input.host);
|
|
312
|
+
if (input.sshPort !== undefined)
|
|
313
|
+
updated.sshPort = assertPort('SSH port', input.sshPort);
|
|
314
|
+
if (input.daemonPort !== undefined)
|
|
315
|
+
updated.daemonPort = assertPort('daemon port', input.daemonPort);
|
|
316
|
+
if (input.sshUser !== undefined)
|
|
317
|
+
updated.sshUser = input.sshUser;
|
|
318
|
+
if (input.sshAuthMethod !== undefined)
|
|
319
|
+
updated.sshAuthMethod = input.sshAuthMethod;
|
|
320
|
+
if (input.runtime !== undefined)
|
|
321
|
+
updated.runtime = input.runtime;
|
|
322
|
+
if (input.tags !== undefined)
|
|
323
|
+
updated.tags = input.tags;
|
|
324
|
+
if (input.offlineTimeout !== undefined)
|
|
325
|
+
updated.offlineTimeout = input.offlineTimeout;
|
|
326
|
+
if (!updated.name.trim())
|
|
327
|
+
throw new Error('Node name is required');
|
|
317
328
|
// An edit can collide as readily as an add — correcting one node's host
|
|
318
329
|
// to the address another already holds is exactly how a duplicate gets
|
|
319
330
|
// made. Checked before any secret is written, so a refused edit leaves
|
|
@@ -337,17 +348,22 @@ export class NodeManagerService extends EventEmitter {
|
|
|
337
348
|
const passwordCleared = updated.sshAuthMethod !== node.sshAuthMethod && updated.sshAuthMethod === 'key';
|
|
338
349
|
const keyCleared = updated.sshAuthMethod !== node.sshAuthMethod && updated.sshAuthMethod === 'password';
|
|
339
350
|
if ((!passwordCleared && node.hasPassword && sshPassword === undefined)
|
|
340
|
-
|| (!keyCleared && (node.sshPrivateKey &&
|
|
351
|
+
|| (!keyCleared && ((node.sshPrivateKey || node.hasPrivateKey) && sshPrivateKey === undefined
|
|
341
352
|
|| node.hasPassphrase && sshPassphrase === undefined)))
|
|
342
353
|
throw new Error('SSH destination changed. Explicitly supply or clear the SSH credential for the new host, port and account.');
|
|
343
354
|
// Commit the replacement address without credentials before changing its ID-keyed vault slots.
|
|
344
355
|
// A crash between these stores leaves a node that needs credentials. Xec requires explicit auth.
|
|
345
|
-
const detached = { ...updated, hasPassword: false, hasPassphrase: false };
|
|
356
|
+
const detached = { ...updated, hasPassword: false, hasPassphrase: false, hasPrivateKey: false };
|
|
346
357
|
delete detached.sshPrivateKey;
|
|
347
358
|
this.persistNode(detached, true);
|
|
348
359
|
this.nodes.set(id, detached);
|
|
349
360
|
}
|
|
350
|
-
|
|
361
|
+
if (updated.sshAuthMethod === 'password' && node.sshAuthMethod !== updated.sshAuthMethod) {
|
|
362
|
+
await this.assignPrivateKey(updated, '');
|
|
363
|
+
}
|
|
364
|
+
else if (sshPrivateKey !== undefined) {
|
|
365
|
+
await this.assignPrivateKey(updated, sshPrivateKey);
|
|
366
|
+
}
|
|
351
367
|
if (sshPassphrase !== undefined) {
|
|
352
368
|
if (sshPassphrase) {
|
|
353
369
|
updated.hasPassphrase = true;
|
|
@@ -378,7 +394,6 @@ export class NodeManagerService extends EventEmitter {
|
|
|
378
394
|
await this.deleteSecret(id, 'password');
|
|
379
395
|
}
|
|
380
396
|
if (updated.sshAuthMethod === 'password') {
|
|
381
|
-
delete updated.sshPrivateKey;
|
|
382
397
|
if (updated.hasPassphrase) {
|
|
383
398
|
updated.hasPassphrase = false;
|
|
384
399
|
await this.deleteSecret(id, 'passphrase');
|
|
@@ -412,6 +427,8 @@ export class NodeManagerService extends EventEmitter {
|
|
|
412
427
|
throw new Error('Node registration is changing; retry after the current operation');
|
|
413
428
|
this.changingNodes.add(id);
|
|
414
429
|
try {
|
|
430
|
+
if (this.hasPendingCredentials(id))
|
|
431
|
+
await this.repairCredentials(node);
|
|
415
432
|
await this.deleteNodeRegistration(node);
|
|
416
433
|
}
|
|
417
434
|
finally {
|
|
@@ -420,11 +437,10 @@ export class NodeManagerService extends EventEmitter {
|
|
|
420
437
|
}
|
|
421
438
|
async deleteNodeRegistration(node) {
|
|
422
439
|
const id = node.id;
|
|
440
|
+
if (node.hasPrivateKey)
|
|
441
|
+
await this.deleteSecret(id, 'privateKey');
|
|
423
442
|
await this.deleteSecret(id, 'passphrase');
|
|
424
443
|
await this.deleteSecret(id, 'password');
|
|
425
|
-
this.nodes.delete(id);
|
|
426
|
-
this.statusCache.delete(id);
|
|
427
|
-
this.summaryCache.delete(id);
|
|
428
444
|
try {
|
|
429
445
|
this.store.deleteNodeSync(id);
|
|
430
446
|
}
|
|
@@ -432,7 +448,9 @@ export class NodeManagerService extends EventEmitter {
|
|
|
432
448
|
this.logger.warn({ nodeId: id, error: err.message }, 'Failed to delete node row');
|
|
433
449
|
throw err;
|
|
434
450
|
}
|
|
435
|
-
this.
|
|
451
|
+
this.nodes.delete(id);
|
|
452
|
+
this.statusCache.delete(id);
|
|
453
|
+
this.summaryCache.delete(id);
|
|
436
454
|
this.emit('node:removed', id);
|
|
437
455
|
this.logger.info({ nodeId: id, name: node.name }, 'Node removed');
|
|
438
456
|
}
|
|
@@ -641,18 +659,26 @@ export class NodeManagerService extends EventEmitter {
|
|
|
641
659
|
port: node.sshPort,
|
|
642
660
|
username: node.sshUser,
|
|
643
661
|
};
|
|
644
|
-
if (node.
|
|
662
|
+
if (node.hasPrivateKey) {
|
|
663
|
+
const key = await this.getSecret(node.id, 'privateKey');
|
|
664
|
+
if (!key)
|
|
665
|
+
throw new Error('Stored SSH private key is unavailable');
|
|
666
|
+
target.privateKey = key;
|
|
667
|
+
}
|
|
668
|
+
else if (node.sshPrivateKey) {
|
|
645
669
|
target.privateKey = readKeyFile(node.sshPrivateKey);
|
|
646
670
|
}
|
|
647
671
|
if (node.hasPassphrase) {
|
|
648
672
|
const passphrase = await this.getSecret(node.id, 'passphrase');
|
|
649
|
-
if (passphrase)
|
|
650
|
-
|
|
673
|
+
if (!passphrase)
|
|
674
|
+
throw new Error('Stored SSH key passphrase is unavailable');
|
|
675
|
+
target.passphrase = passphrase;
|
|
651
676
|
}
|
|
652
677
|
if (node.hasPassword) {
|
|
653
678
|
const password = await this.getSecret(node.id, 'password');
|
|
654
|
-
if (password)
|
|
655
|
-
|
|
679
|
+
if (!password)
|
|
680
|
+
throw new Error('Stored SSH password is unavailable');
|
|
681
|
+
target.password = password;
|
|
656
682
|
}
|
|
657
683
|
if (this.nodes.get(node.id) !== registered || this.changingNodes.has(node.id))
|
|
658
684
|
throw new Error('SSH node registration changed during credential lookup');
|
|
@@ -853,20 +879,24 @@ export class NodeManagerService extends EventEmitter {
|
|
|
853
879
|
// ===========================================================================
|
|
854
880
|
// Secrets Helpers
|
|
855
881
|
// ===========================================================================
|
|
882
|
+
async assignPrivateKey(node, input) {
|
|
883
|
+
if (isKeyMaterial(input)) {
|
|
884
|
+
await this.setSecret(node.id, 'privateKey', input);
|
|
885
|
+
node.hasPrivateKey = true;
|
|
886
|
+
delete node.sshPrivateKey;
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
if (node.hasPrivateKey)
|
|
890
|
+
await this.deleteSecret(node.id, 'privateKey');
|
|
891
|
+
delete node.hasPrivateKey;
|
|
892
|
+
if (input)
|
|
893
|
+
node.sshPrivateKey = input;
|
|
894
|
+
else
|
|
895
|
+
delete node.sshPrivateKey;
|
|
896
|
+
}
|
|
856
897
|
secretKey(nodeId, field) {
|
|
857
898
|
return `node:${nodeId}:${field}`;
|
|
858
899
|
}
|
|
859
|
-
/** The vault's own answer, with no in-memory fallback. */
|
|
860
|
-
async readVault(nodeId, field) {
|
|
861
|
-
if (!this.secrets)
|
|
862
|
-
return null;
|
|
863
|
-
try {
|
|
864
|
-
return await this.secrets.get(this.secretKey(nodeId, field));
|
|
865
|
-
}
|
|
866
|
-
catch {
|
|
867
|
-
return null;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
900
|
async getSecret(nodeId, field) {
|
|
871
901
|
const key = this.secretKey(nodeId, field);
|
|
872
902
|
if (this.secrets) {
|
|
@@ -881,18 +911,14 @@ export class NodeManagerService extends EventEmitter {
|
|
|
881
911
|
}
|
|
882
912
|
async setSecret(nodeId, field, value) {
|
|
883
913
|
if (!this.secrets) {
|
|
884
|
-
|
|
885
|
-
return;
|
|
914
|
+
throw new Error('SSH credential vault is unavailable');
|
|
886
915
|
}
|
|
887
916
|
await this.secrets.set(this.secretKey(nodeId, field), value);
|
|
888
917
|
}
|
|
889
918
|
async deleteSecret(nodeId, field) {
|
|
890
919
|
if (!this.secrets)
|
|
891
920
|
return;
|
|
892
|
-
|
|
893
|
-
await this.secrets.delete(this.secretKey(nodeId, field));
|
|
894
|
-
}
|
|
895
|
-
catch { /* non-critical */ }
|
|
921
|
+
await this.secrets.delete(this.secretKey(nodeId, field));
|
|
896
922
|
}
|
|
897
923
|
/**
|
|
898
924
|
* Status of the machine this daemon runs on.
|
|
@@ -1006,8 +1032,13 @@ export class NodeManagerService extends EventEmitter {
|
|
|
1006
1032
|
// Persistence
|
|
1007
1033
|
// ===========================================================================
|
|
1008
1034
|
load() {
|
|
1009
|
-
|
|
1010
|
-
|
|
1035
|
+
const remember = (raw) => {
|
|
1036
|
+
const { node, strays } = splitStoredSecrets(raw);
|
|
1037
|
+
this.nodes.set(node.id, node);
|
|
1038
|
+
for (const { field, value } of strays)
|
|
1039
|
+
this.pendingSecrets.set(this.secretKey(node.id, field), value);
|
|
1040
|
+
return node;
|
|
1041
|
+
};
|
|
1011
1042
|
try {
|
|
1012
1043
|
// First-pass: read from SQLite. Each row's `metadata` column
|
|
1013
1044
|
// holds the full INode JSON; the denormalised columns are for
|
|
@@ -1017,14 +1048,7 @@ export class NodeManagerService extends EventEmitter {
|
|
|
1017
1048
|
if (!row.metadata)
|
|
1018
1049
|
continue;
|
|
1019
1050
|
try {
|
|
1020
|
-
const
|
|
1021
|
-
const { node, strays } = splitStoredSecrets(raw);
|
|
1022
|
-
this.nodes.set(node.id, node);
|
|
1023
|
-
if (strays.length > 0) {
|
|
1024
|
-
for (const stray of strays)
|
|
1025
|
-
this.pendingSecrets.set(this.secretKey(node.id, stray.field), stray.value);
|
|
1026
|
-
leaked.push({ node, strays });
|
|
1027
|
-
}
|
|
1051
|
+
const node = remember(JSON.parse(row.metadata));
|
|
1028
1052
|
// Restore the last known health, but NOT a status reading: a
|
|
1029
1053
|
// reachability answer from a previous daemon is not a current one,
|
|
1030
1054
|
// and the console has no way to tell them apart on the dots. What
|
|
@@ -1044,77 +1068,65 @@ export class NodeManagerService extends EventEmitter {
|
|
|
1044
1068
|
// Corrupt row — skip and let the next mutation overwrite.
|
|
1045
1069
|
}
|
|
1046
1070
|
}
|
|
1047
|
-
//
|
|
1048
|
-
//
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
fs.unlinkSync(NODES_FILE);
|
|
1062
|
-
}
|
|
1063
|
-
catch { /* best-effort */ }
|
|
1064
|
-
}
|
|
1065
|
-
catch (err) {
|
|
1066
|
-
this.logger.warn({ error: err.message }, 'Legacy nodes.json migration failed — leaving file in place');
|
|
1071
|
+
// Keep the original file until every imported credential and row is durable.
|
|
1072
|
+
// The marker lets an interrupted import finish after some rows already exist.
|
|
1073
|
+
if ((this.nodes.size === 0 || this.store.kvGetSync(LEGACY_IMPORT_KEY)) && fs.existsSync(NODES_FILE)) {
|
|
1074
|
+
const original = fs.readFileSync(NODES_FILE, 'utf8');
|
|
1075
|
+
const { nodes } = JSON.parse(original);
|
|
1076
|
+
if (!Array.isArray(nodes))
|
|
1077
|
+
throw new Error('Invalid legacy node registry');
|
|
1078
|
+
this.store.kvSetSync(LEGACY_IMPORT_KEY, true);
|
|
1079
|
+
for (const raw of nodes) {
|
|
1080
|
+
if (this.nodes.has(raw.id))
|
|
1081
|
+
continue;
|
|
1082
|
+
const node = remember(raw);
|
|
1083
|
+
if (!this.hasPendingCredentials(node.id))
|
|
1084
|
+
this.persistNode(node, true);
|
|
1067
1085
|
}
|
|
1086
|
+
// Existing SQLite rows may also still need credential migration.
|
|
1087
|
+
const imported = [...this.nodes.values()].map(node => this.repairCredentials(node));
|
|
1088
|
+
void Promise.all(imported).then(() => {
|
|
1089
|
+
if (fs.readFileSync(NODES_FILE, 'utf8') !== original)
|
|
1090
|
+
throw new Error('Legacy node registry changed during import');
|
|
1091
|
+
fs.unlinkSync(NODES_FILE);
|
|
1092
|
+
this.store.kvSetSync(LEGACY_IMPORT_KEY, false);
|
|
1093
|
+
}).catch(() => this.logger.error('Legacy node registry retained; retry credential migration after checking the vault and database'));
|
|
1068
1094
|
}
|
|
1069
1095
|
}
|
|
1070
1096
|
catch (err) {
|
|
1071
1097
|
this.logger.warn({ error: err.message }, 'Failed to load nodes from store');
|
|
1072
1098
|
}
|
|
1073
|
-
|
|
1074
|
-
void this.
|
|
1099
|
+
for (const node of this.nodes.values()) {
|
|
1100
|
+
void this.repairCredentials(node).catch(() => this.logger.error({ nodeId: node.id }, 'SSH credential migration incomplete; source retained for retry'));
|
|
1101
|
+
}
|
|
1075
1102
|
this.loadCheckConfig();
|
|
1076
1103
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
// it whether the secret is stored answers "yes" about the copy we are
|
|
1100
|
-
// trying to store, and the write never happens.
|
|
1101
|
-
const existing = await this.readVault(node.id, field);
|
|
1102
|
-
if (!existing) {
|
|
1103
|
-
if (!this.secrets) {
|
|
1104
|
-
// Nothing to migrate into. The row keeps the value for now — the
|
|
1105
|
-
// node would otherwise stop authenticating — but it no longer
|
|
1106
|
-
// reaches the wire, which is the part that matters.
|
|
1107
|
-
this.logger.error({ nodeId: node.id, field }, 'No secret store available — plaintext secret left in the node row');
|
|
1108
|
-
continue;
|
|
1109
|
-
}
|
|
1110
|
-
await this.setSecret(node.id, field, value);
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
// Rewrite the row from the sanitised node.
|
|
1114
|
-
this.persistNode(node);
|
|
1115
|
-
for (const { field } of strays)
|
|
1116
|
-
this.pendingSecrets.delete(this.secretKey(node.id, field));
|
|
1104
|
+
hasPendingCredentials(id) {
|
|
1105
|
+
return NODE_SECRET_FIELDS.some(field => this.pendingSecrets.has(this.secretKey(id, field)));
|
|
1106
|
+
}
|
|
1107
|
+
repairCredentials(node) {
|
|
1108
|
+
const active = this.repairing.get(node.id);
|
|
1109
|
+
if (active)
|
|
1110
|
+
return active;
|
|
1111
|
+
if (!this.hasPendingCredentials(node.id))
|
|
1112
|
+
return Promise.resolve();
|
|
1113
|
+
const repaired = this.storePendingCredentials(node).finally(() => this.repairing.delete(node.id));
|
|
1114
|
+
this.repairing.set(node.id, repaired);
|
|
1115
|
+
return repaired;
|
|
1116
|
+
}
|
|
1117
|
+
async storePendingCredentials(node) {
|
|
1118
|
+
if (!this.secrets)
|
|
1119
|
+
throw new Error('SSH credential vault is unavailable');
|
|
1120
|
+
for (const field of NODE_SECRET_FIELDS) {
|
|
1121
|
+
const key = this.secretKey(node.id, field);
|
|
1122
|
+
const pending = this.pendingSecrets.get(key);
|
|
1123
|
+
if (pending === undefined)
|
|
1124
|
+
continue;
|
|
1125
|
+
await this.secrets.setIfAbsent(key, pending);
|
|
1117
1126
|
}
|
|
1127
|
+
this.persistNode(node, true);
|
|
1128
|
+
for (const field of NODE_SECRET_FIELDS)
|
|
1129
|
+
this.pendingSecrets.delete(this.secretKey(node.id, field));
|
|
1118
1130
|
}
|
|
1119
1131
|
/** Restore the operator's check configuration, if one was ever set. */
|
|
1120
1132
|
loadCheckConfig() {
|
|
@@ -1156,21 +1168,11 @@ export class NodeManagerService extends EventEmitter {
|
|
|
1156
1168
|
throw err;
|
|
1157
1169
|
}
|
|
1158
1170
|
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Persist every node currently held in memory. Used after a
|
|
1161
|
-
* batch mutation that affected multiple rows (the previous
|
|
1162
|
-
* fs.writeFileSync semantics).
|
|
1163
|
-
*/
|
|
1164
|
-
save() {
|
|
1165
|
-
for (const node of this.nodes.values()) {
|
|
1166
|
-
this.persistNode(node);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
1171
|
ensureLocalNode() {
|
|
1170
1172
|
if (this.nodes.has(LOCAL_NODE_ID))
|
|
1171
1173
|
return;
|
|
1172
1174
|
const now = new Date().toISOString();
|
|
1173
|
-
|
|
1175
|
+
const local = {
|
|
1174
1176
|
id: LOCAL_NODE_ID,
|
|
1175
1177
|
name: 'Local Machine',
|
|
1176
1178
|
host: '127.0.0.1',
|
|
@@ -1183,8 +1185,9 @@ export class NodeManagerService extends EventEmitter {
|
|
|
1183
1185
|
isLocal: true,
|
|
1184
1186
|
createdAt: now,
|
|
1185
1187
|
updatedAt: now,
|
|
1186
|
-
}
|
|
1187
|
-
this.
|
|
1188
|
+
};
|
|
1189
|
+
this.nodes.set(LOCAL_NODE_ID, local);
|
|
1190
|
+
this.persistNode(local);
|
|
1188
1191
|
}
|
|
1189
1192
|
async dispose() {
|
|
1190
1193
|
await this.remoteOps.dispose();
|
|
@@ -1217,10 +1220,10 @@ function toWireNode(node) {
|
|
|
1217
1220
|
createdAt: node.createdAt,
|
|
1218
1221
|
updatedAt: node.updatedAt,
|
|
1219
1222
|
};
|
|
1220
|
-
|
|
1221
|
-
// only whether a secret exists. Neither is a secret itself.
|
|
1222
|
-
if (node.sshPrivateKey !== undefined)
|
|
1223
|
+
if (node.sshPrivateKey && !isKeyMaterial(node.sshPrivateKey))
|
|
1223
1224
|
wire.sshPrivateKey = node.sshPrivateKey;
|
|
1225
|
+
if (node.hasPrivateKey)
|
|
1226
|
+
wire.hasPrivateKey = true;
|
|
1224
1227
|
if (node.hasPassphrase !== undefined)
|
|
1225
1228
|
wire.hasPassphrase = node.hasPassphrase;
|
|
1226
1229
|
if (node.hasPassword !== undefined)
|
|
@@ -1234,6 +1237,11 @@ function splitStoredSecrets(raw) {
|
|
|
1234
1237
|
const { sshPassphrase, sshPassword, ...rest } = raw;
|
|
1235
1238
|
const node = rest;
|
|
1236
1239
|
const strays = [];
|
|
1240
|
+
if (node.sshPrivateKey && isKeyMaterial(node.sshPrivateKey)) {
|
|
1241
|
+
strays.push({ field: 'privateKey', value: node.sshPrivateKey });
|
|
1242
|
+
delete node.sshPrivateKey;
|
|
1243
|
+
node.hasPrivateKey = true;
|
|
1244
|
+
}
|
|
1237
1245
|
if (typeof sshPassphrase === 'string' && sshPassphrase) {
|
|
1238
1246
|
node.hasPassphrase = true;
|
|
1239
1247
|
strays.push({ field: 'passphrase', value: sshPassphrase });
|
|
@@ -1242,7 +1250,10 @@ function splitStoredSecrets(raw) {
|
|
|
1242
1250
|
node.hasPassword = true;
|
|
1243
1251
|
strays.push({ field: 'password', value: sshPassword });
|
|
1244
1252
|
}
|
|
1245
|
-
return { node, strays };
|
|
1253
|
+
return { node: toWireNode(node), strays };
|
|
1254
|
+
}
|
|
1255
|
+
function isKeyMaterial(value) {
|
|
1256
|
+
return /[\r\n\0]|^\s*(?:-{4,}|PuTTY-User-Key-File-\d+:)/i.test(value);
|
|
1246
1257
|
}
|
|
1247
1258
|
/** Fallback offline timeout, matching the health monitor's own default. */
|
|
1248
1259
|
const DEFAULT_OFFLINE_TIMEOUT_MS = 90_000;
|