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
@@ -0,0 +1,678 @@
1
+ /**
2
+ * ╔═══════════════════════════════════════════════════════════════════════════════╗
3
+ * ║ 📡 MA-902 SNMP MONITOR — CELESTIAL STONE TELEMETRY 📡 ║
4
+ * ╠═══════════════════════════════════════════════════════════════════════════════╣
5
+ * ║ ║
6
+ * ║ The MA-902/S-C1 GPS Gigabit Time Server is a hardware MANI stone — ║
7
+ * ║ a celestial marker receiving signals from satellite constellations. ║
8
+ * ║ ║
9
+ * ║ This module queries the MA-902 via SNMP v2c to extract: ║
10
+ * ║ - GPS time (Unix epoch) with sub-second precision ║
11
+ * ║ - Satellite lock status and constellation info ║
12
+ * ║ - Visible/tracked/used satellite counts ║
13
+ * ║ - Alarm and quality indicators ║
14
+ * ║ ║
15
+ * ║ Enterprise OID: 1.3.6.1.4.1.26381 (Chongqing Miaoan / MA-902) ║
16
+ * ║ ║
17
+ * ║ SNMP data feeds directly into ManiTimeDetector for real-time trust ║
18
+ * ║ assessment — if satellites degrade, trust level adjusts automatically. ║
19
+ * ║ ║
20
+ * ╚═══════════════════════════════════════════════════════════════════════════════╝
21
+ *
22
+ * @module mani/ma902-snmp
23
+ */
24
+
25
+ import { EventEmitter } from 'events';
26
+ import { createLogger } from '../utils/logger.js';
27
+
28
+ const log = createLogger('mani:ma902');
29
+
30
+ // ============================================================
31
+ // MA-902 ENTERPRISE OID MAP
32
+ // ============================================================
33
+
34
+ /**
35
+ * Enterprise OID prefix for the MA-902/S-C1 time server
36
+ * Vendor: Chongqing Miaoan Technology (重庆妙安科技有限公司)
37
+ * IANA Enterprise Number: 26381
38
+ */
39
+ const MA902_ENTERPRISE_OID = '1.3.6.1.4.1.26381';
40
+ const MA902_DATA_PREFIX = `${MA902_ENTERPRISE_OID}.1.1`;
41
+
42
+ /**
43
+ * MA-902 proprietary SNMP OID definitions
44
+ * Discovered via SNMP walk on 2026-02-19
45
+ */
46
+ const MA902_OIDS = {
47
+ GPS_TIME: `${MA902_DATA_PREFIX}.1.0`, // Unix timestamp (seconds)
48
+ SUB_SECONDS: `${MA902_DATA_PREFIX}.2.0`, // Sub-second counter (nanosecond-scale)
49
+ LOCK_STATUS: `${MA902_DATA_PREFIX}.3.0`, // 1 = locked to satellites, 0 = unlocked
50
+ REF_SOURCE: `${MA902_DATA_PREFIX}.4.0`, // Reference source type (1 = GPS)
51
+ CONSTELLATION_MASK: `${MA902_DATA_PREFIX}.5.0`, // Bitmask of active constellations
52
+ SATS_VISIBLE: `${MA902_DATA_PREFIX}.6.0`, // Number of satellites visible
53
+ SATS_USED: `${MA902_DATA_PREFIX}.7.0`, // Number of satellites in fix solution
54
+ SATS_TRACKING: `${MA902_DATA_PREFIX}.8.0`, // Number of satellites being tracked
55
+ ALARM_STATUS: `${MA902_DATA_PREFIX}.9.0`, // 0 = no alarms
56
+ QUALITY: `${MA902_DATA_PREFIX}.10.0`, // Timing quality indicator
57
+ OFFSET: `${MA902_DATA_PREFIX}.11.0`, // Clock offset
58
+ RESERVED: `${MA902_DATA_PREFIX}.12.0`, // Reserved (0xFFFFFFFF sentinel)
59
+ };
60
+
61
+ /**
62
+ * Standard MIB-II OIDs for basic system info
63
+ */
64
+ const SYSTEM_OIDS = {
65
+ SYS_DESCR: '1.3.6.1.2.1.1.1.0',
66
+ SYS_UPTIME: '1.3.6.1.2.1.1.3.0',
67
+ SYS_NAME: '1.3.6.1.2.1.1.5.0',
68
+ };
69
+
70
+ /**
71
+ * Constellation bitmask mapping
72
+ * Based on observed values: GPS=1, BeiDou=2, GLONASS=4, Galileo=8, QZSS=16
73
+ */
74
+ const CONSTELLATION_FLAGS = {
75
+ GPS: 0x01,
76
+ BEIDOU: 0x02,
77
+ GLONASS: 0x04,
78
+ GALILEO: 0x08,
79
+ QZSS: 0x10,
80
+ };
81
+
82
+ /**
83
+ * Minimum satellite thresholds for trust assessment
84
+ */
85
+ const SAT_THRESHOLDS = {
86
+ EXCELLENT: 8, // >=8 sats = excellent fix
87
+ GOOD: 5, // >=5 sats = good fix
88
+ MARGINAL: 3, // >=3 sats = marginal fix (3D requires minimum 4)
89
+ DEGRADED: 1, // 1-2 sats = degraded, likely no valid fix
90
+ };
91
+
92
+ // ============================================================
93
+ // MA-902 SNMP MONITOR
94
+ // ============================================================
95
+
96
+ /**
97
+ * MA-902 GPS Time Server SNMP Monitor
98
+ *
99
+ * Queries the MA-902 via SNMP v2c to extract real-time satellite
100
+ * and timing telemetry. Emits events when status changes.
101
+ *
102
+ * @example
103
+ * const monitor = new MA902Monitor({ host: '192.168.1.30' });
104
+ * monitor.on('telemetry', (data) => console.log(data));
105
+ * await monitor.start();
106
+ */
107
+ export class MA902Monitor extends EventEmitter {
108
+ constructor(options = {}) {
109
+ super();
110
+
111
+ this.options = {
112
+ host: options.host || '192.168.1.30',
113
+ port: options.port || 161,
114
+ community: options.community || 'public',
115
+ pollInterval: options.pollInterval || 10000, // 10s default
116
+ retries: options.retries || 2,
117
+ timeout: options.timeout || 3000, // 3s SNMP timeout
118
+ minSatellites: options.minSatellites || SAT_THRESHOLDS.MARGINAL,
119
+ verbose: options.verbose || false,
120
+ };
121
+
122
+ this.snmpSession = null;
123
+ this.snmpLib = null;
124
+ this.pollTimer = null;
125
+ this.available = false;
126
+ this.lastTelemetry = null;
127
+ this.consecutiveFailures = 0;
128
+ this.maxConsecutiveFailures = 5;
129
+ this.systemInfo = null;
130
+ }
131
+
132
+ /**
133
+ * Initialize the SNMP library (lazy-loaded to avoid hard dependency)
134
+ * @returns {boolean} Whether SNMP is available
135
+ */
136
+ async _initSnmp() {
137
+ if (this.snmpLib) return true;
138
+
139
+ try {
140
+ this.snmpLib = await import('net-snmp');
141
+ // Handle both default and named export patterns
142
+ if (this.snmpLib.default) {
143
+ this.snmpLib = this.snmpLib.default;
144
+ }
145
+ return true;
146
+ } catch (err) {
147
+ log.warn('net-snmp not available - MA-902 SNMP monitoring disabled', {
148
+ hint: 'npm install net-snmp',
149
+ error: err.message,
150
+ });
151
+ return false;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Create or recreate the SNMP session
157
+ */
158
+ _createSession() {
159
+ if (this.snmpSession) {
160
+ try { this.snmpSession.close(); } catch (e) { /* ignore */ }
161
+ }
162
+
163
+ this.snmpSession = this.snmpLib.createSession(this.options.host, this.options.community, {
164
+ port: this.options.port,
165
+ retries: this.options.retries,
166
+ timeout: this.options.timeout,
167
+ version: this.snmpLib.Version2c,
168
+ transport: 'udp4',
169
+ });
170
+
171
+ this.snmpSession.on('error', (err) => {
172
+ log.debug('SNMP session error', { error: err.message });
173
+ });
174
+ }
175
+
176
+ /**
177
+ * Start monitoring the MA-902
178
+ * @returns {boolean} Whether monitoring started successfully
179
+ */
180
+ async start() {
181
+ const snmpAvailable = await this._initSnmp();
182
+ if (!snmpAvailable) {
183
+ this.available = false;
184
+ return false;
185
+ }
186
+
187
+ this._createSession();
188
+
189
+ // Initial probe - verify the device responds
190
+ const probe = await this._querySystemInfo();
191
+ if (!probe) {
192
+ log.warn('MA-902 not responding at ' + this.options.host, {
193
+ action: 'Will retry on next poll cycle',
194
+ });
195
+ } else {
196
+ this.systemInfo = probe;
197
+ this.available = true;
198
+ log.info('MA-902 GPS Time Server connected via SNMP', {
199
+ host: this.options.host,
200
+ description: probe.description,
201
+ uptime: Math.round(probe.uptimeSeconds) + 's',
202
+ });
203
+ }
204
+
205
+ // Start polling
206
+ await this.poll();
207
+
208
+ if (this.options.pollInterval > 0) {
209
+ this.pollTimer = setInterval(() => this.poll(), this.options.pollInterval);
210
+ }
211
+
212
+ return this.available;
213
+ }
214
+
215
+ /**
216
+ * Stop monitoring
217
+ */
218
+ stop() {
219
+ if (this.pollTimer) {
220
+ clearInterval(this.pollTimer);
221
+ this.pollTimer = null;
222
+ }
223
+ if (this.snmpSession) {
224
+ try { this.snmpSession.close(); } catch (e) { /* ignore */ }
225
+ this.snmpSession = null;
226
+ }
227
+ this.available = false;
228
+ }
229
+
230
+ /**
231
+ * Query basic system info (sysDescr, sysUptime, sysName)
232
+ * @returns {Object|null} System info or null on failure
233
+ */
234
+ async _querySystemInfo() {
235
+ return new Promise((resolve) => {
236
+ const oids = [SYSTEM_OIDS.SYS_DESCR, SYSTEM_OIDS.SYS_UPTIME, SYSTEM_OIDS.SYS_NAME];
237
+
238
+ this.snmpSession.get(oids, (err, varbinds) => {
239
+ if (err) {
240
+ resolve(null);
241
+ return;
242
+ }
243
+
244
+ try {
245
+ resolve({
246
+ description: varbinds[0].value.toString(),
247
+ uptimeSeconds: parseInt(varbinds[1].value) / 100,
248
+ name: varbinds[2].value.toString(),
249
+ });
250
+ } catch (e) {
251
+ resolve(null);
252
+ }
253
+ });
254
+ });
255
+ }
256
+
257
+ /**
258
+ * Poll the MA-902 for current telemetry
259
+ * @returns {Object|null} Telemetry data or null on failure
260
+ */
261
+ async poll() {
262
+ if (!this.snmpSession) return null;
263
+
264
+ const telemetry = await this._queryTelemetry();
265
+
266
+ if (!telemetry) {
267
+ this.consecutiveFailures++;
268
+
269
+ if (this.consecutiveFailures >= this.maxConsecutiveFailures) {
270
+ if (this.available) {
271
+ this.available = false;
272
+ log.warn('MA-902 connection lost after ' + this.consecutiveFailures + ' failures', {
273
+ host: this.options.host,
274
+ });
275
+ this.emit('connectionLost', { host: this.options.host });
276
+ }
277
+ // Recreate session on next attempt
278
+ this._createSession();
279
+ }
280
+
281
+ return null;
282
+ }
283
+
284
+ // Success - reset failure counter
285
+ if (!this.available) {
286
+ this.available = true;
287
+ log.info('MA-902 connection restored', { host: this.options.host });
288
+ this.emit('connectionRestored', { host: this.options.host });
289
+ }
290
+ this.consecutiveFailures = 0;
291
+
292
+ // Detect changes
293
+ const previousTelemetry = this.lastTelemetry;
294
+ this.lastTelemetry = telemetry;
295
+
296
+ // Emit telemetry event
297
+ this.emit('telemetry', telemetry);
298
+
299
+ // Detect significant state changes
300
+ if (previousTelemetry) {
301
+ this._checkStateChanges(previousTelemetry, telemetry);
302
+ }
303
+
304
+ return telemetry;
305
+ }
306
+
307
+ /**
308
+ * Query enterprise OIDs for MA-902 telemetry
309
+ * @returns {Object|null} Parsed telemetry or null on failure
310
+ */
311
+ async _queryTelemetry() {
312
+ return new Promise((resolve) => {
313
+ const oids = Object.values(MA902_OIDS);
314
+
315
+ this.snmpSession.get(oids, (err, varbinds) => {
316
+ if (err) {
317
+ log.debug('SNMP query failed', { error: err.message });
318
+ resolve(null);
319
+ return;
320
+ }
321
+
322
+ try {
323
+ const keys = Object.keys(MA902_OIDS);
324
+ const raw = {};
325
+ varbinds.forEach((vb, i) => {
326
+ if (this.snmpLib.isVarbindError(vb)) {
327
+ raw[keys[i]] = null;
328
+ } else {
329
+ raw[keys[i]] = typeof vb.value === 'object' && Buffer.isBuffer(vb.value)
330
+ ? parseInt(vb.value.toString('hex'), 16)
331
+ : parseInt(vb.value.toString());
332
+ }
333
+ });
334
+
335
+ resolve(this._parseTelemetry(raw));
336
+ } catch (e) {
337
+ log.debug('Telemetry parse error', { error: e.message });
338
+ resolve(null);
339
+ }
340
+ });
341
+ });
342
+ }
343
+
344
+ /**
345
+ * Parse raw SNMP values into structured telemetry
346
+ * @param {Object} raw - Raw OID values keyed by MA902_OIDS names
347
+ * @returns {Object} Structured telemetry
348
+ */
349
+ _parseTelemetry(raw) {
350
+ const gpsTimeUnix = raw.GPS_TIME;
351
+ const systemTimeUnix = Math.floor(Date.now() / 1000);
352
+ const clockDelta = gpsTimeUnix ? Math.abs(systemTimeUnix - gpsTimeUnix) : null;
353
+
354
+ const locked = raw.LOCK_STATUS === 1;
355
+ const satsVisible = raw.SATS_VISIBLE || 0;
356
+ const satsUsed = raw.SATS_USED || 0;
357
+ const satsTracking = raw.SATS_TRACKING || 0;
358
+ const alarmActive = raw.ALARM_STATUS !== 0;
359
+
360
+ // Decode constellation bitmask
361
+ const constellationMask = raw.CONSTELLATION_MASK || 0;
362
+ const constellations = [];
363
+ if (constellationMask & CONSTELLATION_FLAGS.GPS) constellations.push('GPS');
364
+ if (constellationMask & CONSTELLATION_FLAGS.BEIDOU) constellations.push('BeiDou');
365
+ if (constellationMask & CONSTELLATION_FLAGS.GLONASS) constellations.push('GLONASS');
366
+ if (constellationMask & CONSTELLATION_FLAGS.GALILEO) constellations.push('Galileo');
367
+ if (constellationMask & CONSTELLATION_FLAGS.QZSS) constellations.push('QZSS');
368
+
369
+ // Assess satellite quality
370
+ let satQuality;
371
+ if (satsUsed >= SAT_THRESHOLDS.EXCELLENT) satQuality = 'excellent';
372
+ else if (satsUsed >= SAT_THRESHOLDS.GOOD) satQuality = 'good';
373
+ else if (satsUsed >= SAT_THRESHOLDS.MARGINAL) satQuality = 'marginal';
374
+ else if (satsUsed >= SAT_THRESHOLDS.DEGRADED) satQuality = 'degraded';
375
+ else satQuality = 'none';
376
+
377
+ // Determine if GPS time source is trustworthy
378
+ const synchronized = locked && satsUsed >= this.options.minSatellites && !alarmActive;
379
+
380
+ // Build telemetry object
381
+ const telemetry = {
382
+ timestamp: Date.now(),
383
+ host: this.options.host,
384
+
385
+ // Time data
386
+ gpsTime: gpsTimeUnix,
387
+ gpsTimeISO: gpsTimeUnix ? new Date(gpsTimeUnix * 1000).toISOString() : null,
388
+ subSeconds: raw.SUB_SECONDS,
389
+ systemTime: systemTimeUnix,
390
+ clockDeltaSeconds: clockDelta,
391
+
392
+ // Lock & sync
393
+ locked,
394
+ synchronized,
395
+ refSource: raw.REF_SOURCE,
396
+
397
+ // Satellite data
398
+ satellites: {
399
+ visible: satsVisible,
400
+ used: satsUsed,
401
+ tracking: satsTracking,
402
+ quality: satQuality,
403
+ constellations,
404
+ constellationMask,
405
+ },
406
+
407
+ // Health
408
+ alarm: alarmActive,
409
+ alarmCode: raw.ALARM_STATUS,
410
+ qualityIndicator: raw.QUALITY,
411
+ offset: raw.OFFSET,
412
+
413
+ // Trust assessment for MANI integration
414
+ maniTrust: this._assessManiTrust(locked, satsUsed, alarmActive, clockDelta),
415
+ };
416
+
417
+ if (this.options.verbose) {
418
+ log.info('MA-902 telemetry', {
419
+ locked,
420
+ sats: `${satsUsed}/${satsTracking}/${satsVisible}`,
421
+ quality: satQuality,
422
+ constellations: constellations.join('+'),
423
+ delta: clockDelta !== null ? clockDelta + 's' : 'unknown',
424
+ });
425
+ }
426
+
427
+ return telemetry;
428
+ }
429
+
430
+ /**
431
+ * Assess MANI trust level based on MA-902 telemetry
432
+ *
433
+ * This is the key integration point - translating hardware telemetry
434
+ * into the MANI trust hierarchy.
435
+ *
436
+ * @param {boolean} locked - Satellite lock status
437
+ * @param {number} satsUsed - Satellites used in fix
438
+ * @param {boolean} alarm - Alarm active
439
+ * @param {number|null} clockDelta - Seconds between GPS and system time
440
+ * @returns {Object} Trust assessment
441
+ */
442
+ _assessManiTrust(locked, satsUsed, alarm, clockDelta) {
443
+ // GPS trust requires: locked, sufficient satellites, no alarms
444
+ if (!locked || alarm) {
445
+ return {
446
+ trustworthy: false,
447
+ level: 'degraded',
448
+ reason: !locked ? 'No satellite lock' : 'Alarm active',
449
+ confidence: 0,
450
+ };
451
+ }
452
+
453
+ if (satsUsed < SAT_THRESHOLDS.MARGINAL) {
454
+ return {
455
+ trustworthy: false,
456
+ level: 'degraded',
457
+ reason: `Insufficient satellites (${satsUsed} < ${SAT_THRESHOLDS.MARGINAL})`,
458
+ confidence: 0.2,
459
+ };
460
+ }
461
+
462
+ // Clock delta sanity check - GPS leap seconds can cause up to ~37s offset
463
+ // but anything beyond 120s suggests something is wrong
464
+ if (clockDelta !== null && clockDelta > 120) {
465
+ return {
466
+ trustworthy: false,
467
+ level: 'suspect',
468
+ reason: `Clock delta too large (${clockDelta}s)`,
469
+ confidence: 0.1,
470
+ };
471
+ }
472
+
473
+ // Calculate confidence based on satellite count
474
+ const satConfidence = Math.min(1.0, satsUsed / SAT_THRESHOLDS.EXCELLENT);
475
+
476
+ if (satsUsed >= SAT_THRESHOLDS.EXCELLENT) {
477
+ return {
478
+ trustworthy: true,
479
+ level: 'excellent',
480
+ reason: `${satsUsed} satellites, locked, no alarms`,
481
+ confidence: 1.0,
482
+ };
483
+ }
484
+
485
+ if (satsUsed >= SAT_THRESHOLDS.GOOD) {
486
+ return {
487
+ trustworthy: true,
488
+ level: 'good',
489
+ reason: `${satsUsed} satellites, locked`,
490
+ confidence: satConfidence,
491
+ };
492
+ }
493
+
494
+ // Marginal (3-4 sats)
495
+ return {
496
+ trustworthy: true,
497
+ level: 'marginal',
498
+ reason: `${satsUsed} satellites (marginal fix)`,
499
+ confidence: satConfidence,
500
+ };
501
+ }
502
+
503
+ /**
504
+ * Check for significant state changes between polls
505
+ */
506
+ _checkStateChanges(prev, curr) {
507
+ // Lock lost
508
+ if (prev.locked && !curr.locked) {
509
+ log.warn('MA-902: Satellite lock LOST');
510
+ this.emit('lockLost', curr);
511
+ }
512
+
513
+ // Lock acquired
514
+ if (!prev.locked && curr.locked) {
515
+ log.info('MA-902: Satellite lock acquired');
516
+ this.emit('lockAcquired', curr);
517
+ }
518
+
519
+ // Alarm triggered
520
+ if (!prev.alarm && curr.alarm) {
521
+ log.warn('MA-902: Alarm triggered', { code: curr.alarmCode });
522
+ this.emit('alarm', curr);
523
+ }
524
+
525
+ // Alarm cleared
526
+ if (prev.alarm && !curr.alarm) {
527
+ log.info('MA-902: Alarm cleared');
528
+ this.emit('alarmCleared', curr);
529
+ }
530
+
531
+ // Satellite degradation (significant drop)
532
+ if (prev.satellites.used - curr.satellites.used >= 3) {
533
+ log.warn('MA-902: Significant satellite degradation', {
534
+ before: prev.satellites.used,
535
+ after: curr.satellites.used,
536
+ });
537
+ this.emit('satelliteDegradation', {
538
+ before: prev.satellites.used,
539
+ after: curr.satellites.used,
540
+ telemetry: curr,
541
+ });
542
+ }
543
+
544
+ // Trust level change
545
+ if (prev.maniTrust.level !== curr.maniTrust.level) {
546
+ log.info('MA-902: Trust level changed', {
547
+ from: prev.maniTrust.level,
548
+ to: curr.maniTrust.level,
549
+ });
550
+ this.emit('trustChanged', {
551
+ from: prev.maniTrust.level,
552
+ to: curr.maniTrust.level,
553
+ telemetry: curr,
554
+ });
555
+ }
556
+ }
557
+
558
+ /**
559
+ * Get current telemetry (last polled values)
560
+ * @returns {Object|null} Latest telemetry or null if unavailable
561
+ */
562
+ getTelemetry() {
563
+ return this.lastTelemetry;
564
+ }
565
+
566
+ /**
567
+ * Check if the MA-902 is available and responding
568
+ * @returns {boolean}
569
+ */
570
+ isAvailable() {
571
+ return this.available;
572
+ }
573
+
574
+ /**
575
+ * Check if the MA-902 has a valid satellite lock
576
+ * @returns {boolean}
577
+ */
578
+ isLocked() {
579
+ return this.lastTelemetry?.locked ?? false;
580
+ }
581
+
582
+ /**
583
+ * Check if the MA-902 is synchronized (locked + sufficient sats + no alarms)
584
+ * @returns {boolean}
585
+ */
586
+ isSynchronized() {
587
+ return this.lastTelemetry?.synchronized ?? false;
588
+ }
589
+
590
+ /**
591
+ * Get satellite count (used in fix)
592
+ * @returns {number}
593
+ */
594
+ getSatelliteCount() {
595
+ return this.lastTelemetry?.satellites?.used ?? 0;
596
+ }
597
+
598
+ /**
599
+ * Get the MANI trust assessment from latest telemetry
600
+ * @returns {Object} Trust assessment
601
+ */
602
+ getManiTrust() {
603
+ return this.lastTelemetry?.maniTrust ?? {
604
+ trustworthy: false,
605
+ level: 'unavailable',
606
+ reason: 'No telemetry data',
607
+ confidence: 0,
608
+ };
609
+ }
610
+
611
+ /**
612
+ * Get status summary for API responses
613
+ * @returns {Object} Status object
614
+ */
615
+ getStatus() {
616
+ if (!this.available || !this.lastTelemetry) {
617
+ return {
618
+ available: false,
619
+ host: this.options.host,
620
+ reason: 'MA-902 not responding or SNMP disabled',
621
+ };
622
+ }
623
+
624
+ const t = this.lastTelemetry;
625
+ return {
626
+ available: true,
627
+ host: this.options.host,
628
+ locked: t.locked,
629
+ synchronized: t.synchronized,
630
+ satellites: t.satellites,
631
+ gpsTime: t.gpsTimeISO,
632
+ clockDelta: t.clockDeltaSeconds,
633
+ alarm: t.alarm,
634
+ quality: t.qualityIndicator,
635
+ trust: t.maniTrust,
636
+ uptime: this.systemInfo?.uptimeSeconds ?? null,
637
+ lastPoll: t.timestamp,
638
+ };
639
+ }
640
+ }
641
+
642
+ // ============================================================
643
+ // SINGLETON INSTANCE
644
+ // ============================================================
645
+
646
+ let globalMonitor = null;
647
+
648
+ /**
649
+ * Get or create the global MA-902 monitor instance
650
+ * @param {Object} options - Monitor options
651
+ * @returns {MA902Monitor}
652
+ */
653
+ export function getMA902Monitor(options = {}) {
654
+ if (!globalMonitor) {
655
+ globalMonitor = new MA902Monitor(options);
656
+ }
657
+ return globalMonitor;
658
+ }
659
+
660
+ // ============================================================
661
+ // EXPORTS
662
+ // ============================================================
663
+
664
+ export {
665
+ MA902_OIDS,
666
+ MA902_ENTERPRISE_OID,
667
+ CONSTELLATION_FLAGS,
668
+ SAT_THRESHOLDS,
669
+ };
670
+
671
+ export default {
672
+ MA902Monitor,
673
+ getMA902Monitor,
674
+ MA902_OIDS,
675
+ MA902_ENTERPRISE_OID,
676
+ CONSTELLATION_FLAGS,
677
+ SAT_THRESHOLDS,
678
+ };
@@ -14,9 +14,11 @@
14
14
  * @module ModuleSealer
15
15
  */
16
16
 
17
- import { sha3_256, sha3_512 } from '@noble/hashes/sha3.js';
17
+ import { sha3_256 as _nobleSha3, sha3_512 } from '@noble/hashes/sha3.js';
18
18
  import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
19
19
  import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
20
+ // ACCEL: Hardware-accelerated crypto
21
+ import { sha3_256, mlDsa65Sign, mlDsa65Verify } from '../utils/accel.js';
20
22
  import { readFileSync, writeFileSync, existsSync } from 'fs';
21
23
  import { join, dirname } from 'path';
22
24
  import { fileURLToPath } from 'url';
@@ -74,7 +76,7 @@ export class SealedModule {
74
76
  const pubKeyBytes = hexToBytes(attestation.publicKey);
75
77
 
76
78
  // IMPORTANT: ml_dsa65.verify(signature, message, publicKey) - signature FIRST!
77
- return ml_dsa65.verify(sigBytes, messageBytes, pubKeyBytes);
79
+ return mlDsa65Verify(sigBytes, messageBytes, pubKeyBytes);
78
80
  } catch (e) {
79
81
  return false;
80
82
  }
@@ -243,7 +245,7 @@ export class ModuleSealer {
243
245
  const privKeyBytes = hexToBytes(this.privateKey);
244
246
 
245
247
  // IMPORTANT: ml_dsa65.sign(message, secretKey) - message FIRST!
246
- const signature = ml_dsa65.sign(messageBytes, privKeyBytes);
248
+ const signature = mlDsa65Sign(messageBytes, privKeyBytes);
247
249
 
248
250
  return {
249
251
  publicKey: this.publicKey,