yakmesh 2.9.0 → 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 (225) hide show
  1. package/CHANGELOG.md +637 -0
  2. package/Caddyfile +77 -0
  3. package/README.md +119 -29
  4. package/content/api.js +50 -41
  5. package/content/index.js +1 -2
  6. package/content/store.js +323 -177
  7. package/dashboard/index.html +19 -3
  8. package/database/replication.js +117 -37
  9. package/docs/CRYPTO-AGILITY.md +204 -0
  10. package/docs/MTLS-RESEARCH.md +367 -0
  11. package/docs/NAMCHE-SPEC.md +681 -0
  12. package/docs/PEERQUANTA-YAKMESH-INTEGRATION.md +407 -0
  13. package/docs/PRECISION-DISCLOSURE.md +96 -0
  14. package/docs/README.md +76 -0
  15. package/docs/ROADMAP-2.4.0.md +447 -0
  16. package/docs/ROADMAP-2.5.0.md +244 -0
  17. package/docs/SECURITY-AUDIT-REPORT.md +306 -0
  18. package/docs/SST-INTEGRATION.md +712 -0
  19. package/docs/STEADYWATCH-IMPLEMENTATION.md +303 -0
  20. package/docs/TERNARY-AUDIT-REPORT.md +247 -0
  21. package/docs/TME-FAQ.md +221 -0
  22. package/docs/WHITEPAPER.md +623 -0
  23. package/docs/adapters.html +1001 -0
  24. package/docs/advanced-systems.html +1045 -0
  25. package/docs/annex.html +1046 -0
  26. package/docs/api.html +970 -0
  27. package/docs/business/response-templates.md +160 -0
  28. package/docs/c2c.html +1225 -0
  29. package/docs/cli.html +1332 -0
  30. package/docs/configuration.html +1248 -0
  31. package/docs/darshan.html +1085 -0
  32. package/docs/dharma.html +966 -0
  33. package/docs/docs-bundle.html +1075 -0
  34. package/docs/docs.css +3120 -0
  35. package/docs/docs.js +556 -0
  36. package/docs/doko.html +969 -0
  37. package/docs/geo-proof.html +858 -0
  38. package/docs/getting-started.html +840 -0
  39. package/docs/gumba-tutorial.html +1144 -0
  40. package/docs/gumba.html +1098 -0
  41. package/docs/index.html +914 -0
  42. package/docs/jhilke.html +1312 -0
  43. package/docs/karma.html +1100 -0
  44. package/docs/katha.html +1037 -0
  45. package/docs/lama.html +978 -0
  46. package/docs/mandala.html +1067 -0
  47. package/docs/mani.html +964 -0
  48. package/docs/mantra.html +967 -0
  49. package/docs/mesh.html +1409 -0
  50. package/docs/nakpak.html +869 -0
  51. package/docs/namche.html +928 -0
  52. package/docs/nav-order.json +53 -0
  53. package/docs/prahari.html +1043 -0
  54. package/docs/prism-bash.min.js +1 -0
  55. package/docs/prism-javascript.min.js +1 -0
  56. package/docs/prism-json.min.js +1 -0
  57. package/docs/prism-tomorrow.min.css +1 -0
  58. package/docs/prism.min.js +1 -0
  59. package/docs/privacy.html +699 -0
  60. package/docs/quick-reference.html +1181 -0
  61. package/docs/sakshi.html +1402 -0
  62. package/docs/sandboxing.md +386 -0
  63. package/docs/seva.html +911 -0
  64. package/docs/sherpa.html +871 -0
  65. package/docs/studio.html +860 -0
  66. package/docs/stupa.html +995 -0
  67. package/docs/tailwind.min.css +2 -0
  68. package/docs/tattva.html +1332 -0
  69. package/docs/terms.html +686 -0
  70. package/docs/time-server-deployment.md +166 -0
  71. package/docs/time-sources.html +1392 -0
  72. package/docs/tivra.html +1127 -0
  73. package/docs/trademark-policy.html +686 -0
  74. package/docs/tribhuj.html +1183 -0
  75. package/docs/trust-security.html +1029 -0
  76. package/docs/tutorials/backup-recovery.html +654 -0
  77. package/docs/tutorials/dashboard.html +604 -0
  78. package/docs/tutorials/domain-setup.html +605 -0
  79. package/docs/tutorials/host-website.html +456 -0
  80. package/docs/tutorials/mesh-network.html +505 -0
  81. package/docs/tutorials/mobile-access.html +445 -0
  82. package/docs/tutorials/privacy.html +467 -0
  83. package/docs/tutorials/raspberry-pi.html +600 -0
  84. package/docs/tutorials/security-basics.html +539 -0
  85. package/docs/tutorials/share-files.html +431 -0
  86. package/docs/tutorials/troubleshooting.html +637 -0
  87. package/docs/tutorials/trust-karma.html +419 -0
  88. package/docs/tutorials/yak-protocol.html +456 -0
  89. package/docs/tutorials.html +1034 -0
  90. package/docs/vani.html +1270 -0
  91. package/docs/webserver.html +809 -0
  92. package/docs/yak-protocol.html +940 -0
  93. package/docs/yak-timeserver-design.md +475 -0
  94. package/docs/yakapp.html +1015 -0
  95. package/docs/ypc27.html +1069 -0
  96. package/docs/yurt.html +1344 -0
  97. package/embedded-docs/bundle.js +274 -114
  98. package/gossip/protocol.js +247 -27
  99. package/identity/key-resolver.js +262 -0
  100. package/identity/machine-seed.js +632 -0
  101. package/identity/node-key.js +669 -368
  102. package/identity/tribhuj-ratchet.js +506 -0
  103. package/knowledge-base.js +37 -8
  104. package/launcher/yakmesh.bat +62 -0
  105. package/launcher/yakmesh.sh +70 -0
  106. package/mesh/annex.js +462 -108
  107. package/mesh/beacon-broadcast.js +4 -1
  108. package/mesh/darshan.js +17 -5
  109. package/mesh/gumba.js +47 -13
  110. package/mesh/jhilke.js +651 -0
  111. package/mesh/katha.js +5 -2
  112. package/mesh/nakpak-routing.js +8 -5
  113. package/mesh/network.js +724 -34
  114. package/mesh/pulse-sync.js +4 -1
  115. package/mesh/seva.js +526 -0
  116. package/mesh/sherpa-discovery.js +89 -8
  117. package/mesh/sybil-defense.js +19 -5
  118. package/mesh/temporal-encoder.js +4 -3
  119. package/mesh/yurt.js +72 -17
  120. package/models/entropy-sentinel.onnx +0 -0
  121. package/models/karma-trust.onnx +0 -0
  122. package/models/manifest.json +43 -0
  123. package/models/sakshi-anomaly.onnx +0 -0
  124. package/oracle/code-proof-protocol.js +7 -6
  125. package/oracle/codebase-lock.js +257 -28
  126. package/oracle/index.js +74 -15
  127. package/oracle/ma902-snmp.js +678 -0
  128. package/oracle/module-sealer.js +5 -3
  129. package/oracle/packet-checksum.js +201 -0
  130. package/oracle/ternary-144t.js +714 -0
  131. package/oracle/ternary-ml.js +481 -0
  132. package/oracle/time-api.js +239 -0
  133. package/oracle/time-source.js +137 -47
  134. package/oracle/validation-oracle-hardened.js +1111 -1071
  135. package/oracle/validation-oracle.js +4 -2
  136. package/oracle/ypc27.js +211 -0
  137. package/package.json +20 -3
  138. package/protocol/yak-handler.js +35 -9
  139. package/protocol/yak-protocol.js +6 -5
  140. package/reference/cpp/yakmesh_mceliece_shard.cpp +168 -0
  141. package/reference/cpp/yakmesh_ypc27.cpp +179 -0
  142. package/sbom.json +87 -0
  143. package/scripts/security-audit.mjs +264 -0
  144. package/scripts/update-docs-sidebar.cjs +164 -0
  145. package/security/crypto-config.js +4 -3
  146. package/security/dharma-moderation.js +4 -3
  147. package/security/doko-identity.js +193 -143
  148. package/security/domain-consensus.js +86 -85
  149. package/security/fs-hardening.js +620 -0
  150. package/security/hardware-attestation.js +5 -3
  151. package/security/hybrid-trust.js +227 -87
  152. package/security/karma-rate-limiter.js +692 -0
  153. package/security/khata-protocol.js +22 -21
  154. package/security/khata-trust-integration.js +277 -150
  155. package/security/memory-safety.js +635 -0
  156. package/security/mesh-auth.js +11 -10
  157. package/security/mesh-revocation.js +18 -5
  158. package/security/namche-gateway.js +298 -69
  159. package/security/sakshi.js +102 -3
  160. package/security/sangha.js +770 -0
  161. package/security/secure-config.js +473 -0
  162. package/security/silicon-parity.js +13 -10
  163. package/security/steadywatch.js +1142 -0
  164. package/security/strike-system.js +32 -3
  165. package/security/temporal-signing.js +488 -0
  166. package/security/trit-commitment.js +464 -0
  167. package/server/crypto/annex.js +247 -0
  168. package/server/darshan-api.js +343 -0
  169. package/server/index.js +3259 -362
  170. package/server/komm-api.js +668 -0
  171. package/utils/accel.js +2273 -0
  172. package/utils/ternary-id.js +79 -0
  173. package/utils/verify-worker.js +57 -0
  174. package/webserver/index.js +95 -5
  175. package/assets/yakmesh-logo.png +0 -0
  176. package/assets/yakmesh-logo.svg +0 -80
  177. package/assets/yakmesh-logo2.png +0 -0
  178. package/assets/yakmesh-logo2sm.png +0 -0
  179. package/assets/ymsm.png +0 -0
  180. package/scripts/update-docs-nav.cjs +0 -194
  181. package/update-docs-nav.cjs +0 -18
  182. package/update-nav.ps1 +0 -16
  183. package/website/assets/silhouettes/adapters.svg +0 -107
  184. package/website/assets/silhouettes/api-endpoints.svg +0 -115
  185. package/website/assets/silhouettes/atomic-clock.svg +0 -83
  186. package/website/assets/silhouettes/base-camp.svg +0 -81
  187. package/website/assets/silhouettes/bridge.svg +0 -69
  188. package/website/assets/silhouettes/docs-bundle.svg +0 -113
  189. package/website/assets/silhouettes/doko-basket.svg +0 -70
  190. package/website/assets/silhouettes/fortress.svg +0 -93
  191. package/website/assets/silhouettes/gateway.svg +0 -54
  192. package/website/assets/silhouettes/gears.svg +0 -93
  193. package/website/assets/silhouettes/globe-satellite.svg +0 -67
  194. package/website/assets/silhouettes/karma-wheel.svg +0 -137
  195. package/website/assets/silhouettes/lama-council.svg +0 -141
  196. package/website/assets/silhouettes/mandala-network.svg +0 -169
  197. package/website/assets/silhouettes/mani-stones.svg +0 -149
  198. package/website/assets/silhouettes/mantra-wheel.svg +0 -116
  199. package/website/assets/silhouettes/mesh-nodes.svg +0 -113
  200. package/website/assets/silhouettes/nakpak.svg +0 -56
  201. package/website/assets/silhouettes/peak-lightning.svg +0 -73
  202. package/website/assets/silhouettes/sherpa.svg +0 -69
  203. package/website/assets/silhouettes/stupa-tower.svg +0 -119
  204. package/website/assets/silhouettes/tattva-eye.svg +0 -78
  205. package/website/assets/silhouettes/terminal.svg +0 -74
  206. package/website/assets/silhouettes/webserver.svg +0 -145
  207. package/website/assets/silhouettes/yak.svg +0 -78
  208. package/website/assets/yakmesh-logo.png +0 -0
  209. package/website/assets/yakmesh-logo.webp +0 -0
  210. package/website/assets/yakmesh-logo128x140.webp +0 -0
  211. package/website/assets/yakmesh-logo2.png +0 -0
  212. package/website/assets/yakmesh-logo2.svg +0 -51
  213. package/website/assets/yakmesh-logo40x44.webp +0 -0
  214. package/website/assets/yakmesh.gif +0 -0
  215. package/website/assets/yakmesh.ico +0 -0
  216. package/website/assets/yakmesh.jpg +0 -0
  217. package/website/assets/yakmesh.pdf +0 -0
  218. package/website/assets/yakmesh.png +0 -0
  219. package/website/assets/yakmesh.svg +0 -70
  220. package/website/assets/yakmesh128.webp +0 -0
  221. package/website/assets/yakmesh32.png +0 -0
  222. package/website/assets/yakmesh32.svg +0 -65
  223. package/website/assets/yakmesh32o.ico +0 -2
  224. package/website/assets/yakmesh32o.svg +0 -65
  225. package/website/assets/yakmesh32o.svgz +0 -0
