instar 1.3.896 → 1.3.897
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/dashboard/index.html +30 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +111 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +12 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CoherenceJournal.d.ts +17 -1
- package/dist/core/CoherenceJournal.d.ts.map +1 -1
- package/dist/core/CoherenceJournal.js +29 -2
- package/dist/core/CoherenceJournal.js.map +1 -1
- package/dist/core/JournalSyncApplier.d.ts.map +1 -1
- package/dist/core/JournalSyncApplier.js +11 -0
- package/dist/core/JournalSyncApplier.js.map +1 -1
- package/dist/core/MachineSshEndpoint.d.ts +47 -0
- package/dist/core/MachineSshEndpoint.d.ts.map +1 -0
- package/dist/core/MachineSshEndpoint.js +221 -0
- package/dist/core/MachineSshEndpoint.js.map +1 -0
- package/dist/core/MachineSshIdentity.d.ts +26 -0
- package/dist/core/MachineSshIdentity.d.ts.map +1 -0
- package/dist/core/MachineSshIdentity.js +97 -0
- package/dist/core/MachineSshIdentity.js.map +1 -0
- package/dist/core/MeshRpc.d.ts +6 -0
- package/dist/core/MeshRpc.d.ts.map +1 -1
- package/dist/core/MeshRpc.js +2 -0
- package/dist/core/MeshRpc.js.map +1 -1
- package/dist/core/MutualSshHealthController.d.ts +33 -0
- package/dist/core/MutualSshHealthController.d.ts.map +1 -0
- package/dist/core/MutualSshHealthController.js +113 -0
- package/dist/core/MutualSshHealthController.js.map +1 -0
- package/dist/core/MutualSshProbeScheduler.d.ts +21 -0
- package/dist/core/MutualSshProbeScheduler.d.ts.map +1 -0
- package/dist/core/MutualSshProbeScheduler.js +57 -0
- package/dist/core/MutualSshProbeScheduler.js.map +1 -0
- package/dist/core/MutualSshRuntime.d.ts +106 -0
- package/dist/core/MutualSshRuntime.d.ts.map +1 -0
- package/dist/core/MutualSshRuntime.js +423 -0
- package/dist/core/MutualSshRuntime.js.map +1 -0
- package/dist/core/MutualSshVerifier.d.ts +44 -0
- package/dist/core/MutualSshVerifier.d.ts.map +1 -0
- package/dist/core/MutualSshVerifier.js +97 -0
- package/dist/core/MutualSshVerifier.js.map +1 -0
- package/dist/core/SshBootstrapAdvert.d.ts +23 -0
- package/dist/core/SshBootstrapAdvert.d.ts.map +1 -0
- package/dist/core/SshBootstrapAdvert.js +43 -0
- package/dist/core/SshBootstrapAdvert.js.map +1 -0
- package/dist/core/SshHostKeyLifecycle.d.ts +43 -0
- package/dist/core/SshHostKeyLifecycle.d.ts.map +1 -0
- package/dist/core/SshHostKeyLifecycle.js +68 -0
- package/dist/core/SshHostKeyLifecycle.js.map +1 -0
- package/dist/core/SshPeerAdmissionStore.d.ts +29 -0
- package/dist/core/SshPeerAdmissionStore.d.ts.map +1 -0
- package/dist/core/SshPeerAdmissionStore.js +86 -0
- package/dist/core/SshPeerAdmissionStore.js.map +1 -0
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +6 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/machineCoherenceManifest.d.ts.map +1 -1
- package/dist/core/machineCoherenceManifest.js +8 -0
- package/dist/core/machineCoherenceManifest.js.map +1 -1
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +22 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.js +2 -2
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +9 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/testing/selfActionRegistry.d.ts.map +1 -1
- package/dist/testing/selfActionRegistry.js +39 -0
- package/dist/testing/selfActionRegistry.js.map +1 -1
- package/package.json +3 -1
- package/src/data/builtin-manifest.json +47 -47
- package/src/data/state-coherence-registry.json +36 -0
- package/upgrades/1.3.897.md +39 -0
- package/upgrades/side-effects/mutual-ssh-autobootstrap.md +137 -0
package/dashboard/index.html
CHANGED
|
@@ -3779,6 +3779,11 @@
|
|
|
3779
3779
|
headline + tiles + drill-down here. Rebuilt from the old insider "guards" line.
|
|
3780
3780
|
Nickname editing (issue #1429) lives one drill down, commits on Enter/blur. -->
|
|
3781
3781
|
<div id="machinesGlance" class="glance-root"></div>
|
|
3782
|
+
<section aria-labelledby="mutualSshTitle" style="border:1px solid var(--border);border-radius:10px;padding:16px">
|
|
3783
|
+
<h3 id="mutualSshTitle" style="margin:0 0 8px">Machine-to-machine SSH</h3>
|
|
3784
|
+
<p style="margin:0 0 10px;color:var(--text-dim)">Restricted Instar subsystem connectivity. This does not enable Remote Login or change personal SSH files.</p>
|
|
3785
|
+
<div id="mutualSshStatus" class="glance-empty" aria-live="polite">Loading connectivity…</div>
|
|
3786
|
+
</section>
|
|
3782
3787
|
<section aria-labelledby="throughputFloorTitle" style="border:1px solid var(--border);border-radius:10px;padding:16px">
|
|
3783
3788
|
<h3 id="throughputFloorTitle" style="margin:0 0 8px">Autonomous throughput observations</h3>
|
|
3784
3789
|
<p style="margin:0 0 10px;color:var(--text-dim)">Pull-only evidence of project PR movement and manager communication. This view never notifies or changes a run.</p>
|
|
@@ -5541,6 +5546,7 @@
|
|
|
5541
5546
|
return;
|
|
5542
5547
|
}
|
|
5543
5548
|
if (stamp) stamp.textContent = new Date().toLocaleTimeString();
|
|
5549
|
+
await loadMutualSshStatus();
|
|
5544
5550
|
await loadThroughputFloor();
|
|
5545
5551
|
|
|
5546
5552
|
if (!pool.enabled) {
|
|
@@ -5564,6 +5570,30 @@
|
|
|
5564
5570
|
glance.renderGlance(document, glanceRoot, spec);
|
|
5565
5571
|
}
|
|
5566
5572
|
|
|
5573
|
+
async function loadMutualSshStatus() {
|
|
5574
|
+
const root = document.getElementById('mutualSshStatus');
|
|
5575
|
+
if (!root) return;
|
|
5576
|
+
root.textContent = '';
|
|
5577
|
+
let status;
|
|
5578
|
+
try { status = await apiFetch('/machines/ssh-health?scope=pool'); }
|
|
5579
|
+
catch { root.textContent = 'Not enabled on this agent.'; return; }
|
|
5580
|
+
if (status.dryRun) { root.textContent = 'Observation only — no peer SSH sessions are accepted.'; return; }
|
|
5581
|
+
const pairs = Array.isArray(status.pairs) ? status.pairs : [];
|
|
5582
|
+
if (pairs.length === 0) { root.textContent = status.listener ? 'Ready; no paired machines to verify.' : 'Waiting for the restricted listener.'; return; }
|
|
5583
|
+
const healthy = pairs.filter(pair => pair && pair.mutual === true).length;
|
|
5584
|
+
const summary = document.createElement('div');
|
|
5585
|
+
summary.textContent = healthy === pairs.length
|
|
5586
|
+
? 'Mutual proof is current for all ' + String(pairs.length) + ' machine pair' + (pairs.length === 1 ? '.' : 's.')
|
|
5587
|
+
: String(healthy) + ' of ' + String(pairs.length) + ' machine pairs have current proof in both directions.';
|
|
5588
|
+
root.appendChild(summary);
|
|
5589
|
+
for (const pair of pairs) {
|
|
5590
|
+
const row = document.createElement('div');
|
|
5591
|
+
row.style.marginTop = '8px';
|
|
5592
|
+
row.textContent = (pair.mutual ? 'Verified: ' : 'Blocked: ') + (Array.isArray(pair.machines) ? pair.machines.join(' ↔ ') : 'machine pair');
|
|
5593
|
+
root.appendChild(row);
|
|
5594
|
+
}
|
|
5595
|
+
}
|
|
5596
|
+
|
|
5567
5597
|
async function loadThroughputFloor() {
|
|
5568
5598
|
const root = document.getElementById('throughputFloorStatus');
|
|
5569
5599
|
if (!root) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2EH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AA0BjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA8H7D,OAAO,KAAK,EAAW,oBAAoB,EAAe,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAgFtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA8pDD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,oBAAoB,GAAG,IAAI,GACxC;IACD,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAqBA;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,EAQrD,oBAAoB,CAAC,EAAE,MAAM;IAC3B,YAAY,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,GACP,IAAI,CAwtBN;AAgmBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2EH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AA0BjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA8H7D,OAAO,KAAK,EAAW,oBAAoB,EAAe,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAgFtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA8pDD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,oBAAoB,GAAG,IAAI,GACxC;IACD,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAqBA;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,EAQrD,oBAAoB,CAAC,EAAE,MAAM;IAC3B,YAAY,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,GACP,IAAI,CAwtBN;AAgmBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA47oBtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
|
package/dist/commands/server.js
CHANGED
|
@@ -4304,9 +4304,15 @@ export async function startServer(options) {
|
|
|
4304
4304
|
}
|
|
4305
4305
|
coherenceJournal.setReplicatedRecordCommitObserver((kind, entries) => {
|
|
4306
4306
|
replicatedPeerStreamReader?.observeCommittedEntries(kind, entries);
|
|
4307
|
+
if (kind === 'ssh-direction-proof')
|
|
4308
|
+
for (const entry of entries)
|
|
4309
|
+
mutualSshRuntime?.handleJournalProof(entry.data, entry.machine);
|
|
4307
4310
|
});
|
|
4308
|
-
journalSyncApplier?.setReplicatedRecordCommitObserver((
|
|
4311
|
+
journalSyncApplier?.setReplicatedRecordCommitObserver((senderMachineId, kind, entries) => {
|
|
4309
4312
|
replicatedPeerStreamReader?.observeCommittedEntries(kind, entries);
|
|
4313
|
+
if (kind === 'ssh-direction-proof')
|
|
4314
|
+
for (const entry of entries)
|
|
4315
|
+
mutualSshRuntime?.handleJournalProof(entry.data, senderMachineId);
|
|
4310
4316
|
});
|
|
4311
4317
|
// Author-side HLC clock — persisted under the journal dir (atomic temp+rename)
|
|
4312
4318
|
// so the merge total order survives restarts (§3.5). node = this machine's id.
|
|
@@ -18402,6 +18408,7 @@ export async function startServer(options) {
|
|
|
18402
18408
|
// are live too. Single-machine uses the in-memory store; the cross-machine
|
|
18403
18409
|
// git-backed store swaps in for the Track-H proof (the registry is store-agnostic).
|
|
18404
18410
|
let meshRpcDispatcher;
|
|
18411
|
+
let mutualSshRuntime = null;
|
|
18405
18412
|
let sessionOwnershipRegistry;
|
|
18406
18413
|
/**
|
|
18407
18414
|
* Ownership Follows Live Work — Part B (claim-on-spawn). Assigned inside the
|
|
@@ -19810,6 +19817,18 @@ export async function startServer(options) {
|
|
|
19810
19817
|
}
|
|
19811
19818
|
},
|
|
19812
19819
|
handlers: {
|
|
19820
|
+
'ssh-bootstrap-advert': (cmd, sender) => {
|
|
19821
|
+
if (!mutualSshRuntime)
|
|
19822
|
+
return { ok: false, reason: 'mutual-ssh-disabled' };
|
|
19823
|
+
const c = cmd;
|
|
19824
|
+
return mutualSshRuntime.handleAdvert(c.advert, sender);
|
|
19825
|
+
},
|
|
19826
|
+
'ssh-proof-publish': (cmd, sender) => {
|
|
19827
|
+
if (!mutualSshRuntime)
|
|
19828
|
+
return { ok: false, reason: 'mutual-ssh-disabled' };
|
|
19829
|
+
const c = cmd;
|
|
19830
|
+
return mutualSshRuntime.handleProof(c.proof, sender);
|
|
19831
|
+
},
|
|
19813
19832
|
'capacity-report': () => machinePoolRegistry?.getCapacities() ?? [],
|
|
19814
19833
|
'session-status': () => {
|
|
19815
19834
|
const base = machinePoolRegistry?.getCapacity(meshSelfId) ?? { machineId: meshSelfId };
|
|
@@ -20355,6 +20374,92 @@ export async function startServer(options) {
|
|
|
20355
20374
|
nickname: m.entry.nickname,
|
|
20356
20375
|
lastKnownUrl: m.entry.lastKnownUrl ?? null,
|
|
20357
20376
|
}));
|
|
20377
|
+
// Mutual SSH phase 1: the signed HTTP mesh is the bootstrap carrier;
|
|
20378
|
+
// the resulting proof itself traverses the restricted SSH subsystem.
|
|
20379
|
+
// Hard-dark on fleet, dry-run first on the development agent.
|
|
20380
|
+
if (resolveDevAgentGate(config.multiMachine?.mutualSsh?.enabled, config)) {
|
|
20381
|
+
try {
|
|
20382
|
+
const sshMod = await import('../core/MutualSshRuntime.js');
|
|
20383
|
+
const portMod = await import('../core/PortRegistry.js');
|
|
20384
|
+
const cryptoMod = await import('node:crypto');
|
|
20385
|
+
const selfPem = meshIdMgr.getSigningPublicKeyPem(meshSelfId) ?? '';
|
|
20386
|
+
const machineFingerprint = (pem) => cryptoMod.createHash('sha256').update(pem).digest('hex');
|
|
20387
|
+
const privateHosts = (getSelfMeshEndpoints?.() ?? []).flatMap((endpoint) => {
|
|
20388
|
+
try {
|
|
20389
|
+
const host = new URL(endpoint.url).hostname;
|
|
20390
|
+
return host ? [host] : [];
|
|
20391
|
+
}
|
|
20392
|
+
catch {
|
|
20393
|
+
// @silent-fallback-ok — malformed advertised URLs are omitted;
|
|
20394
|
+
// readiness names the peer and reports the missing endpoint.
|
|
20395
|
+
return [];
|
|
20396
|
+
}
|
|
20397
|
+
});
|
|
20398
|
+
const bindHost = privateHosts.find(host => host.startsWith('100.'))
|
|
20399
|
+
?? privateHosts.find(host => /^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)/.test(host))
|
|
20400
|
+
?? '127.0.0.1';
|
|
20401
|
+
const bindPort = portMod.allocatePort(`${config.projectName}-mutual-ssh`, 42000, 49000);
|
|
20402
|
+
const peers = () => meshIdMgr.getActiveMachines()
|
|
20403
|
+
.filter(row => row.machineId !== meshSelfId)
|
|
20404
|
+
.map(row => ({
|
|
20405
|
+
machineId: row.machineId,
|
|
20406
|
+
// Millisecond precision is part of the revoke→re-pair replay fence;
|
|
20407
|
+
// second truncation could accidentally reuse an epoch during a fast re-pair.
|
|
20408
|
+
pairingEpoch: Math.max(1, Date.parse(row.entry.pairedAt)),
|
|
20409
|
+
machineFingerprint: machineFingerprint(meshIdMgr.getSigningPublicKeyPem(row.machineId) ?? ''),
|
|
20410
|
+
endpoints: (row.entry.endpoints ?? []).map(endpoint => endpoint.url),
|
|
20411
|
+
}));
|
|
20412
|
+
mutualSshRuntime = new sshMod.MutualSshRuntime({
|
|
20413
|
+
stateDir: config.stateDir, agentId: config.projectName, selfMachineId: meshSelfId,
|
|
20414
|
+
selfMachineFingerprint: machineFingerprint(selfPem),
|
|
20415
|
+
observerBootId: `${meshSelfId}:${process.pid}:${Date.now()}`,
|
|
20416
|
+
bindHost, bindPort, dryRun: config.multiMachine?.mutualSsh?.dryRun !== false,
|
|
20417
|
+
requiredForReadiness: config.multiMachine?.mutualSsh?.requiredForEmployeeRole === true,
|
|
20418
|
+
freshnessMs: config.multiMachine?.mutualSsh?.freshnessMs,
|
|
20419
|
+
cadenceMs: config.multiMachine?.mutualSsh?.cadenceMs,
|
|
20420
|
+
concurrency: config.multiMachine?.mutualSsh?.concurrency,
|
|
20421
|
+
listPeers: peers,
|
|
20422
|
+
send: async (machineId, command) => {
|
|
20423
|
+
const url = peerUrl(machineId);
|
|
20424
|
+
if (!url)
|
|
20425
|
+
throw new Error('mutual-ssh-control-plane-unavailable');
|
|
20426
|
+
const result = await meshClient.send({ machineId, url }, command, 0, { timeoutMs: 15_000 });
|
|
20427
|
+
if (!result.ok)
|
|
20428
|
+
throw new Error(result.reason ?? `mutual-ssh-mesh-${result.status}`);
|
|
20429
|
+
return result.result;
|
|
20430
|
+
},
|
|
20431
|
+
sign: payload => idMod.sign(payload, localSigningKeyPem),
|
|
20432
|
+
verify: (machineId, payload, signature) => {
|
|
20433
|
+
const pem = meshIdMgr.getSigningPublicKeyPem(machineId);
|
|
20434
|
+
return pem ? idMod.verify(payload, signature, pem) : false;
|
|
20435
|
+
},
|
|
20436
|
+
emitJournalProof: proof => {
|
|
20437
|
+
if (!proof.machineSignature)
|
|
20438
|
+
return;
|
|
20439
|
+
const { monotonicDeadlineMs: _, ...durable } = proof;
|
|
20440
|
+
coherenceJournal?.emitSshDirectionProof(durable);
|
|
20441
|
+
},
|
|
20442
|
+
audit: event => {
|
|
20443
|
+
const file = path.join(config.stateDir, 'logs', 'mutual-ssh.jsonl');
|
|
20444
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
20445
|
+
/* state-registry: audit-mutual-ssh */ fs.appendFileSync(file, `${JSON.stringify(event)}\n`);
|
|
20446
|
+
},
|
|
20447
|
+
notifySecurity: event => notify('IMMEDIATE', 'mutual-ssh-host-key-change', JSON.stringify(event)),
|
|
20448
|
+
});
|
|
20449
|
+
await mutualSshRuntime.start();
|
|
20450
|
+
globalThis.__instarMutualSshRuntime = mutualSshRuntime;
|
|
20451
|
+
guardRegistry.register('multiMachine.mutualSsh.enabled', () => ({
|
|
20452
|
+
enabled: true, dryRun: config.multiMachine?.mutualSsh?.dryRun !== false,
|
|
20453
|
+
status: mutualSshRuntime?.status() ?? null,
|
|
20454
|
+
}));
|
|
20455
|
+
}
|
|
20456
|
+
catch (err) {
|
|
20457
|
+
// @silent-fallback-ok — the feature fails closed and surfaces its
|
|
20458
|
+
// stable bootstrap reason while the rest of the server remains live.
|
|
20459
|
+
mutualSshRuntime = null;
|
|
20460
|
+
console.warn(`[mutual-ssh] initialization blocked: ${err instanceof Error ? err.message : String(err)}`);
|
|
20461
|
+
}
|
|
20462
|
+
}
|
|
20358
20463
|
// Pool Dashboard Streaming requesting side (§2.2): build the connector
|
|
20359
20464
|
// the WebSocketManager uses to open an upstream /pool-stream to a peer.
|
|
20360
20465
|
// connect() is synchronous (PeerStreamProxy contract) but the mint +
|
|
@@ -23530,6 +23635,11 @@ export async function startServer(options) {
|
|
|
23530
23635
|
stopHeartbeat?.();
|
|
23531
23636
|
}
|
|
23532
23637
|
catch { /* non-critical during shutdown */ }
|
|
23638
|
+
try {
|
|
23639
|
+
await globalThis.__instarMutualSshRuntime?.rollback();
|
|
23640
|
+
}
|
|
23641
|
+
catch { /* @silent-fallback-ok: shutdown continues; endpoint sockets also die with the process */ }
|
|
23642
|
+
delete globalThis.__instarMutualSshRuntime;
|
|
23533
23643
|
// pid-guarded: only remove OUR OWN registration. An unguarded
|
|
23534
23644
|
// unregister-by-path here deletes the successor generation's fresh
|
|
23535
23645
|
// entry during back-to-back update restarts (registry lost-update
|