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
|
@@ -13,9 +13,12 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { EventEmitter } from 'events';
|
|
16
|
-
import { sha3_256 } from '@noble/hashes/sha3.js';
|
|
17
|
-
import { bytesToHex,
|
|
16
|
+
import { sha3_256 as _nobleSha3 } from '@noble/hashes/sha3.js';
|
|
17
|
+
import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils.js';
|
|
18
|
+
import { ternaryId } from '../utils/ternary-id.js';
|
|
18
19
|
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
|
|
20
|
+
// ACCEL: Hardware-accelerated crypto
|
|
21
|
+
import { sha3_256, mlDsa65Verify } from '../utils/accel.js';
|
|
19
22
|
import { createLogger } from '../utils/logger.js';
|
|
20
23
|
import { MESH_REVOCATION_MESSAGES } from './mesh-revocation.js';
|
|
21
24
|
import { SILICON_PARITY_MESSAGES } from './silicon-parity.js';
|
|
@@ -31,20 +34,20 @@ export const KHATA_TRUST_MESSAGE = {
|
|
|
31
34
|
ATTESTATION_ANNOUNCE: 'khata:trust:attestation:announce',
|
|
32
35
|
ATTESTATION_REQUEST: 'khata:trust:attestation:request',
|
|
33
36
|
REVOCATION_CERTIFICATE: 'khata:trust:revocation:cert',
|
|
34
|
-
|
|
37
|
+
|
|
35
38
|
// Silicon Parity messages
|
|
36
39
|
SILICON_CHALLENGE: 'khata:trust:silicon:challenge',
|
|
37
40
|
SILICON_RESPONSE: 'khata:trust:silicon:response',
|
|
38
41
|
SILICON_IDENTITY: 'khata:trust:silicon:identity',
|
|
39
|
-
|
|
42
|
+
|
|
40
43
|
// Sybil Graph messages
|
|
41
44
|
GRAPH_UPDATE: 'khata:trust:graph:update',
|
|
42
45
|
CLUSTER_ALERT: 'khata:trust:cluster:alert',
|
|
43
|
-
|
|
46
|
+
|
|
44
47
|
// Trust Tier messages
|
|
45
48
|
TIER_ANNOUNCE: 'khata:trust:tier:announce',
|
|
46
49
|
TIER_REQUEST: 'khata:trust:tier:request',
|
|
47
|
-
|
|
50
|
+
|
|
48
51
|
// v2.5.0 Geographic Proof messages
|
|
49
52
|
GEO_PROOF_ANNOUNCE: 'khata:trust:geo:announce',
|
|
50
53
|
GEO_PROOF_REQUEST: 'khata:trust:geo:request',
|
|
@@ -64,10 +67,10 @@ const DEFAULT_CONFIG = {
|
|
|
64
67
|
};
|
|
65
68
|
|
|
66
69
|
/**
|
|
67
|
-
* Generate unique message ID
|
|
70
|
+
* Generate unique message ID (balanced ternary — '666' impossible by design)
|
|
68
71
|
*/
|
|
69
72
|
function generateMessageId() {
|
|
70
|
-
return
|
|
73
|
+
return ternaryId(16);
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
/**
|
|
@@ -79,9 +82,9 @@ function generateMessageId() {
|
|
|
79
82
|
export class KhataTrustIntegration extends EventEmitter {
|
|
80
83
|
constructor(options = {}) {
|
|
81
84
|
super();
|
|
82
|
-
|
|
85
|
+
|
|
83
86
|
this.config = { ...DEFAULT_CONFIG, ...options };
|
|
84
|
-
|
|
87
|
+
|
|
85
88
|
// Core components (injected)
|
|
86
89
|
this.meshRevocation = options.meshRevocation || null;
|
|
87
90
|
this.siliconParity = options.siliconParity || null;
|
|
@@ -89,17 +92,17 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
89
92
|
this.trustRegistry = options.trustRegistry || null;
|
|
90
93
|
this.nodeIdentity = options.nodeIdentity || null;
|
|
91
94
|
this.geoProofService = options.geoProofService || null; // v2.5.0
|
|
92
|
-
|
|
95
|
+
|
|
93
96
|
// Network layer (set by setNetworkLayer)
|
|
94
97
|
this.sendToPeer = null;
|
|
95
98
|
this.broadcastToPeers = null;
|
|
96
|
-
|
|
99
|
+
|
|
97
100
|
// Message deduplication
|
|
98
101
|
this.seenMessages = new Map();
|
|
99
|
-
|
|
102
|
+
|
|
100
103
|
// Pending silicon challenges
|
|
101
104
|
this.pendingChallenges = new Map();
|
|
102
|
-
|
|
105
|
+
|
|
103
106
|
// Statistics
|
|
104
107
|
this.stats = {
|
|
105
108
|
attestationsGossiped: 0,
|
|
@@ -112,11 +115,11 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
112
115
|
geoProofsReceived: 0,
|
|
113
116
|
landmarksAnnounced: 0,
|
|
114
117
|
};
|
|
115
|
-
|
|
118
|
+
|
|
116
119
|
// Cleanup interval
|
|
117
120
|
this.cleanupInterval = setInterval(() => this.cleanup(), 60000);
|
|
118
121
|
}
|
|
119
|
-
|
|
122
|
+
|
|
120
123
|
/**
|
|
121
124
|
* Set network layer functions
|
|
122
125
|
*/
|
|
@@ -124,7 +127,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
124
127
|
this.sendToPeer = sendToPeer;
|
|
125
128
|
this.broadcastToPeers = broadcastToPeers;
|
|
126
129
|
}
|
|
127
|
-
|
|
130
|
+
|
|
128
131
|
/**
|
|
129
132
|
* Set core components
|
|
130
133
|
*/
|
|
@@ -136,11 +139,103 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
136
139
|
if (nodeIdentity) this.nodeIdentity = nodeIdentity;
|
|
137
140
|
if (geoProofService) this.geoProofService = geoProofService;
|
|
138
141
|
}
|
|
139
|
-
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Verify ML-DSA-65 signature on an incoming trust message.
|
|
145
|
+
*
|
|
146
|
+
* Messages that include a `signature` field must prove authorship via
|
|
147
|
+
* the signer's public key. The dokoId/nodeId in the message identifies
|
|
148
|
+
* who signed. Their publicKey is resolved from the mesh peer registry.
|
|
149
|
+
*
|
|
150
|
+
* @param {Object} message - The incoming trust message
|
|
151
|
+
* @param {string} fromPeerId - The peer who forwarded this message
|
|
152
|
+
* @returns {{ valid: boolean, reason?: string }}
|
|
153
|
+
*/
|
|
154
|
+
_verifyMessageSignature(message, fromPeerId) {
|
|
155
|
+
// Messages without signatures are rejected if they should have one
|
|
156
|
+
if (!message.signature) {
|
|
157
|
+
return { valid: false, reason: 'Missing signature' };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Identify the signer's public key. Trust messages carry dokoId or nodeId.
|
|
161
|
+
const signerId = message.dokoId || message.landmark?.nodeId || fromPeerId;
|
|
162
|
+
if (!signerId) {
|
|
163
|
+
return { valid: false, reason: 'Cannot identify message signer' };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Resolve public key from mesh peer registry (same pattern as requirePeerAuth)
|
|
167
|
+
let publicKey = null;
|
|
168
|
+
if (this._resolvePublicKey) {
|
|
169
|
+
publicKey = this._resolvePublicKey(signerId);
|
|
170
|
+
}
|
|
171
|
+
// Fallback: if this is the message dispatcher for another node's forwarded message,
|
|
172
|
+
// the fromPeerId may differ from the original signer. Try the signer directly.
|
|
173
|
+
if (!publicKey && signerId !== fromPeerId && this._resolvePublicKey) {
|
|
174
|
+
publicKey = this._resolvePublicKey(fromPeerId);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!publicKey) {
|
|
178
|
+
return { valid: false, reason: `No public key for signer ${signerId.slice(0, 20)}` };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Reconstruct the signed payload based on message type
|
|
182
|
+
try {
|
|
183
|
+
let payload;
|
|
184
|
+
|
|
185
|
+
// Each announcement type signs a specific subset of fields
|
|
186
|
+
switch (message.type) {
|
|
187
|
+
case KHATA_TRUST_MESSAGE.TIER_ANNOUNCE:
|
|
188
|
+
payload = JSON.stringify({
|
|
189
|
+
dokoId: message.dokoId,
|
|
190
|
+
tier: message.tier,
|
|
191
|
+
weight: message.weight,
|
|
192
|
+
timestamp: message.timestamp,
|
|
193
|
+
});
|
|
194
|
+
break;
|
|
195
|
+
case KHATA_TRUST_MESSAGE.GEO_PROOF_ANNOUNCE:
|
|
196
|
+
payload = JSON.stringify({
|
|
197
|
+
proof: message.proof,
|
|
198
|
+
dokoId: message.dokoId,
|
|
199
|
+
timestamp: message.timestamp,
|
|
200
|
+
});
|
|
201
|
+
break;
|
|
202
|
+
case KHATA_TRUST_MESSAGE.LANDMARK_ANNOUNCE:
|
|
203
|
+
payload = JSON.stringify({
|
|
204
|
+
landmark: message.landmark,
|
|
205
|
+
timestamp: message.timestamp,
|
|
206
|
+
});
|
|
207
|
+
break;
|
|
208
|
+
default:
|
|
209
|
+
// For other signed messages, sign the full payload minus signature/messageId/hops
|
|
210
|
+
const { signature: _s, messageId: _m, hops: _h, ...rest } = message;
|
|
211
|
+
payload = JSON.stringify(rest);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const sigBytes = hexToBytes(message.signature);
|
|
215
|
+
const pubKeyBytes = hexToBytes(publicKey);
|
|
216
|
+
const msgBytes = utf8ToBytes(payload);
|
|
217
|
+
const valid = mlDsa65Verify(sigBytes, msgBytes, pubKeyBytes);
|
|
218
|
+
|
|
219
|
+
return { valid };
|
|
220
|
+
} catch (e) {
|
|
221
|
+
return { valid: false, reason: `Verification error: ${e.message}` };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Set public key resolver function.
|
|
227
|
+
* Called during construction or setNetworkLayer to allow signature verification
|
|
228
|
+
* against the mesh peer registry.
|
|
229
|
+
* @param {Function} resolver - (nodeId) => publicKeyHex | null
|
|
230
|
+
*/
|
|
231
|
+
setPublicKeyResolver(resolver) {
|
|
232
|
+
this._resolvePublicKey = resolver;
|
|
233
|
+
}
|
|
234
|
+
|
|
140
235
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
141
236
|
// MESH REVOCATION GOSSIP
|
|
142
237
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
143
|
-
|
|
238
|
+
|
|
144
239
|
/**
|
|
145
240
|
* Gossip an attestation to the network
|
|
146
241
|
*/
|
|
@@ -149,7 +244,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
149
244
|
log.warn('khata-trust', 'Cannot gossip: network layer not set');
|
|
150
245
|
return;
|
|
151
246
|
}
|
|
152
|
-
|
|
247
|
+
|
|
153
248
|
const message = {
|
|
154
249
|
type: KHATA_TRUST_MESSAGE.ATTESTATION_ANNOUNCE,
|
|
155
250
|
messageId: generateMessageId(),
|
|
@@ -158,19 +253,19 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
158
253
|
ttl: this.config.attestationTTL,
|
|
159
254
|
hops: 0,
|
|
160
255
|
};
|
|
161
|
-
|
|
256
|
+
|
|
162
257
|
// Mark as seen
|
|
163
258
|
const hash = this.computeMessageHash(message);
|
|
164
259
|
this.seenMessages.set(hash, Date.now());
|
|
165
|
-
|
|
260
|
+
|
|
166
261
|
this.stats.attestationsGossiped++;
|
|
167
262
|
log.debug('khata-trust', `Gossiping attestation for ${attestation.targetDokoId}`);
|
|
168
|
-
|
|
263
|
+
|
|
169
264
|
await this.broadcastToPeers(message);
|
|
170
|
-
|
|
265
|
+
|
|
171
266
|
return message.messageId;
|
|
172
267
|
}
|
|
173
|
-
|
|
268
|
+
|
|
174
269
|
/**
|
|
175
270
|
* Handle incoming attestation announcement
|
|
176
271
|
*/
|
|
@@ -181,32 +276,32 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
181
276
|
return;
|
|
182
277
|
}
|
|
183
278
|
this.seenMessages.set(hash, Date.now());
|
|
184
|
-
|
|
279
|
+
|
|
185
280
|
// Check hop limit
|
|
186
281
|
if (message.hops >= this.config.maxHops) {
|
|
187
282
|
return;
|
|
188
283
|
}
|
|
189
|
-
|
|
284
|
+
|
|
190
285
|
// Check TTL
|
|
191
286
|
const age = Date.now() - message.timestamp;
|
|
192
287
|
if (age > message.ttl) {
|
|
193
288
|
return;
|
|
194
289
|
}
|
|
195
|
-
|
|
290
|
+
|
|
196
291
|
this.stats.attestationsReceived++;
|
|
197
|
-
|
|
292
|
+
|
|
198
293
|
// Add to local mesh revocation system
|
|
199
294
|
if (this.meshRevocation) {
|
|
200
295
|
try {
|
|
201
296
|
const result = await this.meshRevocation.addAttestation(message.attestation);
|
|
202
|
-
|
|
297
|
+
|
|
203
298
|
if (result.added) {
|
|
204
299
|
this.emit('attestation-received', {
|
|
205
300
|
attestation: message.attestation,
|
|
206
301
|
fromPeerId,
|
|
207
302
|
revocationTriggered: result.revoked,
|
|
208
303
|
});
|
|
209
|
-
|
|
304
|
+
|
|
210
305
|
// Update sybil graph with attestation
|
|
211
306
|
if (this.sybilGraph) {
|
|
212
307
|
this.sybilGraph.addAttestation(
|
|
@@ -215,7 +310,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
215
310
|
message.timestamp
|
|
216
311
|
);
|
|
217
312
|
}
|
|
218
|
-
|
|
313
|
+
|
|
219
314
|
// Propagate to other peers
|
|
220
315
|
if (this.broadcastToPeers) {
|
|
221
316
|
await this.broadcastToPeers({
|
|
@@ -229,31 +324,31 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
229
324
|
}
|
|
230
325
|
}
|
|
231
326
|
}
|
|
232
|
-
|
|
327
|
+
|
|
233
328
|
/**
|
|
234
329
|
* Broadcast revocation certificate
|
|
235
330
|
*/
|
|
236
331
|
async broadcastRevocationCertificate(certificate) {
|
|
237
332
|
if (!this.broadcastToPeers) return;
|
|
238
|
-
|
|
333
|
+
|
|
239
334
|
const message = {
|
|
240
335
|
type: KHATA_TRUST_MESSAGE.REVOCATION_CERTIFICATE,
|
|
241
336
|
messageId: generateMessageId(),
|
|
242
337
|
certificate,
|
|
243
338
|
timestamp: Date.now(),
|
|
244
339
|
};
|
|
245
|
-
|
|
340
|
+
|
|
246
341
|
log.info('khata-trust', `Broadcasting revocation certificate for ${certificate.targetDokoId}`);
|
|
247
|
-
|
|
342
|
+
|
|
248
343
|
await this.broadcastToPeers(message);
|
|
249
344
|
}
|
|
250
|
-
|
|
345
|
+
|
|
251
346
|
/**
|
|
252
347
|
* Handle incoming revocation certificate
|
|
253
348
|
*/
|
|
254
349
|
async handleRevocationCertificate(message, fromPeerId) {
|
|
255
350
|
const { certificate } = message;
|
|
256
|
-
|
|
351
|
+
|
|
257
352
|
// Verify certificate
|
|
258
353
|
if (this.meshRevocation) {
|
|
259
354
|
const valid = await this.meshRevocation.constructor.verifyCertificate(
|
|
@@ -267,28 +362,28 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
267
362
|
return null;
|
|
268
363
|
}
|
|
269
364
|
);
|
|
270
|
-
|
|
365
|
+
|
|
271
366
|
if (valid) {
|
|
272
367
|
this.emit('revocation-certificate', {
|
|
273
368
|
certificate,
|
|
274
369
|
fromPeerId,
|
|
275
370
|
targetDokoId: certificate.targetDokoId,
|
|
276
371
|
});
|
|
277
|
-
|
|
372
|
+
|
|
278
373
|
// Mark node as revoked in local state
|
|
279
|
-
log.info('khata-trust',
|
|
374
|
+
log.info('khata-trust',
|
|
280
375
|
`Verified revocation certificate for ${certificate.targetDokoId}`);
|
|
281
376
|
} else {
|
|
282
|
-
log.warn('khata-trust',
|
|
377
|
+
log.warn('khata-trust',
|
|
283
378
|
`Invalid revocation certificate for ${certificate.targetDokoId}`);
|
|
284
379
|
}
|
|
285
380
|
}
|
|
286
381
|
}
|
|
287
|
-
|
|
382
|
+
|
|
288
383
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
289
384
|
// SILICON PARITY CHALLENGES
|
|
290
385
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
291
|
-
|
|
386
|
+
|
|
292
387
|
/**
|
|
293
388
|
* Challenge a peer to prove their silicon identity
|
|
294
389
|
*/
|
|
@@ -296,55 +391,55 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
296
391
|
if (!this.sendToPeer) {
|
|
297
392
|
throw new Error('Network layer not set');
|
|
298
393
|
}
|
|
299
|
-
|
|
394
|
+
|
|
300
395
|
const challengeId = generateMessageId();
|
|
301
396
|
const challenge = {
|
|
302
397
|
type: KHATA_TRUST_MESSAGE.SILICON_CHALLENGE,
|
|
303
398
|
challengeId,
|
|
304
399
|
challengerId: this.nodeIdentity?.identity?.nodeId,
|
|
305
|
-
nonce:
|
|
400
|
+
nonce: ternaryId(32),
|
|
306
401
|
timestamp: Date.now(),
|
|
307
402
|
};
|
|
308
|
-
|
|
403
|
+
|
|
309
404
|
// Create promise for response
|
|
310
405
|
return new Promise((resolve, reject) => {
|
|
311
406
|
const timeout = setTimeout(() => {
|
|
312
407
|
this.pendingChallenges.delete(challengeId);
|
|
313
408
|
reject(new Error('Silicon challenge timeout'));
|
|
314
409
|
}, this.config.siliconChallengeTimeout);
|
|
315
|
-
|
|
316
|
-
this.pendingChallenges.set(challengeId, {
|
|
317
|
-
resolve,
|
|
318
|
-
reject,
|
|
410
|
+
|
|
411
|
+
this.pendingChallenges.set(challengeId, {
|
|
412
|
+
resolve,
|
|
413
|
+
reject,
|
|
319
414
|
timeout,
|
|
320
415
|
peerId,
|
|
321
416
|
});
|
|
322
|
-
|
|
417
|
+
|
|
323
418
|
this.stats.siliconChallengesSent++;
|
|
324
419
|
this.sendToPeer(peerId, challenge);
|
|
325
420
|
});
|
|
326
421
|
}
|
|
327
|
-
|
|
422
|
+
|
|
328
423
|
/**
|
|
329
424
|
* Handle incoming silicon challenge
|
|
330
425
|
*/
|
|
331
426
|
async handleSiliconChallenge(message, fromPeerId) {
|
|
332
427
|
this.stats.siliconChallengesReceived++;
|
|
333
|
-
|
|
428
|
+
|
|
334
429
|
if (!this.siliconParity) {
|
|
335
430
|
log.warn('khata-trust', 'Received silicon challenge but SiliconParity not configured');
|
|
336
431
|
return;
|
|
337
432
|
}
|
|
338
|
-
|
|
433
|
+
|
|
339
434
|
// Collect or retrieve our silicon identity
|
|
340
435
|
const myDokoId = this.nodeIdentity?.identity?.nodeId;
|
|
341
436
|
let identity = this.siliconParity.getIdentity(myDokoId);
|
|
342
|
-
|
|
437
|
+
|
|
343
438
|
if (!identity) {
|
|
344
439
|
// Collect identity on demand
|
|
345
440
|
identity = await this.siliconParity.collectIdentity(myDokoId);
|
|
346
441
|
}
|
|
347
|
-
|
|
442
|
+
|
|
348
443
|
// Create response
|
|
349
444
|
const response = {
|
|
350
445
|
type: KHATA_TRUST_MESSAGE.SILICON_RESPONSE,
|
|
@@ -352,10 +447,10 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
352
447
|
responderId: myDokoId,
|
|
353
448
|
identity: identity.toJSON(),
|
|
354
449
|
challengeNonce: message.nonce,
|
|
355
|
-
responseNonce:
|
|
450
|
+
responseNonce: ternaryId(16),
|
|
356
451
|
timestamp: Date.now(),
|
|
357
452
|
};
|
|
358
|
-
|
|
453
|
+
|
|
359
454
|
// Sign response
|
|
360
455
|
if (this.nodeIdentity) {
|
|
361
456
|
const payload = JSON.stringify({
|
|
@@ -366,12 +461,12 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
366
461
|
});
|
|
367
462
|
response.signature = this.nodeIdentity.sign(payload);
|
|
368
463
|
}
|
|
369
|
-
|
|
464
|
+
|
|
370
465
|
if (this.sendToPeer) {
|
|
371
466
|
await this.sendToPeer(fromPeerId, response);
|
|
372
467
|
}
|
|
373
468
|
}
|
|
374
|
-
|
|
469
|
+
|
|
375
470
|
/**
|
|
376
471
|
* Handle silicon challenge response
|
|
377
472
|
*/
|
|
@@ -380,10 +475,10 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
380
475
|
if (!pending) {
|
|
381
476
|
return; // Unknown or expired challenge
|
|
382
477
|
}
|
|
383
|
-
|
|
478
|
+
|
|
384
479
|
clearTimeout(pending.timeout);
|
|
385
480
|
this.pendingChallenges.delete(message.challengeId);
|
|
386
|
-
|
|
481
|
+
|
|
387
482
|
// Verify response
|
|
388
483
|
const result = {
|
|
389
484
|
valid: true,
|
|
@@ -391,7 +486,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
391
486
|
responderId: message.responderId,
|
|
392
487
|
issues: [],
|
|
393
488
|
};
|
|
394
|
-
|
|
489
|
+
|
|
395
490
|
// Check signature if we have the public key
|
|
396
491
|
if (message.signature && this.nodeIdentity) {
|
|
397
492
|
const payload = JSON.stringify({
|
|
@@ -400,7 +495,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
400
495
|
responseNonce: message.responseNonce,
|
|
401
496
|
fingerprint: message.identity.aesFingerprint,
|
|
402
497
|
});
|
|
403
|
-
|
|
498
|
+
|
|
404
499
|
// Verify signature with responder's public key from trust registry
|
|
405
500
|
if (this.trustRegistry && message.responderId) {
|
|
406
501
|
try {
|
|
@@ -409,9 +504,9 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
409
504
|
const payloadBytes = utf8ToBytes(payload);
|
|
410
505
|
const publicKey = hexToBytes(profile.publicKey);
|
|
411
506
|
const signature = hexToBytes(message.signature);
|
|
412
|
-
|
|
507
|
+
|
|
413
508
|
// ml_dsa65.verify(signature, message, publicKey)
|
|
414
|
-
const sigValid =
|
|
509
|
+
const sigValid = mlDsa65Verify(signature, payloadBytes, publicKey);
|
|
415
510
|
if (!sigValid) {
|
|
416
511
|
result.valid = false;
|
|
417
512
|
result.issues.push('Invalid signature on silicon response');
|
|
@@ -426,33 +521,33 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
426
521
|
}
|
|
427
522
|
}
|
|
428
523
|
}
|
|
429
|
-
|
|
524
|
+
|
|
430
525
|
// Check for VM indicators
|
|
431
526
|
if (!message.identity.isRealSilicon) {
|
|
432
527
|
result.issues.push('VM or emulation detected');
|
|
433
528
|
}
|
|
434
|
-
|
|
529
|
+
|
|
435
530
|
// Check core count for weight calculation
|
|
436
531
|
if (message.identity.coreCount > 64) {
|
|
437
532
|
result.issues.push(`High core count: ${message.identity.coreCount}`);
|
|
438
533
|
}
|
|
439
|
-
|
|
534
|
+
|
|
440
535
|
pending.resolve(result);
|
|
441
536
|
}
|
|
442
|
-
|
|
537
|
+
|
|
443
538
|
/**
|
|
444
539
|
* Announce silicon identity to network
|
|
445
540
|
*/
|
|
446
541
|
async announceSiliconIdentity() {
|
|
447
542
|
if (!this.broadcastToPeers || !this.siliconParity) return;
|
|
448
|
-
|
|
543
|
+
|
|
449
544
|
const myDokoId = this.nodeIdentity?.identity?.nodeId;
|
|
450
545
|
let identity = this.siliconParity.getIdentity(myDokoId);
|
|
451
|
-
|
|
546
|
+
|
|
452
547
|
if (!identity) {
|
|
453
548
|
identity = await this.siliconParity.collectIdentity(myDokoId);
|
|
454
549
|
}
|
|
455
|
-
|
|
550
|
+
|
|
456
551
|
const message = {
|
|
457
552
|
type: KHATA_TRUST_MESSAGE.SILICON_IDENTITY,
|
|
458
553
|
messageId: generateMessageId(),
|
|
@@ -460,23 +555,23 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
460
555
|
identity: identity.toJSON(),
|
|
461
556
|
timestamp: Date.now(),
|
|
462
557
|
};
|
|
463
|
-
|
|
558
|
+
|
|
464
559
|
await this.broadcastToPeers(message);
|
|
465
560
|
}
|
|
466
|
-
|
|
561
|
+
|
|
467
562
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
468
563
|
// SYBIL GRAPH UPDATES
|
|
469
564
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
470
|
-
|
|
565
|
+
|
|
471
566
|
/**
|
|
472
567
|
* Share graph update with network
|
|
473
568
|
*/
|
|
474
569
|
async shareGraphUpdate() {
|
|
475
570
|
if (!this.broadcastToPeers || !this.sybilGraph) return;
|
|
476
|
-
|
|
571
|
+
|
|
477
572
|
const stats = this.sybilGraph.getGraphStats();
|
|
478
573
|
const myDokoId = this.nodeIdentity?.identity?.nodeId;
|
|
479
|
-
|
|
574
|
+
|
|
480
575
|
const message = {
|
|
481
576
|
type: KHATA_TRUST_MESSAGE.GRAPH_UPDATE,
|
|
482
577
|
messageId: generateMessageId(),
|
|
@@ -484,31 +579,31 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
484
579
|
graphStats: stats,
|
|
485
580
|
timestamp: Date.now(),
|
|
486
581
|
};
|
|
487
|
-
|
|
582
|
+
|
|
488
583
|
await this.broadcastToPeers(message);
|
|
489
584
|
}
|
|
490
|
-
|
|
585
|
+
|
|
491
586
|
/**
|
|
492
587
|
* Handle incoming graph update
|
|
493
588
|
*/
|
|
494
589
|
async handleGraphUpdate(message, fromPeerId) {
|
|
495
590
|
this.stats.graphUpdatesReceived++;
|
|
496
|
-
|
|
591
|
+
|
|
497
592
|
this.emit('graph-update', {
|
|
498
593
|
senderId: message.senderId,
|
|
499
594
|
stats: message.graphStats,
|
|
500
595
|
fromPeerId,
|
|
501
596
|
});
|
|
502
|
-
|
|
597
|
+
|
|
503
598
|
// Could merge graph data here if implementing distributed analysis
|
|
504
599
|
}
|
|
505
|
-
|
|
600
|
+
|
|
506
601
|
/**
|
|
507
602
|
* Broadcast cluster alert
|
|
508
603
|
*/
|
|
509
604
|
async broadcastClusterAlert(cluster) {
|
|
510
605
|
if (!this.broadcastToPeers) return;
|
|
511
|
-
|
|
606
|
+
|
|
512
607
|
const message = {
|
|
513
608
|
type: KHATA_TRUST_MESSAGE.CLUSTER_ALERT,
|
|
514
609
|
messageId: generateMessageId(),
|
|
@@ -522,53 +617,53 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
522
617
|
detectedBy: this.nodeIdentity?.identity?.nodeId,
|
|
523
618
|
timestamp: Date.now(),
|
|
524
619
|
};
|
|
525
|
-
|
|
526
|
-
log.warn('khata-trust',
|
|
620
|
+
|
|
621
|
+
log.warn('khata-trust',
|
|
527
622
|
`Broadcasting cluster alert: ${cluster.size} nodes, score=${cluster.suspicionScore}`);
|
|
528
|
-
|
|
623
|
+
|
|
529
624
|
await this.broadcastToPeers(message);
|
|
530
625
|
}
|
|
531
|
-
|
|
626
|
+
|
|
532
627
|
/**
|
|
533
628
|
* Handle incoming cluster alert
|
|
534
629
|
*/
|
|
535
630
|
async handleClusterAlert(message, fromPeerId) {
|
|
536
631
|
this.stats.clusterAlertsReceived++;
|
|
537
|
-
|
|
632
|
+
|
|
538
633
|
this.emit('cluster-alert', {
|
|
539
634
|
cluster: message.cluster,
|
|
540
635
|
detectedBy: message.detectedBy,
|
|
541
636
|
fromPeerId,
|
|
542
637
|
});
|
|
543
|
-
|
|
638
|
+
|
|
544
639
|
// Cross-reference with our own analysis
|
|
545
640
|
if (this.sybilGraph) {
|
|
546
641
|
const ourAnalysis = this.sybilGraph.analyze();
|
|
547
|
-
const matchingCluster = ourAnalysis.clusters.find(c =>
|
|
642
|
+
const matchingCluster = ourAnalysis.clusters.find(c =>
|
|
548
643
|
c.nodes.some(n => message.cluster.nodes.includes(n))
|
|
549
644
|
);
|
|
550
|
-
|
|
645
|
+
|
|
551
646
|
if (matchingCluster) {
|
|
552
|
-
log.info('khata-trust',
|
|
647
|
+
log.info('khata-trust',
|
|
553
648
|
`Cluster alert confirmed by local analysis: ${matchingCluster.size} nodes`);
|
|
554
649
|
}
|
|
555
650
|
}
|
|
556
651
|
}
|
|
557
|
-
|
|
652
|
+
|
|
558
653
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
559
654
|
// TRUST TIER ANNOUNCEMENTS
|
|
560
655
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
561
|
-
|
|
656
|
+
|
|
562
657
|
/**
|
|
563
658
|
* Announce our trust tier
|
|
564
659
|
*/
|
|
565
660
|
async announceTier() {
|
|
566
661
|
if (!this.broadcastToPeers || !this.trustRegistry) return;
|
|
567
|
-
|
|
662
|
+
|
|
568
663
|
const myDokoId = this.nodeIdentity?.identity?.nodeId;
|
|
569
664
|
const tier = await this.trustRegistry.getTier(myDokoId);
|
|
570
665
|
const weight = await this.trustRegistry.getWeight(myDokoId);
|
|
571
|
-
|
|
666
|
+
|
|
572
667
|
const message = {
|
|
573
668
|
type: KHATA_TRUST_MESSAGE.TIER_ANNOUNCE,
|
|
574
669
|
messageId: generateMessageId(),
|
|
@@ -577,7 +672,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
577
672
|
weight,
|
|
578
673
|
timestamp: Date.now(),
|
|
579
674
|
};
|
|
580
|
-
|
|
675
|
+
|
|
581
676
|
// Sign announcement
|
|
582
677
|
if (this.nodeIdentity) {
|
|
583
678
|
const payload = JSON.stringify({
|
|
@@ -588,26 +683,26 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
588
683
|
});
|
|
589
684
|
message.signature = this.nodeIdentity.sign(payload);
|
|
590
685
|
}
|
|
591
|
-
|
|
686
|
+
|
|
592
687
|
await this.broadcastToPeers(message);
|
|
593
688
|
}
|
|
594
|
-
|
|
689
|
+
|
|
595
690
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
596
691
|
// GEOGRAPHIC PROOF (v2.5.0)
|
|
597
692
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
598
|
-
|
|
693
|
+
|
|
599
694
|
/**
|
|
600
695
|
* Announce our geographic proof to the network
|
|
601
696
|
*/
|
|
602
697
|
async announceGeoProof() {
|
|
603
698
|
if (!this.broadcastToPeers || !this.geoProofService) return;
|
|
604
|
-
|
|
699
|
+
|
|
605
700
|
const proof = this.geoProofService.getProof();
|
|
606
701
|
if (!proof) {
|
|
607
702
|
log.debug('khata-trust', 'No geo-proof available to announce');
|
|
608
703
|
return;
|
|
609
704
|
}
|
|
610
|
-
|
|
705
|
+
|
|
611
706
|
const message = {
|
|
612
707
|
type: KHATA_TRUST_MESSAGE.GEO_PROOF_ANNOUNCE,
|
|
613
708
|
messageId: generateMessageId(),
|
|
@@ -616,7 +711,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
616
711
|
timestamp: Date.now(),
|
|
617
712
|
hops: 0,
|
|
618
713
|
};
|
|
619
|
-
|
|
714
|
+
|
|
620
715
|
// Sign announcement
|
|
621
716
|
if (this.nodeIdentity) {
|
|
622
717
|
const payload = JSON.stringify({
|
|
@@ -626,14 +721,14 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
626
721
|
});
|
|
627
722
|
message.signature = this.nodeIdentity.sign(payload);
|
|
628
723
|
}
|
|
629
|
-
|
|
724
|
+
|
|
630
725
|
this.stats.geoProofsGossiped++;
|
|
631
726
|
log.debug('khata-trust', `Announcing geo-proof with ${proof.exclusionZones.length} zones`);
|
|
632
|
-
|
|
727
|
+
|
|
633
728
|
await this.broadcastToPeers(message);
|
|
634
729
|
return message.messageId;
|
|
635
730
|
}
|
|
636
|
-
|
|
731
|
+
|
|
637
732
|
/**
|
|
638
733
|
* Request geographic proof from a specific peer
|
|
639
734
|
*/
|
|
@@ -641,18 +736,18 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
641
736
|
if (!this.sendToPeer) {
|
|
642
737
|
throw new Error('Network layer not set');
|
|
643
738
|
}
|
|
644
|
-
|
|
739
|
+
|
|
645
740
|
const message = {
|
|
646
741
|
type: KHATA_TRUST_MESSAGE.GEO_PROOF_REQUEST,
|
|
647
742
|
messageId: generateMessageId(),
|
|
648
743
|
requesterId: this.nodeIdentity?.identity?.nodeId,
|
|
649
744
|
timestamp: Date.now(),
|
|
650
745
|
};
|
|
651
|
-
|
|
746
|
+
|
|
652
747
|
await this.sendToPeer(peerId, message);
|
|
653
748
|
return message.messageId;
|
|
654
749
|
}
|
|
655
|
-
|
|
750
|
+
|
|
656
751
|
/**
|
|
657
752
|
* Handle incoming geo-proof announcement
|
|
658
753
|
*/
|
|
@@ -663,20 +758,20 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
663
758
|
return;
|
|
664
759
|
}
|
|
665
760
|
this.seenMessages.set(hash, Date.now());
|
|
666
|
-
|
|
761
|
+
|
|
667
762
|
// Check hop limit
|
|
668
763
|
if (message.hops >= this.config.maxHops) {
|
|
669
764
|
return;
|
|
670
765
|
}
|
|
671
|
-
|
|
766
|
+
|
|
672
767
|
this.stats.geoProofsReceived++;
|
|
673
|
-
|
|
768
|
+
|
|
674
769
|
this.emit('geo-proof-received', {
|
|
675
770
|
proof: message.proof,
|
|
676
771
|
dokoId: message.dokoId,
|
|
677
772
|
fromPeerId,
|
|
678
773
|
});
|
|
679
|
-
|
|
774
|
+
|
|
680
775
|
// Store in local registry if trust registry supports it
|
|
681
776
|
if (this.trustRegistry && this.trustRegistry.setGeoProof) {
|
|
682
777
|
try {
|
|
@@ -685,7 +780,18 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
685
780
|
log.warn('khata-trust', `Failed to store geo-proof: ${err.message}`);
|
|
686
781
|
}
|
|
687
782
|
}
|
|
688
|
-
|
|
783
|
+
|
|
784
|
+
// Cache deserialized proof in GeoProofService for /geo/verify lookups
|
|
785
|
+
if (this.geoProofService && message.proof) {
|
|
786
|
+
try {
|
|
787
|
+
const { GeographicProof } = await import('./geo-proof.js');
|
|
788
|
+
const peerProof = GeographicProof.deserialize(message.proof);
|
|
789
|
+
this.geoProofService.proofs.set(peerProof.nodeId || message.dokoId, peerProof);
|
|
790
|
+
} catch (err) {
|
|
791
|
+
log.warn('khata-trust', `Failed to cache peer geo-proof: ${err.message}`);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
689
795
|
// Propagate to other peers
|
|
690
796
|
if (this.broadcastToPeers) {
|
|
691
797
|
await this.broadcastToPeers({
|
|
@@ -694,19 +800,19 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
694
800
|
}, fromPeerId);
|
|
695
801
|
}
|
|
696
802
|
}
|
|
697
|
-
|
|
803
|
+
|
|
698
804
|
/**
|
|
699
805
|
* Handle incoming geo-proof request
|
|
700
806
|
*/
|
|
701
807
|
async handleGeoProofRequest(message, fromPeerId) {
|
|
702
808
|
if (!this.sendToPeer || !this.geoProofService) return;
|
|
703
|
-
|
|
809
|
+
|
|
704
810
|
const proof = this.geoProofService.getProof();
|
|
705
811
|
if (!proof) {
|
|
706
812
|
log.debug('khata-trust', 'No geo-proof available to respond with');
|
|
707
813
|
return;
|
|
708
814
|
}
|
|
709
|
-
|
|
815
|
+
|
|
710
816
|
const response = {
|
|
711
817
|
type: KHATA_TRUST_MESSAGE.GEO_PROOF_RESPONSE,
|
|
712
818
|
requestId: message.messageId,
|
|
@@ -714,10 +820,10 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
714
820
|
dokoId: this.nodeIdentity?.identity?.nodeId,
|
|
715
821
|
timestamp: Date.now(),
|
|
716
822
|
};
|
|
717
|
-
|
|
823
|
+
|
|
718
824
|
await this.sendToPeer(fromPeerId, response);
|
|
719
825
|
}
|
|
720
|
-
|
|
826
|
+
|
|
721
827
|
/**
|
|
722
828
|
* Handle incoming geo-proof response
|
|
723
829
|
*/
|
|
@@ -729,13 +835,13 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
729
835
|
fromPeerId,
|
|
730
836
|
});
|
|
731
837
|
}
|
|
732
|
-
|
|
838
|
+
|
|
733
839
|
/**
|
|
734
840
|
* Announce this node as a landmark
|
|
735
841
|
*/
|
|
736
842
|
async announceLandmark(landmarkInfo) {
|
|
737
843
|
if (!this.broadcastToPeers) return;
|
|
738
|
-
|
|
844
|
+
|
|
739
845
|
const message = {
|
|
740
846
|
type: KHATA_TRUST_MESSAGE.LANDMARK_ANNOUNCE,
|
|
741
847
|
messageId: generateMessageId(),
|
|
@@ -751,7 +857,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
751
857
|
},
|
|
752
858
|
timestamp: Date.now(),
|
|
753
859
|
};
|
|
754
|
-
|
|
860
|
+
|
|
755
861
|
// Sign announcement
|
|
756
862
|
if (this.nodeIdentity) {
|
|
757
863
|
const payload = JSON.stringify({
|
|
@@ -760,14 +866,14 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
760
866
|
});
|
|
761
867
|
message.signature = this.nodeIdentity.sign(payload);
|
|
762
868
|
}
|
|
763
|
-
|
|
869
|
+
|
|
764
870
|
this.stats.landmarksAnnounced++;
|
|
765
871
|
log.info('khata-trust', `Announcing as landmark: ${landmarkInfo.name} (${landmarkInfo.region})`);
|
|
766
|
-
|
|
872
|
+
|
|
767
873
|
await this.broadcastToPeers(message);
|
|
768
874
|
return message.messageId;
|
|
769
875
|
}
|
|
770
|
-
|
|
876
|
+
|
|
771
877
|
/**
|
|
772
878
|
* Handle incoming landmark announcement
|
|
773
879
|
*/
|
|
@@ -778,29 +884,29 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
778
884
|
return;
|
|
779
885
|
}
|
|
780
886
|
this.seenMessages.set(hash, Date.now());
|
|
781
|
-
|
|
887
|
+
|
|
782
888
|
this.emit('landmark-announce', {
|
|
783
889
|
landmark: message.landmark,
|
|
784
890
|
fromPeerId,
|
|
785
891
|
});
|
|
786
|
-
|
|
892
|
+
|
|
787
893
|
// Register landmark if geo-proof service is available
|
|
788
894
|
if (this.geoProofService) {
|
|
789
895
|
try {
|
|
790
896
|
this.geoProofService.registerLandmark(message.landmark);
|
|
791
|
-
log.info('khata-trust',
|
|
897
|
+
log.info('khata-trust',
|
|
792
898
|
`Registered landmark from gossip: ${message.landmark.name} (${message.landmark.region})`);
|
|
793
899
|
} catch (err) {
|
|
794
900
|
log.warn('khata-trust', `Failed to register landmark: ${err.message}`);
|
|
795
901
|
}
|
|
796
902
|
}
|
|
797
|
-
|
|
903
|
+
|
|
798
904
|
// Propagate to other peers
|
|
799
905
|
if (this.broadcastToPeers) {
|
|
800
906
|
await this.broadcastToPeers(message, fromPeerId);
|
|
801
907
|
}
|
|
802
908
|
}
|
|
803
|
-
|
|
909
|
+
|
|
804
910
|
/**
|
|
805
911
|
* Request landmark verification from peer
|
|
806
912
|
*/
|
|
@@ -808,7 +914,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
808
914
|
if (!this.sendToPeer) {
|
|
809
915
|
throw new Error('Network layer not set');
|
|
810
916
|
}
|
|
811
|
-
|
|
917
|
+
|
|
812
918
|
const message = {
|
|
813
919
|
type: KHATA_TRUST_MESSAGE.LANDMARK_VERIFY,
|
|
814
920
|
messageId: generateMessageId(),
|
|
@@ -816,19 +922,40 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
816
922
|
requesterId: this.nodeIdentity?.identity?.nodeId,
|
|
817
923
|
timestamp: Date.now(),
|
|
818
924
|
};
|
|
819
|
-
|
|
925
|
+
|
|
820
926
|
await this.sendToPeer(peerId, message);
|
|
821
927
|
return message.messageId;
|
|
822
928
|
}
|
|
823
|
-
|
|
929
|
+
|
|
824
930
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
825
931
|
// MESSAGE ROUTING
|
|
826
932
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
827
|
-
|
|
933
|
+
|
|
828
934
|
/**
|
|
829
935
|
* Handle incoming trust message
|
|
830
936
|
*/
|
|
831
937
|
async handleMessage(message, fromPeerId) {
|
|
938
|
+
// Messages that carry a signature MUST pass verification.
|
|
939
|
+
// Requests and challenges without signatures are allowed through.
|
|
940
|
+
const SIGNED_TYPES = new Set([
|
|
941
|
+
KHATA_TRUST_MESSAGE.TIER_ANNOUNCE,
|
|
942
|
+
KHATA_TRUST_MESSAGE.GEO_PROOF_ANNOUNCE,
|
|
943
|
+
KHATA_TRUST_MESSAGE.LANDMARK_ANNOUNCE,
|
|
944
|
+
KHATA_TRUST_MESSAGE.SILICON_IDENTITY,
|
|
945
|
+
KHATA_TRUST_MESSAGE.GRAPH_UPDATE,
|
|
946
|
+
KHATA_TRUST_MESSAGE.ATTESTATION_ANNOUNCE,
|
|
947
|
+
KHATA_TRUST_MESSAGE.REVOCATION_CERTIFICATE,
|
|
948
|
+
]);
|
|
949
|
+
|
|
950
|
+
if (SIGNED_TYPES.has(message.type)) {
|
|
951
|
+
const sigResult = this._verifyMessageSignature(message, fromPeerId);
|
|
952
|
+
if (!sigResult.valid) {
|
|
953
|
+
log.warn('khata-trust',
|
|
954
|
+
`Rejected unsigned/forged trust message: ${message.type} from ${fromPeerId?.slice(0, 20)} — ${sigResult.reason}`);
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
832
959
|
switch (message.type) {
|
|
833
960
|
// Mesh Revocation
|
|
834
961
|
case KHATA_TRUST_MESSAGE.ATTESTATION_ANNOUNCE:
|
|
@@ -837,7 +964,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
837
964
|
case KHATA_TRUST_MESSAGE.REVOCATION_CERTIFICATE:
|
|
838
965
|
await this.handleRevocationCertificate(message, fromPeerId);
|
|
839
966
|
break;
|
|
840
|
-
|
|
967
|
+
|
|
841
968
|
// Silicon Parity
|
|
842
969
|
case KHATA_TRUST_MESSAGE.SILICON_CHALLENGE:
|
|
843
970
|
await this.handleSiliconChallenge(message, fromPeerId);
|
|
@@ -848,7 +975,7 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
848
975
|
case KHATA_TRUST_MESSAGE.SILICON_IDENTITY:
|
|
849
976
|
this.emit('silicon-identity', { identity: message.identity, dokoId: message.dokoId, fromPeerId });
|
|
850
977
|
break;
|
|
851
|
-
|
|
978
|
+
|
|
852
979
|
// Sybil Graph
|
|
853
980
|
case KHATA_TRUST_MESSAGE.GRAPH_UPDATE:
|
|
854
981
|
await this.handleGraphUpdate(message, fromPeerId);
|
|
@@ -856,12 +983,12 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
856
983
|
case KHATA_TRUST_MESSAGE.CLUSTER_ALERT:
|
|
857
984
|
await this.handleClusterAlert(message, fromPeerId);
|
|
858
985
|
break;
|
|
859
|
-
|
|
986
|
+
|
|
860
987
|
// Trust Tier
|
|
861
988
|
case KHATA_TRUST_MESSAGE.TIER_ANNOUNCE:
|
|
862
989
|
this.emit('tier-announce', { dokoId: message.dokoId, tier: message.tier, weight: message.weight, fromPeerId });
|
|
863
990
|
break;
|
|
864
|
-
|
|
991
|
+
|
|
865
992
|
// Geographic Proof (v2.5.0)
|
|
866
993
|
case KHATA_TRUST_MESSAGE.GEO_PROOF_ANNOUNCE:
|
|
867
994
|
await this.handleGeoProofAnnounce(message, fromPeerId);
|
|
@@ -878,26 +1005,26 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
878
1005
|
case KHATA_TRUST_MESSAGE.LANDMARK_VERIFY:
|
|
879
1006
|
this.emit('landmark-verify-request', { landmarkId: message.landmarkId, requesterId: message.requesterId, fromPeerId });
|
|
880
1007
|
break;
|
|
881
|
-
|
|
1008
|
+
|
|
882
1009
|
default:
|
|
883
1010
|
// Unknown message type - might be handled by base KHATA protocol
|
|
884
1011
|
return false;
|
|
885
1012
|
}
|
|
886
|
-
|
|
1013
|
+
|
|
887
1014
|
return true;
|
|
888
1015
|
}
|
|
889
|
-
|
|
1016
|
+
|
|
890
1017
|
/**
|
|
891
1018
|
* Check if message type is a trust message
|
|
892
1019
|
*/
|
|
893
1020
|
isTrustMessage(messageType) {
|
|
894
1021
|
return Object.values(KHATA_TRUST_MESSAGE).includes(messageType);
|
|
895
1022
|
}
|
|
896
|
-
|
|
1023
|
+
|
|
897
1024
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
898
1025
|
// UTILITIES
|
|
899
1026
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
900
|
-
|
|
1027
|
+
|
|
901
1028
|
/**
|
|
902
1029
|
* Compute message hash for deduplication
|
|
903
1030
|
*/
|
|
@@ -908,21 +1035,21 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
908
1035
|
});
|
|
909
1036
|
return bytesToHex(sha3_256(new TextEncoder().encode(content)));
|
|
910
1037
|
}
|
|
911
|
-
|
|
1038
|
+
|
|
912
1039
|
/**
|
|
913
1040
|
* Cleanup old data
|
|
914
1041
|
*/
|
|
915
1042
|
cleanup() {
|
|
916
1043
|
const now = Date.now();
|
|
917
1044
|
const maxAge = this.config.attestationTTL;
|
|
918
|
-
|
|
1045
|
+
|
|
919
1046
|
for (const [hash, timestamp] of this.seenMessages.entries()) {
|
|
920
1047
|
if (now - timestamp > maxAge) {
|
|
921
1048
|
this.seenMessages.delete(hash);
|
|
922
1049
|
}
|
|
923
1050
|
}
|
|
924
1051
|
}
|
|
925
|
-
|
|
1052
|
+
|
|
926
1053
|
/**
|
|
927
1054
|
* Get statistics
|
|
928
1055
|
*/
|
|
@@ -933,13 +1060,13 @@ export class KhataTrustIntegration extends EventEmitter {
|
|
|
933
1060
|
pendingChallenges: this.pendingChallenges.size,
|
|
934
1061
|
};
|
|
935
1062
|
}
|
|
936
|
-
|
|
1063
|
+
|
|
937
1064
|
/**
|
|
938
1065
|
* Shutdown
|
|
939
1066
|
*/
|
|
940
1067
|
destroy() {
|
|
941
1068
|
clearInterval(this.cleanupInterval);
|
|
942
|
-
|
|
1069
|
+
|
|
943
1070
|
// Cleanup pending challenges
|
|
944
1071
|
for (const [id, pending] of this.pendingChallenges.entries()) {
|
|
945
1072
|
clearTimeout(pending.timeout);
|