@@ -22,6 +22,7 @@
22
22
  import { sha3_256 } from '@noble/hashes/sha3.js';
23
23
  import { bytesToHex, randomBytes, utf8ToBytes } from '@noble/hashes/utils.js';
24
24
  import { EventEmitter } from 'events';
25
+ const peerTag = (id) => id?.split('-pq-').pop() || id?.slice?.(-8) || String(id);
25
26
 
26
27
  // v2.7.0 Ternary Math Integration (TRIBHUJ)
27
28
  import { Trit, TritState, calculatePathBalance, POSITIVE, NEUTRAL, NEGATIVE } from '../oracle/tribhuj.js';
@@ -257,6 +258,11 @@ class BeaconMessage {
257
258
  this.timestamp = options.timestamp || Date.now();
258
259
  this.ttl = options.ttl || 3600; // 1 hour default TTL
259
260
 
261
+ // Explicit reachable endpoints (override auto-constructed from ports)
262
+ // These let nodes behind firewalls/NAT advertise their actual connectable URLs
263
+ this.wsEndpoint = options.wsEndpoint || null; // e.g., "wss://mesh.yakmesh.dev"
264
+ this.relayEndpoint = options.relayEndpoint || null; // e.g., "https://yakmesh.dev/mesh/relay"
265
+
260
266
  // Node capabilities
261
267
  this.capabilities = {
262
268
  wsPort: options.wsPort || null,
@@ -336,6 +342,7 @@ class BeaconMessage {
336
342
  nodeId: peerInfo.nodeId,
337
343
  endpoint: peerInfo.endpoint, // e.g., "https://example.com"
338
344
  wsEndpoint: peerInfo.wsEndpoint, // e.g., "wss://example.com:9001"
345
+ relayEndpoint: peerInfo.relayEndpoint || null, // e.g., "https://example.com/mesh/relay"
339
346
  lastSeen: peerInfo.lastSeen || Date.now(),
340
347
  score: peerInfo.score || 1.0,
341
348
  networkName: peerInfo.networkName,
@@ -363,7 +370,7 @@ class BeaconMessage {
363
370
  * Serialize beacon for HTTP response
364
371
  */
365
372
  serialize() {
366
- return {
373
+ const data = {
367
374
  version: this.version,
368
375
  nodeId: this.nodeId,
369
376
  networkName: this.networkName,
@@ -376,6 +383,12 @@ class BeaconMessage {
376
383
  publicKey: this.publicKey,
377
384
  signature: this.signature,
378
385
  };
386
+
387
+ // Include explicit endpoints if configured (firewall/NAT traversal)
388
+ if (this.wsEndpoint) data.wsEndpoint = this.wsEndpoint;
389
+ if (this.relayEndpoint) data.relayEndpoint = this.relayEndpoint;
390
+
391
+ return data;
379
392
  }
380
393
 
381
394
  /**
@@ -390,6 +403,9 @@ class BeaconMessage {
390
403
  capabilities: this.capabilities,
391
404
  geo: this.geo, // v2.5.0 include geo in signature
392
405
  namche: this.namche, // Include NAMCHE in signature
406
+ // Include endpoints in signature to prevent tampering
407
+ wsEndpoint: this.wsEndpoint || undefined,
408
+ relayEndpoint: this.relayEndpoint || undefined,
393
409
  });
394
410
  }
395
411
 
@@ -402,6 +418,9 @@ class BeaconMessage {
402
418
  networkName: data.networkName,
403
419
  timestamp: data.timestamp,
404
420
  ttl: data.ttl,
421
+ // Explicit reachable endpoints (must match what was signed)
422
+ wsEndpoint: data.wsEndpoint || null,
423
+ relayEndpoint: data.relayEndpoint || null,
405
424
  wsPort: data.capabilities?.wsPort,
406
425
  httpPort: data.capabilities?.httpPort,
407
426
  supportsAnnex: data.capabilities?.supportsAnnex,
@@ -466,6 +485,8 @@ class PeerRegistry {
466
485
  // Update existing peer
467
486
  existing.endpoint = peerInfo.endpoint || existing.endpoint;
468
487
  existing.wsEndpoint = peerInfo.wsEndpoint || existing.wsEndpoint;
488
+ existing.relayEndpoint = peerInfo.relayEndpoint || existing.relayEndpoint;
489
+ existing.publicKey = peerInfo.publicKey || existing.publicKey;
469
490
  existing.lastSeen = Math.max(existing.lastSeen, peerInfo.lastSeen || Date.now());
470
491
  existing.score = Math.min(1.0, existing.score + SHERPA_CONFIG.successBonus);
471
492
  existing.capabilities = peerInfo.capabilities || existing.capabilities;
@@ -479,6 +500,8 @@ class PeerRegistry {
479
500
  nodeId: peerInfo.nodeId,
480
501
  endpoint: peerInfo.endpoint,
481
502
  wsEndpoint: peerInfo.wsEndpoint,
503
+ relayEndpoint: peerInfo.relayEndpoint || null,
504
+ publicKey: peerInfo.publicKey || null,
482
505
  lastSeen: peerInfo.lastSeen || Date.now(),
483
506
  score: peerInfo.score || 1.0,
484
507
  networkName: peerInfo.networkName,
@@ -595,6 +618,7 @@ class SherpaDiscovery extends EventEmitter {
595
618
  // Our own endpoint info
596
619
  this.selfEndpoint = options.selfEndpoint || null; // e.g., "https://mynode.com"
597
620
  this.wsEndpoint = options.wsEndpoint || null;
621
+ this.relayEndpoint = options.relayEndpoint || null; // e.g., "https://mynode.com/mesh/relay"
598
622
  this.capabilities = options.capabilities || {};
599
623
 
600
624
  // v2.5.0 Geographic proof configuration
@@ -688,6 +712,9 @@ class SherpaDiscovery extends EventEmitter {
688
712
  supportsNakpak: this.capabilities.supportsNakpak ?? true,
689
713
  supportsGossip: this.capabilities.supportsGossip ?? true,
690
714
  publicKey: this.publicKey,
715
+ // Explicit reachable endpoints (firewall/NAT traversal)
716
+ wsEndpoint: this.wsEndpoint,
717
+ relayEndpoint: this.relayEndpoint,
691
718
  // v2.5.0 Geographic coordinates (if configured)
692
719
  supportsGeoProof: this.geoConfig.enabled,
693
720
  geoLat: this.geoConfig.lat,
@@ -745,12 +772,16 @@ class SherpaDiscovery extends EventEmitter {
745
772
 
746
773
  // Add the beacon source as a peer
747
774
  if (beacon.nodeId && beacon.nodeId !== this.nodeId) {
775
+ // Prefer explicit wsEndpoint from beacon (firewall/NAT aware)
776
+ // Fall back to auto-constructed from hostname:wsPort
777
+ const autoWsEndpoint = beacon.capabilities?.wsPort
778
+ ? `wss://${new URL(endpoint).hostname}:${beacon.capabilities.wsPort}`
779
+ : null;
748
780
  this.registry.upsert({
749
781
  nodeId: beacon.nodeId,
750
782
  endpoint: endpoint,
751
- wsEndpoint: beacon.capabilities?.wsPort
752
- ? `wss://${new URL(endpoint).hostname}:${beacon.capabilities.wsPort}`
753
- : null,
783
+ wsEndpoint: beacon.wsEndpoint || autoWsEndpoint,
784
+ relayEndpoint: beacon.relayEndpoint || null,
754
785
  networkName: beacon.networkName,
755
786
  capabilities: beacon.capabilities,
756
787
  });
@@ -769,6 +800,7 @@ class SherpaDiscovery extends EventEmitter {
769
800
  nodeId: peer.nodeId,
770
801
  endpoint: peer.endpoint,
771
802
  wsEndpoint: peer.wsEndpoint,
803
+ relayEndpoint: peer.relayEndpoint,
772
804
  networkName: peer.networkName,
773
805
  lastSeen: peer.lastSeen,
774
806
  });
@@ -807,7 +839,54 @@ class SherpaDiscovery extends EventEmitter {
807
839
  * v2.5.0: Also measures RTT for geographic proof
808
840
  */
809
841
  async _fetchBeacon(endpoint) {
810
- const url = new URL(SHERPA_CONFIG.beaconPath, endpoint).toString();
842
+ // ── SSRF Protection: Validate endpoint URL before fetching ──
843
+ // Peer-supplied endpoints could target internal services, cloud metadata,
844
+ // or private networks. Block anything that isn't public HTTP(S).
845
+ let parsedUrl;
846
+ try {
847
+ parsedUrl = new URL(SHERPA_CONFIG.beaconPath, endpoint);
848
+ } catch {
849
+ throw new Error(`Invalid beacon endpoint URL: ${endpoint}`);
850
+ }
851
+
852
+ // Only allow HTTP and HTTPS schemes
853
+ if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
854
+ throw new Error(`Blocked beacon fetch: disallowed scheme ${parsedUrl.protocol}`);
855
+ }
856
+
857
+ // Block private/reserved IP ranges and cloud metadata endpoints
858
+ const hostname = parsedUrl.hostname.toLowerCase();
859
+ const BLOCKED_HOSTS = [
860
+ 'localhost', '127.0.0.1', '::1', '[::1]', '0.0.0.0',
861
+ '169.254.169.254', // AWS/GCP/Azure metadata
862
+ 'metadata.google.internal',
863
+ 'metadata.google',
864
+ ];
865
+ if (BLOCKED_HOSTS.includes(hostname)) {
866
+ throw new Error(`Blocked beacon fetch: reserved host ${hostname}`);
867
+ }
868
+
869
+ // Block private IP ranges: 10.x, 172.16-31.x, 192.168.x, fc00::/7, fe80::/10
870
+ const ipv4Match = hostname.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
871
+ if (ipv4Match) {
872
+ const [, a, b] = ipv4Match.map(Number);
873
+ if (a === 10 || // 10.0.0.0/8
874
+ (a === 172 && b >= 16 && b <= 31) || // 172.16.0.0/12
875
+ (a === 192 && b === 168) || // 192.168.0.0/16
876
+ a === 127 || // 127.0.0.0/8
877
+ a === 0 || // 0.0.0.0/8
878
+ (a === 169 && b === 254)) { // 169.254.0.0/16 (link-local)
879
+ throw new Error(`Blocked beacon fetch: private IP ${hostname}`);
880
+ }
881
+ }
882
+
883
+ // Block IPv6 private: starts with fc, fd, or fe80
884
+ const bareV6 = hostname.replace(/^\[|\]$/g, '');
885
+ if (bareV6.startsWith('fc') || bareV6.startsWith('fd') || bareV6.startsWith('fe80')) {
886
+ throw new Error(`Blocked beacon fetch: private IPv6 ${hostname}`);
887
+ }
888
+
889
+ const url = parsedUrl.toString();
811
890
 
812
891
  const controller = new AbortController();
813
892
  const timeout = setTimeout(() => controller.abort(), SHERPA_CONFIG.crawlTimeout);
@@ -877,14 +956,16 @@ class SherpaDiscovery extends EventEmitter {
877
956
  }
878
957
 
879
958
  /**
880
- * Get connection candidates for mesh networking
959
+ * Get connection candidates for mesh networking.
960
+ * Returns peers that have either a wsEndpoint or relayEndpoint.
881
961
  */
882
962
  getConnectionCandidates(count = 5) {
883
963
  return this.registry.getBestPeers(count)
884
- .filter(p => p.wsEndpoint)
964
+ .filter(p => p.wsEndpoint || p.relayEndpoint)
885
965
  .map(p => ({
886
966
  nodeId: p.nodeId,
887
967
  wsEndpoint: p.wsEndpoint,
968
+ relayEndpoint: p.relayEndpoint || null,
888
969
  score: p.score,
889
970
  }));
890
971
  }
@@ -979,7 +1060,7 @@ class SherpaDiscovery extends EventEmitter {
979
1060
  beacon.geo.lat,
980
1061
  beacon.geo.lon,
981
1062
  {
982
- name: beacon.geo.name || `SHERPA Beacon ${beacon.nodeId.slice(0, 8)}`,
1063
+ name: beacon.geo.name || `SHERPA Beacon ${peerTag(beacon.nodeId)}`,
983
1064
  endpoint: beacon._endpoint,
984
1065
  timeTier: beacon.geo.timeTier,
985
1066
  accuracyKm: beacon.geo.accuracyKm,
@@ -1,11 +1,20 @@
1
1
  /**
2
2
  * Sybil Attack Protection Module
3
+ *
4
+ * Uses TRIBHUJ balanced ternary for connection evaluation:
5
+ * POSITIVE (+1): Accept — node is trusted or verified
6
+ * NEUTRAL ( 0): Challenge — node must prove itself (NAVR required)
7
+ * NEGATIVE (-1): Reject — node is banned or subnet saturated
8
+ *
3
9
  * @module mesh/sybil-defense.js
4
10
  */
5
11
 
6
12
  import { sha3_256 } from '@noble/hashes/sha3.js';
7
13
  import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
8
14
 
15
+ // ═══ TRIBHUJ — Balanced ternary for connection decisions ═══
16
+ import { POSITIVE, NEUTRAL, NEGATIVE } from '../oracle/tribhuj.js';
17
+
9
18
  export class NAVR {
10
19
  constructor(options = {}) {
11
20
  this.difficulty = options.difficulty || 16;
@@ -125,16 +134,21 @@ export class SybilDefense {
125
134
  this.diversity = new SubnetDiversity(options.diversity || {});
126
135
  }
127
136
 
137
+ /**
138
+ * Evaluate a connection request.
139
+ * Returns `allowed` boolean (backward compat) plus `verdict` trit:
140
+ * POSITIVE: accept, NEUTRAL: challenge required, NEGATIVE: reject
141
+ */
128
142
  evaluateConnection(ip, nodeId, NAVRSolution = null) {
129
143
  const divCheck = this.diversity.allowConnection(ip);
130
- if (!divCheck.allowed) return { allowed: false, reason: divCheck.reason };
144
+ if (!divCheck.allowed) return { allowed: false, verdict: NEGATIVE, reason: divCheck.reason };
131
145
  let record = this.reputation.nodes.get(nodeId);
132
146
  if (!record) record = this.reputation.registerNode(nodeId, NAVRSolution);
133
147
  const trustLevel = this.reputation.getTrustLevel(nodeId);
134
- if (trustLevel === 'banned') return { allowed: false, reason: 'Node is banned' };
135
- if (trustLevel === 'unknown' && !NAVRSolution) return { allowed: false, reason: 'NAVR required', challenge: this.NAVR.createChallenge(nodeId) };
148
+ if (trustLevel === 'banned') return { allowed: false, verdict: NEGATIVE, reason: 'Node is banned' };
149
+ if (trustLevel === 'unknown' && !NAVRSolution) return { allowed: false, verdict: NEUTRAL, reason: 'NAVR required', challenge: this.NAVR.createChallenge(nodeId) };
136
150
  this.diversity.addConnection(ip, nodeId);
137
- return { allowed: true, trustLevel, reputation: record.reputation };
151
+ return { allowed: true, verdict: POSITIVE, trustLevel, reputation: record.reputation };
138
152
  }
139
153
 
140
154
  reportMessage(nodeId, valid) {
@@ -148,4 +162,4 @@ export class SybilDefense {
148
162
  }
149
163
 
150
164
  export default SybilDefense;
151
-
165
+
@@ -20,7 +20,8 @@
20
20
  * @copyright 2026 YAKMESH™ Contributors
21
21
  */
22
22
 
23
- import { randomBytes, createHash } from 'crypto';
23
+ import { createHash } from 'crypto';
24
+ import { ternaryId } from '../utils/ternary-id.js';
24
25
 
25
26
  const TME_CONFIG = {
26
27
  defaultSliceIntervalNs: 50_000_000,
@@ -112,7 +113,7 @@ class TemporalStream {
112
113
  }
113
114
 
114
115
  _generateStreamId() {
115
- return randomBytes(16).toString('hex');
116
+ return ternaryId(16);
116
117
  }
117
118
 
118
119
  encode(message, meshPosition = [0, 0, 0]) {
@@ -287,7 +288,7 @@ class TemporalReconstructor {
287
288
 
288
289
  class TemporalMeshEncoder {
289
290
  constructor(options = {}) {
290
- this.nodeId = options.nodeId || randomBytes(16).toString('hex');
291
+ this.nodeId = options.nodeId || ternaryId(16);
291
292
  this.meshPosition = options.meshPosition || [0, 0, 0];
292
293
  this.reconstructor = new TemporalReconstructor();
293
294
  this.outboundStreams = new Map();
package/mesh/yurt.js CHANGED
@@ -42,9 +42,11 @@
42
42
  */
43
43
 
44
44
  import { randomBytes, createHash } from 'crypto';
45
- import { sha3_256 } from '@noble/hashes/sha3.js';
45
+ import { sha3_256 as _nobleSha3 } from '@noble/hashes/sha3.js';
46
46
  import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils.js';
47
47
  import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
48
+ // ACCEL: Hardware-accelerated crypto
49
+ import { sha3_256, mlDsa65Sign, mlDsa65Verify } from '../utils/accel.js';
48
50
  import { createLogger } from '../utils/logger.js';
49
51
  import EventEmitter from 'events';
50
52
 
@@ -203,7 +205,8 @@ export class YurtEntry {
203
205
  */
204
206
  sign(secretKey) {
205
207
  const payload = utf8ToBytes(this.getSignablePayload());
206
- const signature = ml_dsa65.sign(payload, secretKey);
208
+ const keyBytes = typeof secretKey === 'string' ? hexToBytes(secretKey) : secretKey;
209
+ const signature = mlDsa65Sign(payload, keyBytes);
207
210
  this.signature = bytesToHex(signature);
208
211
  return this;
209
212
  }
@@ -221,7 +224,7 @@ export class YurtEntry {
221
224
  ? hexToBytes(publicKey)
222
225
  : publicKey;
223
226
 
224
- return ml_dsa65.verify(signatureBytes, payload, publicKeyBytes);
227
+ return mlDsa65Verify(signatureBytes, payload, publicKeyBytes);
225
228
  } catch (err) {
226
229
  log.warn('Signature verification failed', { error: err.message });
227
230
  return false;
@@ -718,6 +721,7 @@ export class YurtGossip extends EventEmitter {
718
721
  this.directory = directory;
719
722
  this.mesh = mesh;
720
723
  this.options = options;
724
+ this.keyResolver = options.keyResolver || null;
721
725
 
722
726
  // Track what we've sent to avoid duplicate gossip
723
727
  this.sentTo = new Map(); // peerId -> { entryId -> timestamp }
@@ -1032,13 +1036,24 @@ export class YurtGossip extends EventEmitter {
1032
1036
  }
1033
1037
 
1034
1038
  /**
1035
- * Get public key for a node
1039
+ * Get public key for a node — unified resolution cascade
1040
+ *
1041
+ * Resolution order:
1042
+ * 1. Custom publicKeyLookup callback (backwards compat)
1043
+ * 2. KeyResolver (DOKO cache, peers, SHERPA, etc.)
1036
1044
  */
1037
1045
  _getPublicKey(nodeId) {
1038
- // TODO: Integrate with KHATA
1046
+ // Legacy callback path
1039
1047
  if (this.options.publicKeyLookup) {
1040
- return this.options.publicKeyLookup(nodeId);
1048
+ const key = this.options.publicKeyLookup(nodeId);
1049
+ if (key) return key;
1041
1050
  }
1051
+
1052
+ // KeyResolver: unified key resolution
1053
+ if (this.keyResolver) {
1054
+ return this.keyResolver.resolve(nodeId);
1055
+ }
1056
+
1042
1057
  return null;
1043
1058
  }
1044
1059
 
@@ -1160,6 +1175,8 @@ export class YurtHub extends EventEmitter {
1160
1175
 
1161
1176
  /**
1162
1177
  * Join a room via yak:// link
1178
+ *
1179
+ * Flow: parse URI → resolve host via mesh → request GUMBA session → return access
1163
1180
  */
1164
1181
  async joinViaLink(uri) {
1165
1182
  const parsed = YurtLink.parse(uri);
@@ -1172,15 +1189,42 @@ export class YurtHub extends EventEmitter {
1172
1189
  bundleId: parsed.bundleId,
1173
1190
  });
1174
1191
 
1175
- // Connect to host and request access
1176
- // TODO: Integrate with actual mesh connection
1177
- return {
1178
- success: true,
1179
- parsed,
1180
- endpoint: parsed.endpoint,
1181
- bundleId: parsed.bundleId,
1182
- invite: parsed.invite,
1183
- };
1192
+ // Look up the room in our directory first
1193
+ const entry = this.directory.getByBundle(parsed.bundleId);
1194
+
1195
+ // Request a GUMBA session on the target bundle
1196
+ try {
1197
+ const session = await this.gumbaHub.requestSession(
1198
+ parsed.bundleId,
1199
+ this.identity.identity.nodeId,
1200
+ { invite: parsed.invite }
1201
+ );
1202
+
1203
+ if (session?.error) {
1204
+ return {
1205
+ success: false,
1206
+ error: session.error,
1207
+ parsed,
1208
+ };
1209
+ }
1210
+
1211
+ return {
1212
+ success: true,
1213
+ parsed,
1214
+ endpoint: entry?.hostEndpoint || parsed.endpoint,
1215
+ bundleId: parsed.bundleId,
1216
+ invite: parsed.invite,
1217
+ sessionId: session?.sessionId || null,
1218
+ };
1219
+ } catch (err) {
1220
+ log.warn('joinViaLink failed', { error: err.message, uri });
1221
+ return {
1222
+ success: false,
1223
+ error: 'CONNECTION_FAILED',
1224
+ details: err.message,
1225
+ parsed,
1226
+ };
1227
+ }
1184
1228
  }
1185
1229
 
1186
1230
  /**
@@ -1236,10 +1280,21 @@ export class YurtHub extends EventEmitter {
1236
1280
 
1237
1281
  /**
1238
1282
  * Get our own endpoint
1283
+ *
1284
+ * Priority: explicit option → mesh advertised address → default
1239
1285
  */
1240
1286
  _getOwnEndpoint() {
1241
- // TODO: Discover actual endpoint
1242
- return this.options.endpoint || `localhost:${YURT_CONFIG.defaultPort}`;
1287
+ if (this.options.endpoint) {
1288
+ return this.options.endpoint;
1289
+ }
1290
+
1291
+ // Ask the mesh for our advertised address
1292
+ if (this.mesh?.getAdvertisedAddress) {
1293
+ const addr = this.mesh.getAdvertisedAddress();
1294
+ if (addr) return addr;
1295
+ }
1296
+
1297
+ return `localhost:${YURT_CONFIG.defaultPort}`;
1243
1298
  }
1244
1299
 
1245
1300
  /**
Binary file
Binary file
@@ -0,0 +1,43 @@
1
+ {
2
+ "generated": "2026-02-25 18:49:50",
3
+ "random_seed": 144,
4
+ "models": {
5
+ "entropy-sentinel": {
6
+ "inputs": 32,
7
+ "hidden": [
8
+ 48,
9
+ 32,
10
+ 16
11
+ ],
12
+ "outputs": 1,
13
+ "activation": "sigmoid",
14
+ "samples": 8000,
15
+ "size": 15388
16
+ },
17
+ "sakshi-anomaly": {
18
+ "inputs": 12,
19
+ "hidden": [
20
+ 24,
21
+ 16
22
+ ],
23
+ "outputs": 4,
24
+ "activation": "sigmoid",
25
+ "samples": 7200,
26
+ "size": 3634
27
+ },
28
+ "karma-trust": {
29
+ "inputs": 14,
30
+ "hidden": [
31
+ 24,
32
+ 16
33
+ ],
34
+ "outputs": 4,
35
+ "activation": "softmax",
36
+ "samples": 9000,
37
+ "size": 3807
38
+ }
39
+ },
40
+ "total_size": 22829,
41
+ "total_samples": 24200,
42
+ "training_time_seconds": 16.6
43
+ }
Binary file
@@ -23,6 +23,7 @@ import { getOracle, contentHash } from './validation-oracle-hardened.js';
23
23
  import { createLogger } from '../utils/logger.js';
24
24
 
25
25
  const log = createLogger('oracle:code-proof');
26
+ const peerTag = (id) => id?.split('-pq-').pop() || id?.slice?.(-8) || String(id);
26
27
 
27
28
  // Phase modulation imports
28
29
  import {
@@ -116,7 +117,7 @@ export class CodeProofProtocol {
116
117
  failedAt: Date.now(),
117
118
  reason: 'CHALLENGE_TIMEOUT',
118
119
  });
119
- log.warn('Code proof challenge timed out', { peerId: peerId.slice(0, 16), reason: 'CHALLENGE_TIMEOUT' });
120
+ log.warn('Code proof challenge timed out', { peerId: peerTag(peerId), reason: 'CHALLENGE_TIMEOUT' });
120
121
  }
121
122
  }, this.challengeTimeout);
122
123
 
@@ -149,7 +150,7 @@ export class CodeProofProtocol {
149
150
  if (epoch !== undefined) {
150
151
  const validEpochs = getValidEpochs();
151
152
  if (!validEpochs.includes(epoch)) {
152
- log.warn('Challenge has expired phase', { challengerNodeId: challengerNodeId.slice(0, 16), phase: formatPhaseId(epoch) });
153
+ log.warn('Challenge has expired phase', { challengerNodeId: peerTag(challengerNodeId), phase: formatPhaseId(epoch) });
153
154
  return {
154
155
  type: 'CODE_PROOF_RESPONSE',
155
156
  challengeId,
@@ -163,7 +164,7 @@ export class CodeProofProtocol {
163
164
 
164
165
  // Check expiration
165
166
  if (expiresAt && Date.now() > expiresAt) {
166
- log.warn('Challenge has expired', { challengerNodeId: challengerNodeId.slice(0, 16), expiresAt });
167
+ log.warn('Challenge has expired', { challengerNodeId: peerTag(challengerNodeId), expiresAt });
167
168
  return {
168
169
  type: 'CODE_PROOF_RESPONSE',
169
170
  challengeId,
@@ -262,7 +263,7 @@ export class CodeProofProtocol {
262
263
  this.failedPeers.delete(responderNodeId);
263
264
 
264
265
  log.info('Code proof verified', {
265
- peerId: responderNodeId.slice(0, 16),
266
+ peerId: peerTag(responderNodeId),
266
267
  phaseEpoch: formatPhaseId(epoch || 0),
267
268
  });
268
269
 
@@ -284,7 +285,7 @@ export class CodeProofProtocol {
284
285
  });
285
286
 
286
287
  log.warn('Code proof FAILED', {
287
- peerId: responderNodeId.slice(0, 16),
288
+ peerId: peerTag(responderNodeId),
288
289
  reason: verification.reason,
289
290
  });
290
291
 
@@ -440,7 +441,7 @@ export async function mutualVerification(localProtocol, peerConnection) {
440
441
  peerConnection.send(challenge);
441
442
 
442
443
  log.debug('Sent phased challenge', {
443
- peerId: peerId.slice(0, 16),
444
+ peerId: peerTag(peerId),
444
445
  phaseEpoch: formatPhaseId(currentEpoch.epoch),
445
446
  });
446
447