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.
- package/CHANGELOG.md +637 -0
- package/Caddyfile +77 -0
- package/README.md +119 -29
- package/content/api.js +50 -41
- package/content/index.js +1 -2
- package/content/store.js +323 -177
- package/dashboard/index.html +19 -3
- package/database/replication.js +117 -37
- package/docs/CRYPTO-AGILITY.md +204 -0
- package/docs/MTLS-RESEARCH.md +367 -0
- package/docs/NAMCHE-SPEC.md +681 -0
- package/docs/PEERQUANTA-YAKMESH-INTEGRATION.md +407 -0
- package/docs/PRECISION-DISCLOSURE.md +96 -0
- package/docs/README.md +76 -0
- package/docs/ROADMAP-2.4.0.md +447 -0
- package/docs/ROADMAP-2.5.0.md +244 -0
- package/docs/SECURITY-AUDIT-REPORT.md +306 -0
- package/docs/SST-INTEGRATION.md +712 -0
- package/docs/STEADYWATCH-IMPLEMENTATION.md +303 -0
- package/docs/TERNARY-AUDIT-REPORT.md +247 -0
- package/docs/TME-FAQ.md +221 -0
- package/docs/WHITEPAPER.md +623 -0
- package/docs/adapters.html +1001 -0
- package/docs/advanced-systems.html +1045 -0
- package/docs/annex.html +1046 -0
- package/docs/api.html +970 -0
- package/docs/business/response-templates.md +160 -0
- package/docs/c2c.html +1225 -0
- package/docs/cli.html +1332 -0
- package/docs/configuration.html +1248 -0
- package/docs/darshan.html +1085 -0
- package/docs/dharma.html +966 -0
- package/docs/docs-bundle.html +1075 -0
- package/docs/docs.css +3120 -0
- package/docs/docs.js +556 -0
- package/docs/doko.html +969 -0
- package/docs/geo-proof.html +858 -0
- package/docs/getting-started.html +840 -0
- package/docs/gumba-tutorial.html +1144 -0
- package/docs/gumba.html +1098 -0
- package/docs/index.html +914 -0
- package/docs/jhilke.html +1312 -0
- package/docs/karma.html +1100 -0
- package/docs/katha.html +1037 -0
- package/docs/lama.html +978 -0
- package/docs/mandala.html +1067 -0
- package/docs/mani.html +964 -0
- package/docs/mantra.html +967 -0
- package/docs/mesh.html +1409 -0
- package/docs/nakpak.html +869 -0
- package/docs/namche.html +928 -0
- package/docs/nav-order.json +53 -0
- package/docs/prahari.html +1043 -0
- package/docs/prism-bash.min.js +1 -0
- package/docs/prism-javascript.min.js +1 -0
- package/docs/prism-json.min.js +1 -0
- package/docs/prism-tomorrow.min.css +1 -0
- package/docs/prism.min.js +1 -0
- package/docs/privacy.html +699 -0
- package/docs/quick-reference.html +1181 -0
- package/docs/sakshi.html +1402 -0
- package/docs/sandboxing.md +386 -0
- package/docs/seva.html +911 -0
- package/docs/sherpa.html +871 -0
- package/docs/studio.html +860 -0
- package/docs/stupa.html +995 -0
- package/docs/tailwind.min.css +2 -0
- package/docs/tattva.html +1332 -0
- package/docs/terms.html +686 -0
- package/docs/time-server-deployment.md +166 -0
- package/docs/time-sources.html +1392 -0
- package/docs/tivra.html +1127 -0
- package/docs/trademark-policy.html +686 -0
- package/docs/tribhuj.html +1183 -0
- package/docs/trust-security.html +1029 -0
- package/docs/tutorials/backup-recovery.html +654 -0
- package/docs/tutorials/dashboard.html +604 -0
- package/docs/tutorials/domain-setup.html +605 -0
- package/docs/tutorials/host-website.html +456 -0
- package/docs/tutorials/mesh-network.html +505 -0
- package/docs/tutorials/mobile-access.html +445 -0
- package/docs/tutorials/privacy.html +467 -0
- package/docs/tutorials/raspberry-pi.html +600 -0
- package/docs/tutorials/security-basics.html +539 -0
- package/docs/tutorials/share-files.html +431 -0
- package/docs/tutorials/troubleshooting.html +637 -0
- package/docs/tutorials/trust-karma.html +419 -0
- package/docs/tutorials/yak-protocol.html +456 -0
- package/docs/tutorials.html +1034 -0
- package/docs/vani.html +1270 -0
- package/docs/webserver.html +809 -0
- package/docs/yak-protocol.html +940 -0
- package/docs/yak-timeserver-design.md +475 -0
- package/docs/yakapp.html +1015 -0
- package/docs/ypc27.html +1069 -0
- package/docs/yurt.html +1344 -0
- package/embedded-docs/bundle.js +274 -114
- package/gossip/protocol.js +247 -27
- package/identity/key-resolver.js +262 -0
- package/identity/machine-seed.js +632 -0
- package/identity/node-key.js +669 -368
- package/identity/tribhuj-ratchet.js +506 -0
- package/knowledge-base.js +37 -8
- package/launcher/yakmesh.bat +62 -0
- package/launcher/yakmesh.sh +70 -0
- package/mesh/annex.js +462 -108
- package/mesh/beacon-broadcast.js +4 -1
- package/mesh/darshan.js +17 -5
- package/mesh/gumba.js +47 -13
- package/mesh/jhilke.js +651 -0
- package/mesh/katha.js +5 -2
- package/mesh/nakpak-routing.js +8 -5
- package/mesh/network.js +724 -34
- package/mesh/pulse-sync.js +4 -1
- package/mesh/seva.js +526 -0
- package/mesh/sherpa-discovery.js +89 -8
- package/mesh/sybil-defense.js +19 -5
- package/mesh/temporal-encoder.js +4 -3
- package/mesh/yurt.js +72 -17
- package/models/entropy-sentinel.onnx +0 -0
- package/models/karma-trust.onnx +0 -0
- package/models/manifest.json +43 -0
- package/models/sakshi-anomaly.onnx +0 -0
- package/oracle/code-proof-protocol.js +7 -6
- package/oracle/codebase-lock.js +257 -28
- package/oracle/index.js +74 -15
- package/oracle/ma902-snmp.js +678 -0
- package/oracle/module-sealer.js +5 -3
- package/oracle/packet-checksum.js +201 -0
- package/oracle/ternary-144t.js +714 -0
- package/oracle/ternary-ml.js +481 -0
- package/oracle/time-api.js +239 -0
- package/oracle/time-source.js +137 -47
- package/oracle/validation-oracle-hardened.js +1111 -1071
- package/oracle/validation-oracle.js +4 -2
- package/oracle/ypc27.js +211 -0
- package/package.json +20 -3
- package/protocol/yak-handler.js +35 -9
- package/protocol/yak-protocol.js +6 -5
- package/reference/cpp/yakmesh_mceliece_shard.cpp +168 -0
- package/reference/cpp/yakmesh_ypc27.cpp +179 -0
- package/sbom.json +87 -0
- package/scripts/security-audit.mjs +264 -0
- package/scripts/update-docs-sidebar.cjs +164 -0
- package/security/crypto-config.js +4 -3
- package/security/dharma-moderation.js +4 -3
- package/security/doko-identity.js +193 -143
- package/security/domain-consensus.js +86 -85
- package/security/fs-hardening.js +620 -0
- package/security/hardware-attestation.js +5 -3
- package/security/hybrid-trust.js +227 -87
- package/security/karma-rate-limiter.js +692 -0
- package/security/khata-protocol.js +22 -21
- package/security/khata-trust-integration.js +277 -150
- package/security/memory-safety.js +635 -0
- package/security/mesh-auth.js +11 -10
- package/security/mesh-revocation.js +18 -5
- package/security/namche-gateway.js +298 -69
- package/security/sakshi.js +102 -3
- package/security/sangha.js +770 -0
- package/security/secure-config.js +473 -0
- package/security/silicon-parity.js +13 -10
- package/security/steadywatch.js +1142 -0
- package/security/strike-system.js +32 -3
- package/security/temporal-signing.js +488 -0
- package/security/trit-commitment.js +464 -0
- package/server/crypto/annex.js +247 -0
- package/server/darshan-api.js +343 -0
- package/server/index.js +3259 -362
- package/server/komm-api.js +668 -0
- package/utils/accel.js +2273 -0
- package/utils/ternary-id.js +79 -0
- package/utils/verify-worker.js +57 -0
- package/webserver/index.js +95 -5
- package/assets/yakmesh-logo.png +0 -0
- package/assets/yakmesh-logo.svg +0 -80
- package/assets/yakmesh-logo2.png +0 -0
- package/assets/yakmesh-logo2sm.png +0 -0
- package/assets/ymsm.png +0 -0
- package/scripts/update-docs-nav.cjs +0 -194
- package/update-docs-nav.cjs +0 -18
- package/update-nav.ps1 +0 -16
- package/website/assets/silhouettes/adapters.svg +0 -107
- package/website/assets/silhouettes/api-endpoints.svg +0 -115
- package/website/assets/silhouettes/atomic-clock.svg +0 -83
- package/website/assets/silhouettes/base-camp.svg +0 -81
- package/website/assets/silhouettes/bridge.svg +0 -69
- package/website/assets/silhouettes/docs-bundle.svg +0 -113
- package/website/assets/silhouettes/doko-basket.svg +0 -70
- package/website/assets/silhouettes/fortress.svg +0 -93
- package/website/assets/silhouettes/gateway.svg +0 -54
- package/website/assets/silhouettes/gears.svg +0 -93
- package/website/assets/silhouettes/globe-satellite.svg +0 -67
- package/website/assets/silhouettes/karma-wheel.svg +0 -137
- package/website/assets/silhouettes/lama-council.svg +0 -141
- package/website/assets/silhouettes/mandala-network.svg +0 -169
- package/website/assets/silhouettes/mani-stones.svg +0 -149
- package/website/assets/silhouettes/mantra-wheel.svg +0 -116
- package/website/assets/silhouettes/mesh-nodes.svg +0 -113
- package/website/assets/silhouettes/nakpak.svg +0 -56
- package/website/assets/silhouettes/peak-lightning.svg +0 -73
- package/website/assets/silhouettes/sherpa.svg +0 -69
- package/website/assets/silhouettes/stupa-tower.svg +0 -119
- package/website/assets/silhouettes/tattva-eye.svg +0 -78
- package/website/assets/silhouettes/terminal.svg +0 -74
- package/website/assets/silhouettes/webserver.svg +0 -145
- package/website/assets/silhouettes/yak.svg +0 -78
- package/website/assets/yakmesh-logo.png +0 -0
- package/website/assets/yakmesh-logo.webp +0 -0
- package/website/assets/yakmesh-logo128x140.webp +0 -0
- package/website/assets/yakmesh-logo2.png +0 -0
- package/website/assets/yakmesh-logo2.svg +0 -51
- package/website/assets/yakmesh-logo40x44.webp +0 -0
- package/website/assets/yakmesh.gif +0 -0
- package/website/assets/yakmesh.ico +0 -0
- package/website/assets/yakmesh.jpg +0 -0
- package/website/assets/yakmesh.pdf +0 -0
- package/website/assets/yakmesh.png +0 -0
- package/website/assets/yakmesh.svg +0 -70
- package/website/assets/yakmesh128.webp +0 -0
- package/website/assets/yakmesh32.png +0 -0
- package/website/assets/yakmesh32.svg +0 -65
- package/website/assets/yakmesh32o.ico +0 -2
- package/website/assets/yakmesh32o.svg +0 -65
- package/website/assets/yakmesh32o.svgz +0 -0
package/mesh/sherpa-discovery.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
752
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
package/mesh/sybil-defense.js
CHANGED
|
@@ -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
|
+
|
package/mesh/temporal-encoder.js
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* @copyright 2026 YAKMESH™ Contributors
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import {
|
|
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
|
|
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 ||
|
|
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
|
|
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
|
|
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
|
-
//
|
|
1046
|
+
// Legacy callback path
|
|
1039
1047
|
if (this.options.publicKeyLookup) {
|
|
1040
|
-
|
|
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
|
-
//
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
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
|
-
|
|
1242
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
444
|
+
peerId: peerTag(peerId),
|
|
444
445
|
phaseEpoch: formatPhaseId(currentEpoch.epoch),
|
|
445
446
|
});
|
|
446
447
|
|