yakmesh 2.8.2 → 3.0.0

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.
Files changed (232) hide show
  1. package/CHANGELOG.md +637 -0
  2. package/CONTRIBUTING.md +42 -0
  3. package/Caddyfile +77 -0
  4. package/README.md +119 -29
  5. package/adapters/adapter-mlv-bible/README.md +124 -0
  6. package/adapters/adapter-mlv-bible/index.js +400 -0
  7. package/adapters/chat-mod-adapter.js +532 -0
  8. package/adapters/content-adapter.js +273 -0
  9. package/content/api.js +50 -41
  10. package/content/index.js +2 -2
  11. package/content/store.js +355 -173
  12. package/dashboard/index.html +19 -3
  13. package/database/replication.js +117 -37
  14. package/docs/CRYPTO-AGILITY.md +204 -0
  15. package/docs/MTLS-RESEARCH.md +367 -0
  16. package/docs/NAMCHE-SPEC.md +681 -0
  17. package/docs/PEERQUANTA-YAKMESH-INTEGRATION.md +407 -0
  18. package/docs/PRECISION-DISCLOSURE.md +96 -0
  19. package/docs/README.md +76 -0
  20. package/docs/ROADMAP-2.4.0.md +447 -0
  21. package/docs/ROADMAP-2.5.0.md +244 -0
  22. package/docs/SECURITY-AUDIT-REPORT.md +306 -0
  23. package/docs/SST-INTEGRATION.md +712 -0
  24. package/docs/STEADYWATCH-IMPLEMENTATION.md +303 -0
  25. package/docs/TERNARY-AUDIT-REPORT.md +247 -0
  26. package/docs/TME-FAQ.md +221 -0
  27. package/docs/WHITEPAPER.md +623 -0
  28. package/docs/adapters.html +1001 -0
  29. package/docs/advanced-systems.html +1045 -0
  30. package/docs/annex.html +1046 -0
  31. package/docs/api.html +970 -0
  32. package/docs/business/response-templates.md +160 -0
  33. package/docs/c2c.html +1225 -0
  34. package/docs/cli.html +1332 -0
  35. package/docs/configuration.html +1248 -0
  36. package/docs/darshan.html +1085 -0
  37. package/docs/dharma.html +966 -0
  38. package/docs/docs-bundle.html +1075 -0
  39. package/docs/docs.css +3120 -0
  40. package/docs/docs.js +556 -0
  41. package/docs/doko.html +969 -0
  42. package/docs/geo-proof.html +858 -0
  43. package/docs/getting-started.html +840 -0
  44. package/docs/gumba-tutorial.html +1144 -0
  45. package/docs/gumba.html +1098 -0
  46. package/docs/index.html +914 -0
  47. package/docs/jhilke.html +1312 -0
  48. package/docs/karma.html +1100 -0
  49. package/docs/katha.html +1037 -0
  50. package/docs/lama.html +978 -0
  51. package/docs/mandala.html +1067 -0
  52. package/docs/mani.html +964 -0
  53. package/docs/mantra.html +967 -0
  54. package/docs/mesh.html +1409 -0
  55. package/docs/nakpak.html +869 -0
  56. package/docs/namche.html +928 -0
  57. package/docs/nav-order.json +53 -0
  58. package/docs/prahari.html +1043 -0
  59. package/docs/prism-bash.min.js +1 -0
  60. package/docs/prism-javascript.min.js +1 -0
  61. package/docs/prism-json.min.js +1 -0
  62. package/docs/prism-tomorrow.min.css +1 -0
  63. package/docs/prism.min.js +1 -0
  64. package/docs/privacy.html +699 -0
  65. package/docs/quick-reference.html +1181 -0
  66. package/docs/sakshi.html +1402 -0
  67. package/docs/sandboxing.md +386 -0
  68. package/docs/seva.html +911 -0
  69. package/docs/sherpa.html +871 -0
  70. package/docs/studio.html +860 -0
  71. package/docs/stupa.html +995 -0
  72. package/docs/tailwind.min.css +2 -0
  73. package/docs/tattva.html +1332 -0
  74. package/docs/terms.html +686 -0
  75. package/docs/time-server-deployment.md +166 -0
  76. package/docs/time-sources.html +1392 -0
  77. package/docs/tivra.html +1127 -0
  78. package/docs/trademark-policy.html +686 -0
  79. package/docs/tribhuj.html +1183 -0
  80. package/docs/trust-security.html +1029 -0
  81. package/docs/tutorials/backup-recovery.html +654 -0
  82. package/docs/tutorials/dashboard.html +604 -0
  83. package/docs/tutorials/domain-setup.html +605 -0
  84. package/docs/tutorials/host-website.html +456 -0
  85. package/docs/tutorials/mesh-network.html +505 -0
  86. package/docs/tutorials/mobile-access.html +445 -0
  87. package/docs/tutorials/privacy.html +467 -0
  88. package/docs/tutorials/raspberry-pi.html +600 -0
  89. package/docs/tutorials/security-basics.html +539 -0
  90. package/docs/tutorials/share-files.html +431 -0
  91. package/docs/tutorials/troubleshooting.html +637 -0
  92. package/docs/tutorials/trust-karma.html +419 -0
  93. package/docs/tutorials/yak-protocol.html +456 -0
  94. package/docs/tutorials.html +1034 -0
  95. package/docs/vani.html +1270 -0
  96. package/docs/webserver.html +809 -0
  97. package/docs/yak-protocol.html +940 -0
  98. package/docs/yak-timeserver-design.md +475 -0
  99. package/docs/yakapp.html +1015 -0
  100. package/docs/ypc27.html +1069 -0
  101. package/docs/yurt.html +1344 -0
  102. package/embedded-docs/bundle.js +334 -74
  103. package/gossip/protocol.js +247 -27
  104. package/identity/key-resolver.js +262 -0
  105. package/identity/machine-seed.js +632 -0
  106. package/identity/node-key.js +669 -368
  107. package/identity/tribhuj-ratchet.js +506 -0
  108. package/knowledge-base.js +37 -8
  109. package/launcher/yakmesh.bat +62 -0
  110. package/launcher/yakmesh.sh +70 -0
  111. package/mesh/annex.js +462 -108
  112. package/mesh/beacon-broadcast.js +113 -1
  113. package/mesh/darshan.js +1718 -0
  114. package/mesh/gumba.js +1567 -0
  115. package/mesh/jhilke.js +651 -0
  116. package/mesh/katha.js +1012 -0
  117. package/mesh/nakpak-routing.js +8 -5
  118. package/mesh/network.js +724 -34
  119. package/mesh/pulse-sync.js +4 -1
  120. package/mesh/rate-limiter.js +127 -15
  121. package/mesh/seva.js +526 -0
  122. package/mesh/sherpa-discovery.js +89 -8
  123. package/mesh/sybil-defense.js +19 -5
  124. package/mesh/temporal-encoder.js +4 -3
  125. package/mesh/vani.js +1364 -0
  126. package/mesh/yurt.js +1340 -0
  127. package/models/entropy-sentinel.onnx +0 -0
  128. package/models/karma-trust.onnx +0 -0
  129. package/models/manifest.json +43 -0
  130. package/models/sakshi-anomaly.onnx +0 -0
  131. package/oracle/code-proof-protocol.js +7 -6
  132. package/oracle/codebase-lock.js +257 -28
  133. package/oracle/index.js +74 -15
  134. package/oracle/ma902-snmp.js +678 -0
  135. package/oracle/module-sealer.js +5 -3
  136. package/oracle/network-identity.js +16 -0
  137. package/oracle/packet-checksum.js +201 -0
  138. package/oracle/sst.js +579 -0
  139. package/oracle/ternary-144t.js +714 -0
  140. package/oracle/ternary-ml.js +481 -0
  141. package/oracle/time-api.js +239 -0
  142. package/oracle/time-source.js +137 -47
  143. package/oracle/validation-oracle-hardened.js +1111 -1071
  144. package/oracle/validation-oracle.js +4 -2
  145. package/oracle/ypc27.js +211 -0
  146. package/package.json +20 -3
  147. package/protocol/yak-handler.js +35 -9
  148. package/protocol/yak-protocol.js +28 -13
  149. package/reference/cpp/yakmesh_mceliece_shard.cpp +168 -0
  150. package/reference/cpp/yakmesh_ypc27.cpp +179 -0
  151. package/sbom.json +87 -0
  152. package/scripts/security-audit.mjs +264 -0
  153. package/scripts/update-docs-nav.js +194 -0
  154. package/scripts/update-docs-sidebar.cjs +164 -0
  155. package/security/crypto-config.js +4 -3
  156. package/security/dharma-moderation.js +517 -0
  157. package/security/doko-identity.js +193 -143
  158. package/security/domain-consensus.js +86 -85
  159. package/security/fs-hardening.js +620 -0
  160. package/security/hardware-attestation.js +5 -3
  161. package/security/hybrid-trust.js +227 -87
  162. package/security/karma-rate-limiter.js +692 -0
  163. package/security/khata-protocol.js +22 -21
  164. package/security/khata-trust-integration.js +277 -150
  165. package/security/memory-safety.js +635 -0
  166. package/security/mesh-auth.js +11 -10
  167. package/security/mesh-revocation.js +373 -5
  168. package/security/namche-gateway.js +298 -69
  169. package/security/sakshi.js +460 -3
  170. package/security/sangha.js +770 -0
  171. package/security/secure-config.js +473 -0
  172. package/security/silicon-parity.js +13 -10
  173. package/security/steadywatch.js +1142 -0
  174. package/security/strike-system.js +32 -3
  175. package/security/temporal-signing.js +488 -0
  176. package/security/trit-commitment.js +464 -0
  177. package/server/crypto/annex.js +247 -0
  178. package/server/darshan-api.js +343 -0
  179. package/server/index.js +3259 -362
  180. package/server/komm-api.js +668 -0
  181. package/utils/accel.js +2273 -0
  182. package/utils/ternary-id.js +79 -0
  183. package/utils/verify-worker.js +57 -0
  184. package/webserver/index.js +95 -5
  185. package/assets/yakmesh-logo.png +0 -0
  186. package/assets/yakmesh-logo.svg +0 -80
  187. package/assets/yakmesh-logo2.png +0 -0
  188. package/assets/yakmesh-logo2sm.png +0 -0
  189. package/assets/ymsm.png +0 -0
  190. package/website/assets/silhouettes/adapters.svg +0 -107
  191. package/website/assets/silhouettes/api-endpoints.svg +0 -115
  192. package/website/assets/silhouettes/atomic-clock.svg +0 -83
  193. package/website/assets/silhouettes/base-camp.svg +0 -81
  194. package/website/assets/silhouettes/bridge.svg +0 -69
  195. package/website/assets/silhouettes/docs-bundle.svg +0 -113
  196. package/website/assets/silhouettes/doko-basket.svg +0 -70
  197. package/website/assets/silhouettes/fortress.svg +0 -93
  198. package/website/assets/silhouettes/gateway.svg +0 -54
  199. package/website/assets/silhouettes/gears.svg +0 -93
  200. package/website/assets/silhouettes/globe-satellite.svg +0 -67
  201. package/website/assets/silhouettes/karma-wheel.svg +0 -137
  202. package/website/assets/silhouettes/lama-council.svg +0 -141
  203. package/website/assets/silhouettes/mandala-network.svg +0 -169
  204. package/website/assets/silhouettes/mani-stones.svg +0 -149
  205. package/website/assets/silhouettes/mantra-wheel.svg +0 -116
  206. package/website/assets/silhouettes/mesh-nodes.svg +0 -113
  207. package/website/assets/silhouettes/nakpak.svg +0 -56
  208. package/website/assets/silhouettes/peak-lightning.svg +0 -73
  209. package/website/assets/silhouettes/sherpa.svg +0 -69
  210. package/website/assets/silhouettes/stupa-tower.svg +0 -119
  211. package/website/assets/silhouettes/tattva-eye.svg +0 -78
  212. package/website/assets/silhouettes/terminal.svg +0 -74
  213. package/website/assets/silhouettes/webserver.svg +0 -145
  214. package/website/assets/silhouettes/yak.svg +0 -78
  215. package/website/assets/yakmesh-logo.png +0 -0
  216. package/website/assets/yakmesh-logo.webp +0 -0
  217. package/website/assets/yakmesh-logo128x140.webp +0 -0
  218. package/website/assets/yakmesh-logo2.png +0 -0
  219. package/website/assets/yakmesh-logo2.svg +0 -51
  220. package/website/assets/yakmesh-logo40x44.webp +0 -0
  221. package/website/assets/yakmesh.gif +0 -0
  222. package/website/assets/yakmesh.ico +0 -0
  223. package/website/assets/yakmesh.jpg +0 -0
  224. package/website/assets/yakmesh.pdf +0 -0
  225. package/website/assets/yakmesh.png +0 -0
  226. package/website/assets/yakmesh.svg +0 -70
  227. package/website/assets/yakmesh128.webp +0 -0
  228. package/website/assets/yakmesh32.png +0 -0
  229. package/website/assets/yakmesh32.svg +0 -65
  230. package/website/assets/yakmesh32o.ico +0 -2
  231. package/website/assets/yakmesh32o.svg +0 -65
  232. package/website/assets/yakmesh32o.svgz +0 -0
