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,770 @@
1
+ /**
2
+ * ╔═══════════════════════════════════════════════════════════════════════════════╗
3
+ * ║ 🔗 SANGHA - UNIFIED COMPONENT ATTESTATION 🔗 ║
4
+ * ╠═══════════════════════════════════════════════════════════════════════════════╣
5
+ * ║ ║
6
+ * ║ SANGHA (Sanskrit: संघ) means "community" or "assembly" - the Buddhist ║
7
+ * ║ concept of beings who support each other on the path to enlightenment. ║
8
+ * ║ ║
9
+ * ║ In YAKMESH, SANGHA represents a NOVEL security architecture: ║
10
+ * ║ ║
11
+ * ║ PHILOSOPHY: ║
12
+ * ║ Traditional process isolation SEPARATES components - each stands alone. ║
13
+ * ║ SANGHA UNIFIES components - they protect each other through continuous ║
14
+ * ║ mutual attestation. Like a group of travelers in dangerous terrain, ║
15
+ * ║ strength comes from unity, not isolation. ║
16
+ * ║ ║
17
+ * ║ CORE MECHANISMS: ║
18
+ * ║ 1. SYNAPSE - Cryptographic communication channels between components ║
19
+ * ║ 2. ANTIBODY - Circulating verification routines that check integrity ║
20
+ * ║ 3. TEMPORAL BINDING - Operations bound to GPS time windows ║
21
+ * ║ 4. COLLECTIVE RESPONSE - All components react to any detected anomaly ║
22
+ * ║ ║
23
+ * ║ "No component can be compromised silently - the others would notice." ║
24
+ * ║ ║
25
+ * ╚═══════════════════════════════════════════════════════════════════════════════╝
26
+ *
27
+ * @module security/sangha
28
+ * @version 1.0.0
29
+ * @license MIT
30
+ * @copyright 2026 YAKMESH™ Contributors
31
+ */
32
+
33
+ import { EventEmitter } from 'events';
34
+ import { createHash, randomBytes } from 'crypto';
35
+ import { createLogger } from '../utils/logger.js';
36
+ import { ternaryId } from '../utils/ternary-id.js';
37
+ import { Trit, POSITIVE, NEGATIVE, NEUTRAL } from '../oracle/tribhuj.js';
38
+
39
+ const log = createLogger('security:sangha');
40
+
41
+ // =============================================================================
42
+ // CONSTANTS
43
+ // =============================================================================
44
+
45
+ /**
46
+ * Component identifiers in the SANGHA collective
47
+ * Each component is a member of the assembly
48
+ */
49
+ export const SANGHA_COMPONENT = Object.freeze({
50
+ CRYPTO: 'crypto', // Cryptographic operations (signing, KEM, hashing)
51
+ MESH: 'mesh', // Network layer (WebSocket, gossip, ANNEX)
52
+ ORACLE: 'oracle', // Consensus, time, validation
53
+ ACCEL: 'accel', // Hardware acceleration (GPU/NPU/ONNX)
54
+ HTTP: 'http', // API server and dashboard
55
+ IDENTITY: 'identity', // Key management, DOKO
56
+ });
57
+
58
+ /**
59
+ * Attestation validity windows (milliseconds)
60
+ * Tighter windows for higher-integrity components
61
+ */
62
+ export const ATTESTATION_VALIDITY = Object.freeze({
63
+ [SANGHA_COMPONENT.CRYPTO]: 100, // 100ms - crypto must be real-time
64
+ [SANGHA_COMPONENT.ORACLE]: 200, // 200ms - oracle needs tight time
65
+ [SANGHA_COMPONENT.MESH]: 500, // 500ms - network has latency
66
+ [SANGHA_COMPONENT.ACCEL]: 1000, // 1s - GPU ops can be slow
67
+ [SANGHA_COMPONENT.HTTP]: 2000, // 2s - HTTP is user-facing
68
+ [SANGHA_COMPONENT.IDENTITY]: 100, // 100ms - identity is critical
69
+ });
70
+
71
+ /**
72
+ * Minimum attestations required for collective operations
73
+ * Uses 2-of-3 Byzantine tolerance
74
+ */
75
+ export const QUORUM_THRESHOLD = 3;
76
+ export const QUORUM_MINIMUM = 2;
77
+
78
+ /**
79
+ * SANGHA States (ternary)
80
+ */
81
+ export const SANGHA_STATE = Object.freeze({
82
+ HARMONIOUS: POSITIVE, // +1: All components in agreement
83
+ DISRUPTED: NEGATIVE, // -1: Attestation chain broken
84
+ CONVERGING: NEUTRAL, // 0: Working toward consensus
85
+ });
86
+
87
+ // =============================================================================
88
+ // SYNAPSE - CRYPTOGRAPHIC COMMUNICATION CHANNEL
89
+ // =============================================================================
90
+
91
+ /**
92
+ * Synapse represents a secure, bidirectional communication channel
93
+ * between two SANGHA components.
94
+ *
95
+ * Every message through a synapse is:
96
+ * - Signed by the sender
97
+ * - Timestamped to GPS time
98
+ * - Verified by the receiver
99
+ * - Part of an attestation chain
100
+ */
101
+ export class Synapse {
102
+ #id;
103
+ #componentA;
104
+ #componentB;
105
+ #sharedSecret;
106
+ #messageChain;
107
+ #lastActivity;
108
+ #healthy;
109
+
110
+ /**
111
+ * @param {string} componentA - First component ID
112
+ * @param {string} componentB - Second component ID
113
+ */
114
+ constructor(componentA, componentB) {
115
+ this.#id = `synapse:${componentA}<->${componentB}`;
116
+ this.#componentA = componentA;
117
+ this.#componentB = componentB;
118
+ this.#sharedSecret = randomBytes(32);
119
+ this.#messageChain = []; // Rolling chain of message hashes
120
+ this.#lastActivity = Date.now();
121
+ this.#healthy = true;
122
+
123
+ Object.seal(this);
124
+ }
125
+
126
+ get id() { return this.#id; }
127
+ get componentA() { return this.#componentA; }
128
+ get componentB() { return this.#componentB; }
129
+ get healthy() { return this.#healthy; }
130
+ get lastActivity() { return this.#lastActivity; }
131
+
132
+ /**
133
+ * Generate a message attestation
134
+ * @param {string} sender - Component sending the message
135
+ * @param {Buffer|string} data - Message data
136
+ * @param {number} timestamp - GPS timestamp
137
+ * @returns {{attestation: string, chainHash: string}}
138
+ */
139
+ attest(sender, data, timestamp) {
140
+ if (sender !== this.#componentA && sender !== this.#componentB) {
141
+ throw new Error(`Invalid sender ${sender} for synapse ${this.#id}`);
142
+ }
143
+
144
+ const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(String(data));
145
+
146
+ // Build attestation: hash(sender | data | timestamp | prevChainHash | secret)
147
+ const prevChainHash = this.#messageChain.length > 0
148
+ ? this.#messageChain[this.#messageChain.length - 1]
149
+ : Buffer.alloc(32);
150
+
151
+ const attestation = createHash('sha3-256')
152
+ .update(sender)
153
+ .update(dataBuffer)
154
+ .update(Buffer.from(timestamp.toString()))
155
+ .update(prevChainHash)
156
+ .update(this.#sharedSecret)
157
+ .digest('hex');
158
+
159
+ // Add to chain (rolling window of last 100)
160
+ this.#messageChain.push(Buffer.from(attestation, 'hex'));
161
+ if (this.#messageChain.length > 100) {
162
+ this.#messageChain.shift();
163
+ }
164
+
165
+ this.#lastActivity = Date.now();
166
+
167
+ return {
168
+ attestation,
169
+ chainHash: attestation.slice(0, 16),
170
+ chainLength: this.#messageChain.length,
171
+ };
172
+ }
173
+
174
+ /**
175
+ * Verify a message attestation
176
+ * @param {string} sender - Expected sender
177
+ * @param {Buffer|string} data - Message data
178
+ * @param {number} timestamp - GPS timestamp
179
+ * @param {string} expectedAttestation - Expected attestation hash
180
+ * @returns {boolean}
181
+ */
182
+ verify(sender, data, timestamp, expectedAttestation) {
183
+ try {
184
+ const { attestation } = this.attest(sender, data, timestamp);
185
+ const valid = attestation === expectedAttestation;
186
+
187
+ if (!valid) {
188
+ this.#healthy = false;
189
+ log.warn(`Synapse ${this.#id} attestation mismatch`, {
190
+ expected: expectedAttestation.slice(0, 16),
191
+ got: attestation.slice(0, 16),
192
+ });
193
+ }
194
+
195
+ return valid;
196
+ } catch (e) {
197
+ this.#healthy = false;
198
+ return false;
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Get synapse health metrics
204
+ */
205
+ getHealth() {
206
+ return {
207
+ id: this.#id,
208
+ healthy: this.#healthy,
209
+ chainLength: this.#messageChain.length,
210
+ lastActivity: this.#lastActivity,
211
+ age: Date.now() - this.#lastActivity,
212
+ };
213
+ }
214
+
215
+ /**
216
+ * Reset synapse after recovery
217
+ */
218
+ reset() {
219
+ this.#sharedSecret = randomBytes(32);
220
+ this.#messageChain = [];
221
+ this.#healthy = true;
222
+ this.#lastActivity = Date.now();
223
+ log.info(`Synapse ${this.#id} reset`);
224
+ }
225
+ }
226
+
227
+ // =============================================================================
228
+ // ANTIBODY - CIRCULATING VERIFICATION ROUTINE
229
+ // =============================================================================
230
+
231
+ /**
232
+ * Antibody is a verification routine that circulates between components,
233
+ * collecting attestations and detecting inconsistencies.
234
+ *
235
+ * Like biological antibodies, they:
236
+ * - Patrol continuously
237
+ * - Detect foreign/corrupt elements
238
+ * - Trigger collective immune response
239
+ */
240
+ export class Antibody {
241
+ #id;
242
+ #createdAt;
243
+ #visited;
244
+ #attestations;
245
+ #anomalies;
246
+ #state;
247
+
248
+ constructor() {
249
+ this.#id = ternaryId(8);
250
+ this.#createdAt = Date.now();
251
+ this.#visited = new Set();
252
+ this.#attestations = new Map();
253
+ this.#anomalies = [];
254
+ this.#state = new Trit(NEUTRAL); // Start converging
255
+
256
+ Object.seal(this);
257
+ }
258
+
259
+ get id() { return this.#id; }
260
+ get createdAt() { return this.#createdAt; }
261
+ get visited() { return Array.from(this.#visited); }
262
+ get attestations() { return new Map(this.#attestations); }
263
+ get anomalies() { return [...this.#anomalies]; }
264
+ get state() { return this.#state; }
265
+
266
+ /**
267
+ * Visit a component and collect its attestation
268
+ * @param {string} component - Component ID
269
+ * @param {object} componentState - Component's current state
270
+ * @param {number} timestamp - GPS timestamp
271
+ * @returns {object} - Attestation result
272
+ */
273
+ visit(component, componentState, timestamp) {
274
+ this.#visited.add(component);
275
+
276
+ // Generate attestation from component state
277
+ const attestation = createHash('sha3-256')
278
+ .update(this.#id)
279
+ .update(component)
280
+ .update(JSON.stringify(componentState))
281
+ .update(Buffer.from(timestamp.toString()))
282
+ .digest('hex');
283
+
284
+ this.#attestations.set(component, {
285
+ attestation,
286
+ timestamp,
287
+ stateHash: createHash('sha3-256')
288
+ .update(JSON.stringify(componentState))
289
+ .digest('hex')
290
+ .slice(0, 16),
291
+ });
292
+
293
+ return { component, attestation: attestation.slice(0, 16) };
294
+ }
295
+
296
+ /**
297
+ * Record an anomaly detected during circulation
298
+ * @param {string} component - Component where anomaly detected
299
+ * @param {string} type - Anomaly type
300
+ * @param {object} details - Anomaly details
301
+ */
302
+ recordAnomaly(component, type, details = {}) {
303
+ this.#anomalies.push({
304
+ component,
305
+ type,
306
+ details,
307
+ timestamp: Date.now(),
308
+ });
309
+ this.#state = new Trit(NEGATIVE); // Disrupted
310
+ }
311
+
312
+ /**
313
+ * Finalize circulation and determine collective state
314
+ * @returns {{state: Trit, visited: string[], anomalies: object[]}}
315
+ */
316
+ finalize() {
317
+ if (this.#anomalies.length > 0) {
318
+ this.#state = new Trit(NEGATIVE);
319
+ } else if (this.#visited.size >= QUORUM_MINIMUM) {
320
+ this.#state = new Trit(POSITIVE);
321
+ } else {
322
+ this.#state = new Trit(NEUTRAL);
323
+ }
324
+
325
+ return {
326
+ id: this.#id,
327
+ state: this.#state,
328
+ visited: this.visited,
329
+ attestationCount: this.#attestations.size,
330
+ anomalies: this.#anomalies,
331
+ duration: Date.now() - this.#createdAt,
332
+ };
333
+ }
334
+ }
335
+
336
+ // =============================================================================
337
+ // SANGHA - THE UNIFIED COLLECTIVE
338
+ // =============================================================================
339
+
340
+ /**
341
+ * SANGHA manages the collective of components and their mutual attestations.
342
+ *
343
+ * It maintains:
344
+ * - Synapses between all component pairs
345
+ * - Circulating antibodies for continuous verification
346
+ * - Temporal binding to GPS time
347
+ * - Collective response mechanisms
348
+ */
349
+ export class Sangha extends EventEmitter {
350
+ #components;
351
+ #synapses;
352
+ #antibodies;
353
+ #timeSource;
354
+ #state;
355
+ #lastCirculation;
356
+ #circulationInterval;
357
+ #circulationIntervalMs;
358
+ #started;
359
+
360
+ constructor() {
361
+ super();
362
+
363
+ this.#components = new Map();
364
+ this.#synapses = new Map();
365
+ this.#antibodies = [];
366
+ this.#timeSource = null;
367
+ this.#state = new Trit(NEUTRAL);
368
+ this.#lastCirculation = 0;
369
+ this.#circulationInterval = null;
370
+ this.#circulationIntervalMs = 5000;
371
+ this.#started = false;
372
+
373
+ Object.seal(this);
374
+ }
375
+
376
+ // ─────────────────────────────────────────────────────────────────────────
377
+ // Component Registration
378
+ // ─────────────────────────────────────────────────────────────────────────
379
+
380
+ /**
381
+ * Register a component with the SANGHA collective
382
+ * @param {string} componentId - Component identifier
383
+ * @param {object} componentRef - Reference to component instance
384
+ * @param {function} stateGetter - Function to get component's current state
385
+ */
386
+ register(componentId, componentRef, stateGetter) {
387
+ if (this.#components.has(componentId)) {
388
+ log.warn(`Component ${componentId} already registered, updating`);
389
+ }
390
+
391
+ this.#components.set(componentId, {
392
+ id: componentId,
393
+ ref: componentRef,
394
+ getState: stateGetter,
395
+ registeredAt: Date.now(),
396
+ lastAttestation: 0,
397
+ });
398
+
399
+ // Create synapses to all existing components
400
+ for (const [existingId] of this.#components) {
401
+ if (existingId !== componentId) {
402
+ const synapseId = this.#makeSynapseId(componentId, existingId);
403
+ if (!this.#synapses.has(synapseId)) {
404
+ this.#synapses.set(synapseId, new Synapse(componentId, existingId));
405
+ log.debug(`Created synapse ${synapseId}`);
406
+ }
407
+ }
408
+ }
409
+
410
+ log.info(`Component ${componentId} joined SANGHA`, {
411
+ totalComponents: this.#components.size,
412
+ totalSynapses: this.#synapses.size,
413
+ });
414
+
415
+ this.emit('componentJoined', componentId);
416
+ return this;
417
+ }
418
+
419
+ /**
420
+ * Unregister a component
421
+ * @param {string} componentId
422
+ */
423
+ unregister(componentId) {
424
+ if (!this.#components.has(componentId)) return;
425
+
426
+ this.#components.delete(componentId);
427
+
428
+ // Remove associated synapses
429
+ for (const [synapseId] of this.#synapses) {
430
+ if (synapseId.includes(componentId)) {
431
+ this.#synapses.delete(synapseId);
432
+ }
433
+ }
434
+
435
+ log.info(`Component ${componentId} left SANGHA`);
436
+ this.emit('componentLeft', componentId);
437
+ }
438
+
439
+ // ─────────────────────────────────────────────────────────────────────────
440
+ // Time Source Binding
441
+ // ─────────────────────────────────────────────────────────────────────────
442
+
443
+ /**
444
+ * Bind to a time source for temporal attestations
445
+ * @param {object} timeSource - MANI time source instance
446
+ */
447
+ bindTimeSource(timeSource) {
448
+ this.#timeSource = timeSource;
449
+ log.info('SANGHA bound to time source', {
450
+ type: timeSource?.getSourceType?.() || 'unknown',
451
+ });
452
+ }
453
+
454
+ /**
455
+ * Get current GPS timestamp for attestations
456
+ * @returns {number}
457
+ */
458
+ getTimestamp() {
459
+ if (this.#timeSource?.getCurrentTime) {
460
+ return this.#timeSource.getCurrentTime();
461
+ }
462
+ return Date.now();
463
+ }
464
+
465
+ // ─────────────────────────────────────────────────────────────────────────
466
+ // Synapse Operations
467
+ // ─────────────────────────────────────────────────────────────────────────
468
+
469
+ /**
470
+ * Create standardized synapse ID from two component IDs
471
+ */
472
+ #makeSynapseId(a, b) {
473
+ return a < b ? `${a}<->${b}` : `${b}<->${a}`;
474
+ }
475
+
476
+ /**
477
+ * Get synapse between two components
478
+ * @param {string} componentA
479
+ * @param {string} componentB
480
+ * @returns {Synapse|null}
481
+ */
482
+ getSynapse(componentA, componentB) {
483
+ return this.#synapses.get(this.#makeSynapseId(componentA, componentB));
484
+ }
485
+
486
+ /**
487
+ * Send an attested message between components
488
+ * @param {string} from - Sender component
489
+ * @param {string} to - Receiver component
490
+ * @param {string} action - Action/method name
491
+ * @param {any} payload - Message payload
492
+ * @returns {object} - Attested message envelope
493
+ */
494
+ attestedCall(from, to, action, payload) {
495
+ const synapse = this.getSynapse(from, to);
496
+ if (!synapse) {
497
+ throw new Error(`No synapse between ${from} and ${to}`);
498
+ }
499
+
500
+ const timestamp = this.getTimestamp();
501
+ const validity = ATTESTATION_VALIDITY[from] || 1000;
502
+
503
+ const messageData = JSON.stringify({ action, payload });
504
+ const { attestation, chainHash } = synapse.attest(from, messageData, timestamp);
505
+
506
+ return {
507
+ from,
508
+ to,
509
+ action,
510
+ payload,
511
+ timestamp,
512
+ validUntil: timestamp + validity,
513
+ attestation,
514
+ chainHash,
515
+ };
516
+ }
517
+
518
+ /**
519
+ * Verify an attested message
520
+ * @param {object} envelope - Message envelope from attestedCall
521
+ * @returns {boolean}
522
+ */
523
+ verifyAttestedCall(envelope) {
524
+ const { from, to, action, payload, timestamp, validUntil, attestation } = envelope;
525
+
526
+ // Check temporal validity
527
+ const now = this.getTimestamp();
528
+ if (now > validUntil) {
529
+ log.warn('Attested call expired', { from, to, action, expired: now - validUntil });
530
+ return false;
531
+ }
532
+
533
+ const synapse = this.getSynapse(from, to);
534
+ if (!synapse) {
535
+ log.warn('No synapse for verification', { from, to });
536
+ return false;
537
+ }
538
+
539
+ const messageData = JSON.stringify({ action, payload });
540
+ return synapse.verify(from, messageData, timestamp, attestation);
541
+ }
542
+
543
+ // ─────────────────────────────────────────────────────────────────────────
544
+ // Antibody Circulation
545
+ // ─────────────────────────────────────────────────────────────────────────
546
+
547
+ /**
548
+ * Circulate an antibody through all components
549
+ * @returns {Promise<object>} - Circulation result
550
+ */
551
+ async circulate() {
552
+ const antibody = new Antibody();
553
+ const timestamp = this.getTimestamp();
554
+
555
+ log.debug(`Antibody ${antibody.id} starting circulation`);
556
+
557
+ for (const [componentId, component] of this.#components) {
558
+ try {
559
+ const state = await component.getState();
560
+ antibody.visit(componentId, state, timestamp);
561
+
562
+ // Check attestation age - use 2x circulation interval as threshold
563
+ // (ATTESTATION_VALIDITY is for message expiry, not component liveness)
564
+ const age = timestamp - component.lastAttestation;
565
+ const maxAge = this.#circulationIntervalMs * 2;
566
+
567
+ if (component.lastAttestation > 0 && age > maxAge) {
568
+ antibody.recordAnomaly(componentId, 'STALE_ATTESTATION', {
569
+ age,
570
+ maxAge,
571
+ });
572
+ }
573
+
574
+ component.lastAttestation = timestamp;
575
+ } catch (e) {
576
+ antibody.recordAnomaly(componentId, 'STATE_UNAVAILABLE', {
577
+ error: e.message,
578
+ });
579
+ }
580
+ }
581
+
582
+ const result = antibody.finalize();
583
+
584
+ // Store for analysis (rolling window)
585
+ this.#antibodies.push(result);
586
+ if (this.#antibodies.length > 100) {
587
+ this.#antibodies.shift();
588
+ }
589
+
590
+ // Update collective state
591
+ this.#state = result.state;
592
+ this.#lastCirculation = timestamp;
593
+
594
+ // Emit events based on result
595
+ if (result.anomalies.length > 0) {
596
+ log.warn(`Antibody ${antibody.id} detected ${result.anomalies.length} anomalies`);
597
+ this.emit('anomalyDetected', result.anomalies);
598
+ this.#triggerCollectiveResponse(result.anomalies);
599
+ } else {
600
+ this.emit('circulationComplete', result);
601
+ }
602
+
603
+ return result;
604
+ }
605
+
606
+ /**
607
+ * Trigger collective response to anomalies
608
+ * All components are notified and can take defensive action
609
+ */
610
+ #triggerCollectiveResponse(anomalies) {
611
+ log.error('🚨 SANGHA COLLECTIVE RESPONSE TRIGGERED', {
612
+ anomalyCount: anomalies.length,
613
+ types: anomalies.map(a => a.type),
614
+ });
615
+
616
+ this.emit('collectiveResponse', {
617
+ timestamp: this.getTimestamp(),
618
+ anomalies,
619
+ action: 'ALERT', // Could be: ALERT, ISOLATE, SHUTDOWN
620
+ });
621
+
622
+ // Notify all components
623
+ for (const [componentId, component] of this.#components) {
624
+ if (component.ref?.onSanghaAlert) {
625
+ try {
626
+ component.ref.onSanghaAlert(anomalies);
627
+ } catch (e) {
628
+ log.error(`Failed to alert ${componentId}`, { error: e.message });
629
+ }
630
+ }
631
+ }
632
+ }
633
+
634
+ // ─────────────────────────────────────────────────────────────────────────
635
+ // Lifecycle
636
+ // ─────────────────────────────────────────────────────────────────────────
637
+
638
+ /**
639
+ * Start the SANGHA collective
640
+ * @param {object} options
641
+ * @param {number} options.circulationIntervalMs - How often to circulate antibodies
642
+ */
643
+ start({ circulationIntervalMs = 5000 } = {}) {
644
+ if (this.#started) return;
645
+
646
+ this.#started = true;
647
+ this.#circulationIntervalMs = circulationIntervalMs;
648
+ this.#circulationInterval = setInterval(() => {
649
+ this.circulate().catch(e => {
650
+ log.error('Circulation failed', { error: e.message });
651
+ });
652
+ }, circulationIntervalMs);
653
+
654
+ log.info('SANGHA collective started', {
655
+ components: this.#components.size,
656
+ synapses: this.#synapses.size,
657
+ circulationInterval: circulationIntervalMs,
658
+ });
659
+
660
+ // Initial circulation
661
+ this.circulate();
662
+
663
+ this.emit('started');
664
+ }
665
+
666
+ /**
667
+ * Stop the SANGHA collective
668
+ */
669
+ stop() {
670
+ if (!this.#started) return;
671
+
672
+ this.#started = false;
673
+ if (this.#circulationInterval) {
674
+ clearInterval(this.#circulationInterval);
675
+ this.#circulationInterval = null;
676
+ }
677
+
678
+ log.info('SANGHA collective stopped');
679
+ this.emit('stopped');
680
+ }
681
+
682
+ // ─────────────────────────────────────────────────────────────────────────
683
+ // Status & Diagnostics
684
+ // ─────────────────────────────────────────────────────────────────────────
685
+
686
+ /**
687
+ * Get collective health status
688
+ */
689
+ getStatus() {
690
+ const synapseHealth = [];
691
+ for (const [id, synapse] of this.#synapses) {
692
+ synapseHealth.push(synapse.getHealth());
693
+ }
694
+
695
+ const recentAnomalies = this.#antibodies
696
+ .slice(-10)
697
+ .flatMap(a => a.anomalies);
698
+
699
+ return {
700
+ state: this.#state.value,
701
+ stateLabel: this.#state.value === 1 ? 'HARMONIOUS' :
702
+ this.#state.value === -1 ? 'DISRUPTED' : 'CONVERGING',
703
+ started: this.#started,
704
+ components: Array.from(this.#components.keys()),
705
+ componentCount: this.#components.size,
706
+ synapseCount: this.#synapses.size,
707
+ synapseHealth,
708
+ lastCirculation: this.#lastCirculation,
709
+ circulationCount: this.#antibodies.length,
710
+ recentAnomalies,
711
+ healthy: this.#state.value >= 0 && recentAnomalies.length === 0,
712
+ };
713
+ }
714
+
715
+ /**
716
+ * Get recent antibody results
717
+ * @param {number} count
718
+ */
719
+ getRecentCirculations(count = 10) {
720
+ return this.#antibodies.slice(-count);
721
+ }
722
+ }
723
+
724
+ // =============================================================================
725
+ // SINGLETON & EXPORTS
726
+ // =============================================================================
727
+
728
+ let sanghaInstance = null;
729
+
730
+ /**
731
+ * Get the global SANGHA instance
732
+ * @returns {Sangha}
733
+ */
734
+ export function getSangha() {
735
+ if (!sanghaInstance) {
736
+ sanghaInstance = new Sangha();
737
+ }
738
+ return sanghaInstance;
739
+ }
740
+
741
+ /**
742
+ * Register a component with the global SANGHA
743
+ * @param {string} componentId
744
+ * @param {object} componentRef
745
+ * @param {function} stateGetter
746
+ */
747
+ export function joinSangha(componentId, componentRef, stateGetter) {
748
+ return getSangha().register(componentId, componentRef, stateGetter);
749
+ }
750
+
751
+ /**
752
+ * Create an attested call between components
753
+ * @param {string} from
754
+ * @param {string} to
755
+ * @param {string} action
756
+ * @param {any} payload
757
+ */
758
+ export function attestedCall(from, to, action, payload) {
759
+ return getSangha().attestedCall(from, to, action, payload);
760
+ }
761
+
762
+ /**
763
+ * Verify an attested call
764
+ * @param {object} envelope
765
+ */
766
+ export function verifyAttestedCall(envelope) {
767
+ return getSangha().verifyAttestedCall(envelope);
768
+ }
769
+
770
+ export default Sangha;