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.
- package/CHANGELOG.md +637 -0
- package/CONTRIBUTING.md +42 -0
- package/Caddyfile +77 -0
- package/README.md +119 -29
- package/adapters/adapter-mlv-bible/README.md +124 -0
- package/adapters/adapter-mlv-bible/index.js +400 -0
- package/adapters/chat-mod-adapter.js +532 -0
- package/adapters/content-adapter.js +273 -0
- package/content/api.js +50 -41
- package/content/index.js +2 -2
- package/content/store.js +355 -173
- 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 +334 -74
- 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 +113 -1
- package/mesh/darshan.js +1718 -0
- package/mesh/gumba.js +1567 -0
- package/mesh/jhilke.js +651 -0
- package/mesh/katha.js +1012 -0
- package/mesh/nakpak-routing.js +8 -5
- package/mesh/network.js +724 -34
- package/mesh/pulse-sync.js +4 -1
- package/mesh/rate-limiter.js +127 -15
- 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/vani.js +1364 -0
- package/mesh/yurt.js +1340 -0
- 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/network-identity.js +16 -0
- package/oracle/packet-checksum.js +201 -0
- package/oracle/sst.js +579 -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 +28 -13
- 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-nav.js +194 -0
- package/scripts/update-docs-sidebar.cjs +164 -0
- package/security/crypto-config.js +4 -3
- package/security/dharma-moderation.js +517 -0
- 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 +373 -5
- package/security/namche-gateway.js +298 -69
- package/security/sakshi.js +460 -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/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
|
@@ -59,7 +59,8 @@
|
|
|
59
59
|
*/
|
|
60
60
|
|
|
61
61
|
import { sha3_256 } from '@noble/hashes/sha3.js';
|
|
62
|
-
import { bytesToHex, utf8ToBytes
|
|
62
|
+
import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
|
|
63
|
+
import { ternaryId } from '../utils/ternary-id.js';
|
|
63
64
|
import { EventEmitter } from 'events';
|
|
64
65
|
|
|
65
66
|
/**
|
|
@@ -69,43 +70,43 @@ const DEFAULT_CONFIG = {
|
|
|
69
70
|
// Quorum settings
|
|
70
71
|
quorumSize: 3, // Minimum verifiers needed for consensus
|
|
71
72
|
verifiersToRequest: 5, // Request from more than quorum (some may fail)
|
|
72
|
-
|
|
73
|
+
|
|
73
74
|
// Timeouts
|
|
74
75
|
verificationTimeout: 30000, // 30 seconds per verification
|
|
75
76
|
totalTimeout: 120000, // 2 minutes for entire process
|
|
76
|
-
|
|
77
|
+
|
|
77
78
|
// Retry settings
|
|
78
79
|
maxRetries: 2,
|
|
79
80
|
retryDelay: 5000,
|
|
80
|
-
|
|
81
|
+
|
|
81
82
|
// Beacon requirements
|
|
82
83
|
beaconPath: '/.well-known/yakmesh/beacon',
|
|
83
84
|
beaconMaxAge: 300000, // Beacon must be < 5 minutes old
|
|
84
|
-
|
|
85
|
+
|
|
85
86
|
// Rate limiting
|
|
86
87
|
maxConcurrentVerifications: 10,
|
|
87
88
|
cooldownBetweenClaims: 3600000, // 1 hour between claims for same domain
|
|
88
|
-
|
|
89
|
+
|
|
89
90
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
90
91
|
// SYBIL DEFENSE CONFIGURATION
|
|
91
92
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
92
|
-
|
|
93
|
+
|
|
93
94
|
// Verifier age requirements
|
|
94
95
|
minVerifierAge: 7 * 24 * 60 * 60 * 1000, // 7 days minimum mesh presence
|
|
95
96
|
preferredVerifierAge: 30 * 24 * 60 * 60 * 1000, // 30 days for bonus weight
|
|
96
|
-
|
|
97
|
+
|
|
97
98
|
// IP/ASN diversity requirements
|
|
98
99
|
minDistinctSubnets: 3, // Minimum different /24 subnets
|
|
99
100
|
minDistinctASNs: 2, // Minimum different ASNs (Autonomous Systems)
|
|
100
101
|
subnetMaskBits: 24, // /24 subnet grouping (256 IPs per group)
|
|
101
|
-
|
|
102
|
+
|
|
102
103
|
// Claimant exclusion radius
|
|
103
104
|
claimantExclusionSubnet: 16, // Exclude verifiers in same /16 as claimant
|
|
104
|
-
|
|
105
|
+
|
|
105
106
|
// Reputation thresholds
|
|
106
107
|
minReputationScore: 0.2, // Minimum reputation to be eligible (0-1)
|
|
107
108
|
reputationWeightFactor: 2.0, // Higher reputation = more likely selected
|
|
108
|
-
|
|
109
|
+
|
|
109
110
|
// Time-based verification windows
|
|
110
111
|
enableTimeWindows: false, // When true, verify at T, T+1hr, T+24hr
|
|
111
112
|
timeWindowIntervals: [0, 3600000, 86400000], // 0, 1 hour, 24 hours
|
|
@@ -135,13 +136,13 @@ const DEFAULT_CONFIG = {
|
|
|
135
136
|
class VerifierEligibilityChecker {
|
|
136
137
|
constructor(config = {}) {
|
|
137
138
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
138
|
-
|
|
139
|
+
|
|
139
140
|
// Node reputation store (nodeId -> reputation data)
|
|
140
141
|
this.reputations = new Map();
|
|
141
|
-
|
|
142
|
+
|
|
142
143
|
// Node first-seen timestamps (nodeId -> timestamp)
|
|
143
144
|
this.nodeFirstSeen = new Map();
|
|
144
|
-
|
|
145
|
+
|
|
145
146
|
// Node network info cache (nodeId -> { ip, asn, subnet })
|
|
146
147
|
this.nodeNetworkInfo = new Map();
|
|
147
148
|
}
|
|
@@ -153,7 +154,7 @@ class VerifierEligibilityChecker {
|
|
|
153
154
|
if (!this.nodeFirstSeen.has(nodeId)) {
|
|
154
155
|
this.nodeFirstSeen.set(nodeId, Date.now());
|
|
155
156
|
}
|
|
156
|
-
|
|
157
|
+
|
|
157
158
|
if (networkInfo.ip) {
|
|
158
159
|
this.nodeNetworkInfo.set(nodeId, {
|
|
159
160
|
ip: networkInfo.ip,
|
|
@@ -194,14 +195,14 @@ class VerifierEligibilityChecker {
|
|
|
194
195
|
getReputationScore(nodeId) {
|
|
195
196
|
const rep = this.reputations.get(nodeId);
|
|
196
197
|
if (!rep) return 0.5; // Neutral for unknown nodes
|
|
197
|
-
|
|
198
|
+
|
|
198
199
|
const total = rep.successCount + rep.failureCount;
|
|
199
200
|
if (total === 0) return 0.5;
|
|
200
|
-
|
|
201
|
+
|
|
201
202
|
// Success ratio weighted by volume (more history = more reliable score)
|
|
202
203
|
const successRatio = rep.successCount / total;
|
|
203
204
|
const volumeWeight = Math.min(1, total / 20); // Full weight at 20+ verifications
|
|
204
|
-
|
|
205
|
+
|
|
205
206
|
// Blend toward neutral for low-volume nodes
|
|
206
207
|
return 0.5 + (successRatio - 0.5) * volumeWeight;
|
|
207
208
|
}
|
|
@@ -239,7 +240,7 @@ class VerifierEligibilityChecker {
|
|
|
239
240
|
if (claimantInfo.ip) {
|
|
240
241
|
const nodeInfo = this.nodeNetworkInfo.get(nodeId);
|
|
241
242
|
const claimantWideSubnet = this.getSubnet(claimantInfo.ip, this.config.claimantExclusionSubnet);
|
|
242
|
-
|
|
243
|
+
|
|
243
244
|
if (nodeInfo && nodeInfo.wideSubnet === claimantWideSubnet) {
|
|
244
245
|
reasons.push(`Same IP range as claimant (/${this.config.claimantExclusionSubnet})`);
|
|
245
246
|
}
|
|
@@ -299,21 +300,21 @@ class VerifierEligibilityChecker {
|
|
|
299
300
|
remaining.sort((a, b) => {
|
|
300
301
|
const aInfo = this.nodeNetworkInfo.get(a.nodeId) || {};
|
|
301
302
|
const bInfo = this.nodeNetworkInfo.get(b.nodeId) || {};
|
|
302
|
-
|
|
303
|
+
|
|
303
304
|
const aNewSubnet = !usedSubnets.has(aInfo.subnet) ? 1 : 0;
|
|
304
305
|
const bNewSubnet = !usedSubnets.has(bInfo.subnet) ? 1 : 0;
|
|
305
306
|
if (aNewSubnet !== bNewSubnet) return bNewSubnet - aNewSubnet;
|
|
306
|
-
|
|
307
|
+
|
|
307
308
|
const aNewASN = !usedASNs.has(aInfo.asn) ? 1 : 0;
|
|
308
309
|
const bNewASN = !usedASNs.has(bInfo.asn) ? 1 : 0;
|
|
309
310
|
if (aNewASN !== bNewASN) return bNewASN - aNewASN;
|
|
310
|
-
|
|
311
|
+
|
|
311
312
|
return b.weight - a.weight; // Higher weight preferred
|
|
312
313
|
});
|
|
313
314
|
|
|
314
315
|
const choice = remaining.shift();
|
|
315
316
|
selected.push(choice);
|
|
316
|
-
|
|
317
|
+
|
|
317
318
|
const info = this.nodeNetworkInfo.get(choice.nodeId) || {};
|
|
318
319
|
if (info.subnet) usedSubnets.add(info.subnet);
|
|
319
320
|
if (info.asn) usedASNs.add(info.asn);
|
|
@@ -351,17 +352,17 @@ class VerifierEligibilityChecker {
|
|
|
351
352
|
calculateSelectionWeight(nodeId) {
|
|
352
353
|
const age = this.getNodeAge(nodeId);
|
|
353
354
|
const reputation = this.getReputationScore(nodeId);
|
|
354
|
-
|
|
355
|
+
|
|
355
356
|
// Base weight from reputation
|
|
356
357
|
let weight = reputation * this.config.reputationWeightFactor;
|
|
357
|
-
|
|
358
|
+
|
|
358
359
|
// Bonus for older nodes
|
|
359
360
|
if (age >= this.config.preferredVerifierAge) {
|
|
360
361
|
weight *= 1.5;
|
|
361
362
|
} else if (age >= this.config.minVerifierAge * 2) {
|
|
362
363
|
weight *= 1.25;
|
|
363
364
|
}
|
|
364
|
-
|
|
365
|
+
|
|
365
366
|
return weight;
|
|
366
367
|
}
|
|
367
368
|
|
|
@@ -370,24 +371,24 @@ class VerifierEligibilityChecker {
|
|
|
370
371
|
*/
|
|
371
372
|
getSubnet(ip, maskBits) {
|
|
372
373
|
if (!ip) return null;
|
|
373
|
-
|
|
374
|
+
|
|
374
375
|
// Handle IPv4
|
|
375
376
|
const parts = ip.split('.');
|
|
376
377
|
if (parts.length === 4) {
|
|
377
378
|
const fullBits = parts.map(p => parseInt(p, 10));
|
|
378
379
|
const octetsToKeep = Math.floor(maskBits / 8);
|
|
379
380
|
const result = fullBits.slice(0, octetsToKeep);
|
|
380
|
-
|
|
381
|
+
|
|
381
382
|
// Handle partial octet
|
|
382
383
|
const remainingBits = maskBits % 8;
|
|
383
384
|
if (remainingBits > 0 && octetsToKeep < 4) {
|
|
384
385
|
const mask = (0xFF << (8 - remainingBits)) & 0xFF;
|
|
385
386
|
result.push(fullBits[octetsToKeep] & mask);
|
|
386
387
|
}
|
|
387
|
-
|
|
388
|
+
|
|
388
389
|
return result.join('.') + '/' + maskBits;
|
|
389
390
|
}
|
|
390
|
-
|
|
391
|
+
|
|
391
392
|
// For IPv6 or unknown, just return the IP (less effective but works)
|
|
392
393
|
return ip;
|
|
393
394
|
}
|
|
@@ -437,8 +438,8 @@ class VerifierEligibilityChecker {
|
|
|
437
438
|
return {
|
|
438
439
|
totalNodes: this.nodeFirstSeen.size,
|
|
439
440
|
eligibleByAge: eligibleCount,
|
|
440
|
-
averageAge: this.nodeFirstSeen.size > 0
|
|
441
|
-
? Math.floor(totalAge / this.nodeFirstSeen.size / (24 * 60 * 60 * 1000))
|
|
441
|
+
averageAge: this.nodeFirstSeen.size > 0
|
|
442
|
+
? Math.floor(totalAge / this.nodeFirstSeen.size / (24 * 60 * 60 * 1000))
|
|
442
443
|
: 0,
|
|
443
444
|
nodesWithReputation: this.reputations.size,
|
|
444
445
|
nodesWithNetworkInfo: this.nodeNetworkInfo.size,
|
|
@@ -451,7 +452,7 @@ class VerifierEligibilityChecker {
|
|
|
451
452
|
*/
|
|
452
453
|
class DomainVerificationRequest {
|
|
453
454
|
constructor(options) {
|
|
454
|
-
this.id =
|
|
455
|
+
this.id = ternaryId(16);
|
|
455
456
|
this.domain = options.domain;
|
|
456
457
|
this.claimantNodeId = options.claimantNodeId;
|
|
457
458
|
this.claimantPublicKey = options.claimantPublicKey;
|
|
@@ -561,28 +562,28 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
561
562
|
this.identity = nodeIdentity;
|
|
562
563
|
this.gateway = namcheGateway;
|
|
563
564
|
this.config = { ...DEFAULT_CONFIG, ...options };
|
|
564
|
-
|
|
565
|
+
|
|
565
566
|
// ═══════════════════════════════════════════════════════════════════════
|
|
566
567
|
// SYBIL DEFENSE: Eligibility checker for verifier selection
|
|
567
568
|
// ═══════════════════════════════════════════════════════════════════════
|
|
568
569
|
this.eligibility = new VerifierEligibilityChecker(this.config);
|
|
569
|
-
|
|
570
|
+
|
|
570
571
|
// Active verification requests (by domain)
|
|
571
572
|
this.activeRequests = new Map();
|
|
572
|
-
|
|
573
|
+
|
|
573
574
|
// Cooldown tracking (domain -> last claim timestamp)
|
|
574
575
|
this.cooldowns = new Map();
|
|
575
|
-
|
|
576
|
+
|
|
576
577
|
// Fetch function (must be set by network layer)
|
|
577
578
|
this.fetchBeacon = null;
|
|
578
|
-
|
|
579
|
+
|
|
579
580
|
// Peer messaging (must be set by network layer)
|
|
580
581
|
this.requestVerification = null; // (peerId, request) => Promise<proof>
|
|
581
582
|
this.getVerifierPeers = null; // () => [{ nodeId, ip?, asn? }, ...]
|
|
582
|
-
|
|
583
|
+
|
|
583
584
|
// Our own network info (for claimant exclusion when we claim)
|
|
584
585
|
this.ownNetworkInfo = null;
|
|
585
|
-
|
|
586
|
+
|
|
586
587
|
this.stats = {
|
|
587
588
|
claimsInitiated: 0,
|
|
588
589
|
claimsSucceeded: 0,
|
|
@@ -640,9 +641,9 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
640
641
|
const lastClaim = this.cooldowns.get(domain);
|
|
641
642
|
if (lastClaim && Date.now() - lastClaim < this.config.cooldownBetweenClaims) {
|
|
642
643
|
const waitTime = this.config.cooldownBetweenClaims - (Date.now() - lastClaim);
|
|
643
|
-
return {
|
|
644
|
-
success: false,
|
|
645
|
-
error: 'Cooldown active',
|
|
644
|
+
return {
|
|
645
|
+
success: false,
|
|
646
|
+
error: 'Cooldown active',
|
|
646
647
|
retryAfter: waitTime,
|
|
647
648
|
};
|
|
648
649
|
}
|
|
@@ -672,7 +673,7 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
672
673
|
request.status = 'verifying';
|
|
673
674
|
|
|
674
675
|
// Request verification from each peer (in parallel)
|
|
675
|
-
const verificationPromises = verifiers.map(peerId =>
|
|
676
|
+
const verificationPromises = verifiers.map(peerId =>
|
|
676
677
|
this.requestVerificationFromPeer(peerId, request)
|
|
677
678
|
.catch(err => {
|
|
678
679
|
request.addError({ peerId, error: err.message });
|
|
@@ -683,7 +684,7 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
683
684
|
// Wait for all with timeout
|
|
684
685
|
const results = await Promise.race([
|
|
685
686
|
Promise.all(verificationPromises),
|
|
686
|
-
new Promise((_, reject) =>
|
|
687
|
+
new Promise((_, reject) =>
|
|
687
688
|
setTimeout(() => reject(new Error('Verification timeout')), this.config.totalTimeout)
|
|
688
689
|
),
|
|
689
690
|
]);
|
|
@@ -700,9 +701,9 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
700
701
|
request.status = 'completed';
|
|
701
702
|
this.stats.claimsSucceeded++;
|
|
702
703
|
this.cooldowns.set(domain, Date.now());
|
|
703
|
-
|
|
704
|
-
this.emit('claim-succeeded', {
|
|
705
|
-
domain,
|
|
704
|
+
|
|
705
|
+
this.emit('claim-succeeded', {
|
|
706
|
+
domain,
|
|
706
707
|
requestId: request.id,
|
|
707
708
|
proofs: request.proofs.map(p => p.serialize()),
|
|
708
709
|
});
|
|
@@ -716,9 +717,9 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
716
717
|
} else {
|
|
717
718
|
request.status = 'failed';
|
|
718
719
|
this.stats.claimsFailed++;
|
|
719
|
-
|
|
720
|
-
this.emit('claim-failed', {
|
|
721
|
-
domain,
|
|
720
|
+
|
|
721
|
+
this.emit('claim-failed', {
|
|
722
|
+
domain,
|
|
722
723
|
requestId: request.id,
|
|
723
724
|
proofsCollected: request.proofs.length,
|
|
724
725
|
quorumNeeded: this.config.quorumSize,
|
|
@@ -737,9 +738,9 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
737
738
|
} catch (error) {
|
|
738
739
|
request.status = 'failed';
|
|
739
740
|
this.stats.claimsFailed++;
|
|
740
|
-
|
|
741
|
+
|
|
741
742
|
this.emit('claim-error', { domain, requestId: request.id, error: error.message });
|
|
742
|
-
|
|
743
|
+
|
|
743
744
|
return { success: false, error: error.message };
|
|
744
745
|
} finally {
|
|
745
746
|
this.activeRequests.delete(domain);
|
|
@@ -761,7 +762,7 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
761
762
|
}
|
|
762
763
|
|
|
763
764
|
const allPeers = await this.getVerifierPeers();
|
|
764
|
-
|
|
765
|
+
|
|
765
766
|
// Filter out ourselves
|
|
766
767
|
const candidates = allPeers
|
|
767
768
|
.filter(peer => {
|
|
@@ -820,15 +821,15 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
820
821
|
};
|
|
821
822
|
|
|
822
823
|
const startTime = Date.now();
|
|
823
|
-
|
|
824
|
+
|
|
824
825
|
try {
|
|
825
826
|
const result = await this.requestVerification(peerId, verificationRequest);
|
|
826
827
|
const responseTime = Date.now() - startTime;
|
|
827
|
-
|
|
828
|
+
|
|
828
829
|
// Update verifier reputation based on response
|
|
829
830
|
const success = result && result.success && result.proof;
|
|
830
831
|
this.eligibility.updateReputation(peerId, success, responseTime);
|
|
831
|
-
|
|
832
|
+
|
|
832
833
|
return result;
|
|
833
834
|
} catch (error) {
|
|
834
835
|
// Track failed response in reputation
|
|
@@ -855,7 +856,7 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
855
856
|
try {
|
|
856
857
|
// Fetch the beacon from the claimed domain
|
|
857
858
|
const beaconUrl = `https://${domain}${this.config.beaconPath}`;
|
|
858
|
-
|
|
859
|
+
|
|
859
860
|
if (!this.fetchBeacon) {
|
|
860
861
|
throw new Error('Network layer not configured');
|
|
861
862
|
}
|
|
@@ -891,9 +892,9 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
891
892
|
proof.signature = this.identity.sign(signableData);
|
|
892
893
|
|
|
893
894
|
this.stats.verificationsSucceeded++;
|
|
894
|
-
|
|
895
|
-
this.emit('verification-completed', {
|
|
896
|
-
domain,
|
|
895
|
+
|
|
896
|
+
this.emit('verification-completed', {
|
|
897
|
+
domain,
|
|
897
898
|
claimantNodeId,
|
|
898
899
|
beaconHash,
|
|
899
900
|
});
|
|
@@ -902,9 +903,9 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
902
903
|
|
|
903
904
|
} catch (error) {
|
|
904
905
|
this.stats.verificationsFailed++;
|
|
905
|
-
|
|
906
|
-
this.emit('verification-failed', {
|
|
907
|
-
domain,
|
|
906
|
+
|
|
907
|
+
this.emit('verification-failed', {
|
|
908
|
+
domain,
|
|
908
909
|
claimantNodeId,
|
|
909
910
|
error: error.message,
|
|
910
911
|
});
|
|
@@ -924,9 +925,9 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
924
925
|
|
|
925
926
|
// Check nodeId matches claimant
|
|
926
927
|
if (beacon.nodeId !== expectedNodeId) {
|
|
927
|
-
return {
|
|
928
|
-
valid: false,
|
|
929
|
-
error: `NodeID mismatch: expected ${expectedNodeId}, got ${beacon.nodeId}`
|
|
928
|
+
return {
|
|
929
|
+
valid: false,
|
|
930
|
+
error: `NodeID mismatch: expected ${expectedNodeId}, got ${beacon.nodeId}`
|
|
930
931
|
};
|
|
931
932
|
}
|
|
932
933
|
|
|
@@ -938,9 +939,9 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
938
939
|
// Check beacon is fresh (not too old)
|
|
939
940
|
const age = Date.now() - beacon.timestamp;
|
|
940
941
|
if (age > this.config.beaconMaxAge) {
|
|
941
|
-
return {
|
|
942
|
-
valid: false,
|
|
943
|
-
error: `Beacon too old (${Math.round(age/1000)}s, max ${this.config.beaconMaxAge/1000}s)`
|
|
942
|
+
return {
|
|
943
|
+
valid: false,
|
|
944
|
+
error: `Beacon too old (${Math.round(age / 1000)}s, max ${this.config.beaconMaxAge / 1000}s)`
|
|
944
945
|
};
|
|
945
946
|
}
|
|
946
947
|
|
|
@@ -982,8 +983,8 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
982
983
|
verifyProof(proof) {
|
|
983
984
|
try {
|
|
984
985
|
// Deserialize if needed
|
|
985
|
-
const p = proof instanceof DomainVerificationProof
|
|
986
|
-
? proof
|
|
986
|
+
const p = proof instanceof DomainVerificationProof
|
|
987
|
+
? proof
|
|
987
988
|
: DomainVerificationProof.deserialize(proof);
|
|
988
989
|
|
|
989
990
|
// Verify signature
|
|
@@ -1024,7 +1025,7 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
1024
1025
|
*/
|
|
1025
1026
|
verifyDomainClaim(proofs, domain, claimantNodeId, options = {}) {
|
|
1026
1027
|
const checkDiversity = options.checkDiversity !== false;
|
|
1027
|
-
|
|
1028
|
+
|
|
1028
1029
|
if (!Array.isArray(proofs) || proofs.length === 0) {
|
|
1029
1030
|
return { valid: false, error: 'No proofs provided' };
|
|
1030
1031
|
}
|
|
@@ -1063,10 +1064,10 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
1063
1064
|
// SYBIL DEFENSE: Check verifier diversity
|
|
1064
1065
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1065
1066
|
let diversityCheck = { sufficient: true };
|
|
1066
|
-
|
|
1067
|
+
|
|
1067
1068
|
if (checkDiversity && hasQuorum) {
|
|
1068
1069
|
diversityCheck = this.checkVerifierDiversity(validProofs);
|
|
1069
|
-
|
|
1070
|
+
|
|
1070
1071
|
if (!diversityCheck.sufficient) {
|
|
1071
1072
|
return {
|
|
1072
1073
|
valid: false,
|
|
@@ -1103,7 +1104,7 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
1103
1104
|
|
|
1104
1105
|
for (const proof of proofs) {
|
|
1105
1106
|
const info = this.eligibility.nodeNetworkInfo.get(proof.verifierNodeId);
|
|
1106
|
-
|
|
1107
|
+
|
|
1107
1108
|
if (info) {
|
|
1108
1109
|
if (info.subnet) subnets.add(info.subnet);
|
|
1109
1110
|
if (info.asn && info.asn !== 'unknown') asns.add(info.asn);
|
|
@@ -1114,13 +1115,13 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
1114
1115
|
|
|
1115
1116
|
// If we have enough known verifiers, check diversity
|
|
1116
1117
|
const knownCount = proofs.length - unknownNetwork.length;
|
|
1117
|
-
|
|
1118
|
+
|
|
1118
1119
|
// Require at least minDistinctSubnets known verifiers with different subnets
|
|
1119
1120
|
const sufficientSubnets = subnets.size >= this.config.minDistinctSubnets;
|
|
1120
|
-
|
|
1121
|
+
|
|
1121
1122
|
// ASN diversity is a soft requirement (may not always have ASN info)
|
|
1122
|
-
const sufficientASNs = asns.size >= this.config.minDistinctASNs ||
|
|
1123
|
-
|
|
1123
|
+
const sufficientASNs = asns.size >= this.config.minDistinctASNs ||
|
|
1124
|
+
unknownNetwork.length > 0; // Lenient if some are unknown
|
|
1124
1125
|
|
|
1125
1126
|
return {
|
|
1126
1127
|
sufficient: sufficientSubnets,
|
|
@@ -1152,16 +1153,16 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
1152
1153
|
// Basic domain validation
|
|
1153
1154
|
if (!domain || typeof domain !== 'string') return false;
|
|
1154
1155
|
if (domain.length > 253) return false;
|
|
1155
|
-
|
|
1156
|
+
|
|
1156
1157
|
// Must have at least one dot
|
|
1157
1158
|
if (!domain.includes('.')) return false;
|
|
1158
|
-
|
|
1159
|
+
|
|
1159
1160
|
// No protocol prefix
|
|
1160
1161
|
if (domain.includes('://')) return false;
|
|
1161
|
-
|
|
1162
|
+
|
|
1162
1163
|
// No path
|
|
1163
1164
|
if (domain.includes('/')) return false;
|
|
1164
|
-
|
|
1165
|
+
|
|
1165
1166
|
// Basic pattern check
|
|
1166
1167
|
const domainPattern = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/;
|
|
1167
1168
|
return domainPattern.test(domain);
|
|
@@ -1215,7 +1216,7 @@ export class DomainConsensusVerifier extends EventEmitter {
|
|
|
1215
1216
|
*/
|
|
1216
1217
|
restoreState(data) {
|
|
1217
1218
|
if (!data) return;
|
|
1218
|
-
|
|
1219
|
+
|
|
1219
1220
|
// Only restore if version matches (or upgrade logic here)
|
|
1220
1221
|
if (data.version >= 2) {
|
|
1221
1222
|
if (data.eligibility) {
|