@@ -1440,7 +1440,7 @@ export function assessComputationTrust(computation, computedBy, options = {}) {
1440
1440
  }))
1441
1441
  );
1442
1442
 
1443
- if (verificationAgreement.agreed && verificationAgreement.value === 'VALID') {
1443
+ if (verificationAgreement.agreed && verificationAgreement.data?.value === 'VALID') {
1444
1444
  return {
1445
1445
  trusted: true,
1446
1446
  basis: 'VERIFIED',
@@ -1449,7 +1449,7 @@ export function assessComputationTrust(computation, computedBy, options = {}) {
1449
1449
  };
1450
1450
  }
1451
1451
 
1452
- if (verificationAgreement.agreed && verificationAgreement.value === 'INVALID') {
1452
+ if (verificationAgreement.agreed && verificationAgreement.data?.value === 'INVALID') {
1453
1453
  return {
1454
1454
  trusted: false,
1455
1455
  basis: 'VERIFICATION_FAILED',
@@ -1462,11 +1462,462 @@ export function assessComputationTrust(computation, computedBy, options = {}) {
1462
1462
  return {
1463
1463
  trusted: false,
1464
1464
  basis: 'VERIFIERS_DISAGREE',
1465
- action: verificationAgreement.action,
1465
+ action: verificationAgreement.data?.action,
1466
1466
  suggestion: 'Need more verifiers or investigate disagreement',
1467
1467
  };
1468
1468
  }
1469
1469
 
1470
+ // =============================================================================
1471
+ // BEHAVIOR VELOCITY MONITORING
1472
+ // =============================================================================
1473
+
1474
+ /**
1475
+ * VEGATI - Velocity-based Behavior Change Detection
1476
+ * वेगति (Sanskrit: "velocity, momentum")
1477
+ *
1478
+ * Monitors nodes for sudden behavioral changes that may indicate:
1479
+ * - Compromised hardware/keys
1480
+ * - Reputation farming then abuse
1481
+ * - Insider threat activation
1482
+ *
1483
+ * Uses exponential moving average to establish behavioral baselines,
1484
+ * then triggers alerts when current behavior deviates significantly.
1485
+ */
1486
+
1487
+ /**
1488
+ * Velocity alert severity levels
1489
+ */
1490
+ export const VELOCITY_ALERT = Object.freeze({
1491
+ NORMAL: 'normal', // Within expected variance
1492
+ ELEVATED: 'elevated', // Notable change, monitor closely
1493
+ WARNING: 'warning', // Significant deviation
1494
+ CRITICAL: 'critical', // Dramatic behavioral shift
1495
+ });
1496
+
1497
+ /**
1498
+ * Behavior dimensions tracked for velocity detection
1499
+ */
1500
+ export const BEHAVIOR_DIMENSION = Object.freeze({
1501
+ MESSAGE_RATE: 'message_rate', // Messages per minute
1502
+ GOSSIP_RATIO: 'gossip_ratio', // Gossip vs direct messages
1503
+ ERROR_RATE: 'error_rate', // Invalid messages/signatures
1504
+ ATTESTATION_RATE: 'attestation_rate', // Revocation attestations filed
1505
+ CONNECTION_CHURN: 'connection_churn', // Connect/disconnect frequency
1506
+ RESPONSE_LATENCY: 'response_latency', // Average response time
1507
+ });
1508
+
1509
+ /**
1510
+ * BehaviorVelocityMonitor - Tracks behavioral baselines and detects anomalies
1511
+ *
1512
+ * Each node builds a behavioral "fingerprint" over time.
1513
+ * Sudden changes from this fingerprint trigger velocity alerts.
1514
+ */
1515
+ export class BehaviorVelocityMonitor {
1516
+ constructor(options = {}) {
1517
+ this.profiles = new Map(); // nodeId -> BehaviorProfile
1518
+ this._inferenceEngine = options.inferenceEngine || null;
1519
+ this._modelName = 'sakshi-anomaly';
1520
+
1521
+ // Configuration
1522
+ this.config = {
1523
+ // EMA smoothing factor (0-1, lower = slower adaptation)
1524
+ emaSmoothingFactor: options.emaSmoothingFactor || 0.1,
1525
+
1526
+ // Minimum observations before establishing baseline
1527
+ minObservationsForBaseline: options.minObservationsForBaseline || 50,
1528
+
1529
+ // Standard deviation thresholds for alerts
1530
+ thresholds: {
1531
+ elevated: options.elevatedThreshold || 2.0, // 2 sigma
1532
+ warning: options.warningThreshold || 3.0, // 3 sigma
1533
+ critical: options.criticalThreshold || 4.0, // 4 sigma
1534
+ },
1535
+
1536
+ // Cooldown period after alert before re-alerting (ms)
1537
+ alertCooldown: options.alertCooldown || 60000,
1538
+
1539
+ // Profile retention (ms)
1540
+ profileTTL: options.profileTTL || 7 * 24 * 60 * 60 * 1000, // 7 days
1541
+ };
1542
+
1543
+ // Alert callbacks
1544
+ this.alertCallbacks = [];
1545
+
1546
+ log.info('vegati', 'Behavior velocity monitor initialized');
1547
+ }
1548
+
1549
+ /**
1550
+ * Register a callback for velocity alerts
1551
+ */
1552
+ onAlert(callback) {
1553
+ this.alertCallbacks.push(callback);
1554
+ }
1555
+
1556
+ /**
1557
+ * Record an observation for a node
1558
+ *
1559
+ * @param {string} nodeId - Node identifier
1560
+ * @param {string} dimension - Which behavior dimension (from BEHAVIOR_DIMENSION)
1561
+ * @param {number} value - Observed value
1562
+ * @returns {Object} Current velocity status for this dimension
1563
+ */
1564
+ observe(nodeId, dimension, value) {
1565
+ let profile = this.profiles.get(nodeId);
1566
+
1567
+ if (!profile) {
1568
+ profile = this._createProfile(nodeId);
1569
+ this.profiles.set(nodeId, profile);
1570
+ }
1571
+
1572
+ profile.lastSeen = Date.now();
1573
+
1574
+ // Get or create dimension stats
1575
+ let stats = profile.dimensions.get(dimension);
1576
+ if (!stats) {
1577
+ stats = {
1578
+ count: 0,
1579
+ ema: value, // Exponential moving average
1580
+ emVar: 0, // Exponential moving variance
1581
+ lastValue: value,
1582
+ lastAlert: 0,
1583
+ alertCount: 0,
1584
+ };
1585
+ profile.dimensions.set(dimension, stats);
1586
+ }
1587
+
1588
+ stats.count++;
1589
+ stats.lastValue = value;
1590
+
1591
+ // Update EMA (baseline)
1592
+ const alpha = this.config.emaSmoothingFactor;
1593
+ const delta = value - stats.ema;
1594
+ stats.ema = stats.ema + alpha * delta;
1595
+
1596
+ // Update exponential moving variance (for std dev calculation)
1597
+ stats.emVar = (1 - alpha) * (stats.emVar + alpha * delta * delta);
1598
+
1599
+ // Only check velocity once we have baseline
1600
+ if (stats.count < this.config.minObservationsForBaseline) {
1601
+ return {
1602
+ status: VELOCITY_ALERT.NORMAL,
1603
+ reason: 'BUILDING_BASELINE',
1604
+ progress: stats.count / this.config.minObservationsForBaseline,
1605
+ };
1606
+ }
1607
+
1608
+ // Calculate z-score (standard deviations from mean)
1609
+ const stdDev = Math.sqrt(stats.emVar);
1610
+ const zScore = stdDev > 0 ? Math.abs(delta) / stdDev : 0;
1611
+
1612
+ // Determine alert level
1613
+ let alertLevel = VELOCITY_ALERT.NORMAL;
1614
+ if (zScore >= this.config.thresholds.critical) {
1615
+ alertLevel = VELOCITY_ALERT.CRITICAL;
1616
+ } else if (zScore >= this.config.thresholds.warning) {
1617
+ alertLevel = VELOCITY_ALERT.WARNING;
1618
+ } else if (zScore >= this.config.thresholds.elevated) {
1619
+ alertLevel = VELOCITY_ALERT.ELEVATED;
1620
+ }
1621
+
1622
+ // Emit alert if significant and not in cooldown
1623
+ const now = Date.now();
1624
+ if (alertLevel !== VELOCITY_ALERT.NORMAL &&
1625
+ now - stats.lastAlert > this.config.alertCooldown) {
1626
+ stats.lastAlert = now;
1627
+ stats.alertCount++;
1628
+
1629
+ const alert = {
1630
+ nodeId,
1631
+ dimension,
1632
+ level: alertLevel,
1633
+ zScore,
1634
+ currentValue: value,
1635
+ baselineEma: stats.ema,
1636
+ baselineStdDev: stdDev,
1637
+ deviation: delta,
1638
+ alertCount: stats.alertCount,
1639
+ timestamp: now,
1640
+ };
1641
+
1642
+ // Emit to callbacks
1643
+ for (const callback of this.alertCallbacks) {
1644
+ try {
1645
+ callback(alert);
1646
+ } catch (e) {
1647
+ log.error('vegati', 'Alert callback error', { error: e.message });
1648
+ }
1649
+ }
1650
+
1651
+ log.warn('vegati', `Velocity alert: ${alertLevel}`, {
1652
+ nodeId,
1653
+ dimension,
1654
+ zScore: zScore.toFixed(2),
1655
+ deviation: delta.toFixed(4),
1656
+ });
1657
+ }
1658
+
1659
+ return {
1660
+ status: alertLevel,
1661
+ zScore,
1662
+ deviation: delta,
1663
+ baseline: stats.ema,
1664
+ stdDev,
1665
+ };
1666
+ }
1667
+
1668
+ /**
1669
+ * Get the current behavioral profile for a node
1670
+ */
1671
+ getProfile(nodeId) {
1672
+ const profile = this.profiles.get(nodeId);
1673
+ if (!profile) return null;
1674
+
1675
+ const dimensions = {};
1676
+ for (const [dim, stats] of profile.dimensions) {
1677
+ dimensions[dim] = {
1678
+ baseline: stats.ema,
1679
+ stdDev: Math.sqrt(stats.emVar),
1680
+ observations: stats.count,
1681
+ lastValue: stats.lastValue,
1682
+ alertCount: stats.alertCount,
1683
+ hasBaseline: stats.count >= this.config.minObservationsForBaseline,
1684
+ };
1685
+ }
1686
+
1687
+ return {
1688
+ nodeId: profile.nodeId,
1689
+ createdAt: profile.createdAt,
1690
+ lastSeen: profile.lastSeen,
1691
+ dimensions,
1692
+ };
1693
+ }
1694
+
1695
+ /**
1696
+ * Get nodes with active alerts
1697
+ */
1698
+ getActiveAlerts() {
1699
+ const alerts = [];
1700
+ const now = Date.now();
1701
+
1702
+ for (const profile of this.profiles.values()) {
1703
+ for (const [dim, stats] of profile.dimensions) {
1704
+ if (stats.alertCount > 0 && now - stats.lastAlert < this.config.alertCooldown * 2) {
1705
+ const stdDev = Math.sqrt(stats.emVar);
1706
+ const zScore = stdDev > 0 ? Math.abs(stats.lastValue - stats.ema) / stdDev : 0;
1707
+
1708
+ let level = VELOCITY_ALERT.NORMAL;
1709
+ if (zScore >= this.config.thresholds.critical) level = VELOCITY_ALERT.CRITICAL;
1710
+ else if (zScore >= this.config.thresholds.warning) level = VELOCITY_ALERT.WARNING;
1711
+ else if (zScore >= this.config.thresholds.elevated) level = VELOCITY_ALERT.ELEVATED;
1712
+
1713
+ if (level !== VELOCITY_ALERT.NORMAL) {
1714
+ alerts.push({
1715
+ nodeId: profile.nodeId,
1716
+ dimension: dim,
1717
+ level,
1718
+ zScore,
1719
+ totalAlerts: stats.alertCount,
1720
+ lastAlert: stats.lastAlert,
1721
+ });
1722
+ }
1723
+ }
1724
+ }
1725
+ }
1726
+
1727
+ return alerts;
1728
+ }
1729
+
1730
+ /**
1731
+ * Get aggregate velocity statistics
1732
+ */
1733
+ getStats() {
1734
+ let totalProfiles = 0;
1735
+ let profilesWithBaseline = 0;
1736
+ let activeAlerts = 0;
1737
+ const alertsByLevel = {
1738
+ [VELOCITY_ALERT.ELEVATED]: 0,
1739
+ [VELOCITY_ALERT.WARNING]: 0,
1740
+ [VELOCITY_ALERT.CRITICAL]: 0,
1741
+ };
1742
+
1743
+ for (const profile of this.profiles.values()) {
1744
+ totalProfiles++;
1745
+ let hasAnyBaseline = false;
1746
+
1747
+ for (const [dim, stats] of profile.dimensions) {
1748
+ if (stats.count >= this.config.minObservationsForBaseline) {
1749
+ hasAnyBaseline = true;
1750
+ }
1751
+
1752
+ if (stats.alertCount > 0) {
1753
+ const stdDev = Math.sqrt(stats.emVar);
1754
+ const zScore = stdDev > 0 ? Math.abs(stats.lastValue - stats.ema) / stdDev : 0;
1755
+
1756
+ if (zScore >= this.config.thresholds.critical) {
1757
+ activeAlerts++;
1758
+ alertsByLevel[VELOCITY_ALERT.CRITICAL]++;
1759
+ } else if (zScore >= this.config.thresholds.warning) {
1760
+ activeAlerts++;
1761
+ alertsByLevel[VELOCITY_ALERT.WARNING]++;
1762
+ } else if (zScore >= this.config.thresholds.elevated) {
1763
+ activeAlerts++;
1764
+ alertsByLevel[VELOCITY_ALERT.ELEVATED]++;
1765
+ }
1766
+ }
1767
+ }
1768
+
1769
+ if (hasAnyBaseline) profilesWithBaseline++;
1770
+ }
1771
+
1772
+ return {
1773
+ totalProfiles,
1774
+ profilesWithBaseline,
1775
+ activeAlerts,
1776
+ alertsByLevel,
1777
+ };
1778
+ }
1779
+
1780
+ /**
1781
+ * NPU-accelerated anomaly assessment for a node.
1782
+ * Feeds all behavioral dimensions + contextual features into the
1783
+ * sakshi-anomaly ONNX model for multi-class attack detection.
1784
+ *
1785
+ * Falls back to CPU heuristic (z-score based) if ONNX Runtime is unavailable.
1786
+ *
1787
+ * @param {string} nodeId - Node to assess
1788
+ * @param {Object} context - Additional context features
1789
+ * @param {number} [context.uptimePercent=0.5] - Node uptime (0-1)
1790
+ * @param {number} [context.networkAgeDays=0] - Days on network
1791
+ * @param {number} [context.karmaScore=0.5] - Current KARMA score (0-1)
1792
+ * @param {boolean} [context.hasAesni=false] - Hardware AES-NI attestation
1793
+ * @param {number} [context.timeSourceQuality=0] - Time source quality (0=system, 0.5=ntp, 1=ptp)
1794
+ * @param {number} [context.observationCount=0] - Total observations recorded
1795
+ * @returns {Promise<Object>} Anomaly assessment with scores per threat type
1796
+ */
1797
+ async assessNode(nodeId, context = {}) {
1798
+ const profile = this.profiles.get(nodeId);
1799
+
1800
+ // Default feature values (zero-filled if no profile)
1801
+ const getDimValue = (dim) => {
1802
+ if (!profile) return 0;
1803
+ const stats = profile.dimensions.get(dim);
1804
+ return stats ? stats.lastValue : 0;
1805
+ };
1806
+
1807
+ // Build 12-feature input vector (must match training data order)
1808
+ const features = new Float32Array([
1809
+ getDimValue(BEHAVIOR_DIMENSION.MESSAGE_RATE),
1810
+ getDimValue(BEHAVIOR_DIMENSION.GOSSIP_RATIO),
1811
+ getDimValue(BEHAVIOR_DIMENSION.ERROR_RATE),
1812
+ getDimValue(BEHAVIOR_DIMENSION.ATTESTATION_RATE),
1813
+ getDimValue(BEHAVIOR_DIMENSION.CONNECTION_CHURN),
1814
+ getDimValue(BEHAVIOR_DIMENSION.RESPONSE_LATENCY),
1815
+ Math.min(1.0, context.uptimePercent ?? 0.5),
1816
+ Math.min(1.0, (context.networkAgeDays ?? 0) / 365),
1817
+ Math.min(1.0, context.karmaScore ?? 0.5),
1818
+ context.hasAesni ? 1.0 : 0.0,
1819
+ Math.min(1.0, context.timeSourceQuality ?? 0),
1820
+ Math.min(1.0, (context.observationCount ?? 0) / 1000),
1821
+ ]);
1822
+
1823
+ // NPU path: use ONNX model if available
1824
+ const engine = this._inferenceEngine;
1825
+ if (engine && engine.hasModel(this._modelName)) {
1826
+ try {
1827
+ const result = await engine.infer(this._modelName, {
1828
+ behavior_features: features,
1829
+ });
1830
+ if (result && result.anomaly_scores) {
1831
+ const scores = result.anomaly_scores;
1832
+ return {
1833
+ source: 'NPU',
1834
+ nodeId,
1835
+ anomalyScore: scores[0],
1836
+ sybilScore: scores[1],
1837
+ eclipseScore: scores[2],
1838
+ floodScore: scores[3],
1839
+ features,
1840
+ };
1841
+ }
1842
+ } catch (err) {
1843
+ log.warn('vegati', `NPU assessment failed for ${nodeId}: ${err.message}`);
1844
+ }
1845
+ }
1846
+
1847
+ // CPU fallback: aggregate z-scores across dimensions
1848
+ let maxZScore = 0;
1849
+ let anomalySum = 0;
1850
+ let dimCount = 0;
1851
+
1852
+ if (profile) {
1853
+ for (const [, stats] of profile.dimensions) {
1854
+ if (stats.count >= this.config.minObservationsForBaseline) {
1855
+ const stdDev = Math.sqrt(stats.emVar);
1856
+ const zScore = stdDev > 0 ? Math.abs(stats.lastValue - stats.ema) / stdDev : 0;
1857
+ maxZScore = Math.max(maxZScore, zScore);
1858
+ anomalySum += Math.min(1.0, zScore / this.config.thresholds.critical);
1859
+ dimCount++;
1860
+ }
1861
+ }
1862
+ }
1863
+
1864
+ const anomalyScore = dimCount > 0 ? anomalySum / dimCount : 0;
1865
+ return {
1866
+ source: 'CPU',
1867
+ nodeId,
1868
+ anomalyScore,
1869
+ sybilScore: 0, // CPU fallback cannot distinguish attack types
1870
+ eclipseScore: 0,
1871
+ floodScore: 0,
1872
+ maxZScore,
1873
+ features,
1874
+ };
1875
+ }
1876
+
1877
+ /**
1878
+ * Cleanup old profiles
1879
+ */
1880
+ cleanup() {
1881
+ const now = Date.now();
1882
+ let removed = 0;
1883
+
1884
+ for (const [nodeId, profile] of this.profiles) {
1885
+ if (now - profile.lastSeen > this.config.profileTTL) {
1886
+ this.profiles.delete(nodeId);
1887
+ removed++;
1888
+ }
1889
+ }
1890
+
1891
+ if (removed > 0) {
1892
+ log.info('vegati', `Cleaned up ${removed} stale profiles`);
1893
+ }
1894
+
1895
+ return removed;
1896
+ }
1897
+
1898
+ _createProfile(nodeId) {
1899
+ return {
1900
+ nodeId,
1901
+ createdAt: Date.now(),
1902
+ lastSeen: Date.now(),
1903
+ dimensions: new Map(),
1904
+ };
1905
+ }
1906
+ }
1907
+
1908
+ // Singleton instance
1909
+ let _velocityMonitor = null;
1910
+
1911
+ /**
1912
+ * Get the singleton velocity monitor instance
1913
+ */
1914
+ export function getVelocityMonitor(options) {
1915
+ if (!_velocityMonitor) {
1916
+ _velocityMonitor = new BehaviorVelocityMonitor(options);
1917
+ }
1918
+ return _velocityMonitor;
1919
+ }
1920
+
1470
1921
  // =============================================================================
1471
1922
  // EXPORTS
1472
1923
  // =============================================================================
@@ -1506,4 +1957,10 @@ export default {
1506
1957
  checkRevocationAgreement,
1507
1958
  aggregateAttestations,
1508
1959
  assessComputationTrust,
1960
+
1961
+ // Velocity detection (VEGATI)
1962
+ VELOCITY_ALERT,
1963
+ BEHAVIOR_DIMENSION,
1964
+ BehaviorVelocityMonitor,
1965
+ getVelocityMonitor,
1509
1966
  };