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
|
@@ -11,12 +11,18 @@
|
|
|
11
11
|
* @version 1.0.0
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { sha3_256 } from '@noble/hashes/sha3.js';
|
|
14
|
+
import { sha3_256 as _nobleSha3 } from '@noble/hashes/sha3.js';
|
|
15
15
|
import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
|
|
16
16
|
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
|
|
17
|
+
// ACCEL: Hardware-accelerated crypto
|
|
18
|
+
import { sha3_256, mlDsa65Sign, mlDsa65Verify } from '../utils/accel.js';
|
|
17
19
|
import { EventEmitter } from 'events';
|
|
18
20
|
import { createLogger } from '../utils/logger.js';
|
|
19
21
|
|
|
22
|
+
// ═══ TRIBHUJ — Balanced ternary for revocation status ═══
|
|
23
|
+
// POSITIVE: retained (not revoked), NEUTRAL: forming (insufficient network), NEGATIVE: revoked
|
|
24
|
+
import { POSITIVE, NEUTRAL, NEGATIVE } from '../oracle/tribhuj.js';
|
|
25
|
+
|
|
20
26
|
const log = createLogger('security:mesh-revocation');
|
|
21
27
|
|
|
22
28
|
/**
|
|
@@ -85,7 +91,7 @@ export class Attestation {
|
|
|
85
91
|
*/
|
|
86
92
|
sign(privateKey) {
|
|
87
93
|
const bytes = this.getSignableBytes();
|
|
88
|
-
const sig =
|
|
94
|
+
const sig = mlDsa65Sign(bytes, privateKey);
|
|
89
95
|
this.signature = bytesToHex(sig);
|
|
90
96
|
return this;
|
|
91
97
|
}
|
|
@@ -100,7 +106,7 @@ export class Attestation {
|
|
|
100
106
|
const bytes = this.getSignableBytes();
|
|
101
107
|
const sig = hexToBytes(this.signature);
|
|
102
108
|
const pubKey = typeof publicKey === 'string' ? hexToBytes(publicKey) : publicKey;
|
|
103
|
-
return
|
|
109
|
+
return mlDsa65Verify(sig, bytes, pubKey);
|
|
104
110
|
} catch (e) {
|
|
105
111
|
return false;
|
|
106
112
|
}
|
|
@@ -220,11 +226,16 @@ export class RevocationState {
|
|
|
220
226
|
|
|
221
227
|
/**
|
|
222
228
|
* Check if threshold is met
|
|
223
|
-
* This is THE mathematical determination
|
|
229
|
+
* This is THE mathematical determination.
|
|
230
|
+
*
|
|
231
|
+
* Returns `revoked` boolean (backward compat) plus `status` trit:
|
|
232
|
+
* POSITIVE (+1): retained — below threshold, DOKO is valid
|
|
233
|
+
* NEUTRAL ( 0): forming — insufficient network to determine
|
|
234
|
+
* NEGATIVE (-1): revoked — threshold met, DOKO is revoked
|
|
224
235
|
*/
|
|
225
236
|
isRevoked(activeNodeCount, config = DEFAULT_CONFIG) {
|
|
226
237
|
if (activeNodeCount < config.minNodes) {
|
|
227
|
-
return { revoked: false, reason: 'INSUFFICIENT_NETWORK' };
|
|
238
|
+
return { revoked: false, status: NEUTRAL, reason: 'INSUFFICIENT_NETWORK' };
|
|
228
239
|
}
|
|
229
240
|
|
|
230
241
|
const threshold = Math.ceil(activeNodeCount * config.threshold);
|
|
@@ -233,6 +244,7 @@ export class RevocationState {
|
|
|
233
244
|
if (count >= threshold) {
|
|
234
245
|
return {
|
|
235
246
|
revoked: true,
|
|
247
|
+
status: NEGATIVE,
|
|
236
248
|
reason: this.primaryReason,
|
|
237
249
|
attestationCount: count,
|
|
238
250
|
threshold,
|
|
@@ -243,6 +255,7 @@ export class RevocationState {
|
|
|
243
255
|
|
|
244
256
|
return {
|
|
245
257
|
revoked: false,
|
|
258
|
+
status: POSITIVE,
|
|
246
259
|
reason: 'BELOW_THRESHOLD',
|
|
247
260
|
attestationCount: count,
|
|
248
261
|
threshold,
|
|
@@ -597,6 +610,361 @@ export const MESH_REVOCATION_MESSAGES = {
|
|
|
597
610
|
ATTESTATION: 'mesh:attestation', // Single attestation
|
|
598
611
|
ATTESTATIONS_SYNC: 'mesh:attest-sync', // Bulk attestation sync
|
|
599
612
|
REVOCATION_CERT: 'mesh:revoke-cert', // Completed revocation certificate
|
|
613
|
+
ATTESTATION_DISPUTE: 'mesh:attest-dispute', // Challenge an attestation
|
|
600
614
|
};
|
|
601
615
|
|
|
616
|
+
// =============================================================================
|
|
617
|
+
// ATTESTATION ACCOUNTABILITY
|
|
618
|
+
// =============================================================================
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* ZIMMEDARI - Attestation Accountability Tracker
|
|
622
|
+
* ज़िम्मेदारी (Hindi: "responsibility, accountability")
|
|
623
|
+
*
|
|
624
|
+
* Tracks the accuracy of attestations filed by nodes.
|
|
625
|
+
* If a node files provably false attestations, they lose karma.
|
|
626
|
+
*
|
|
627
|
+
* This prevents:
|
|
628
|
+
* - Coordinated attacks against innocent nodes
|
|
629
|
+
* - Frivolous attestations without evidence
|
|
630
|
+
* - "Crying wolf" revocation attempts
|
|
631
|
+
*
|
|
632
|
+
* Accountability is determined by:
|
|
633
|
+
* 1. If attested node later proves legitimacy (attestation was wrong)
|
|
634
|
+
* 2. If attestation is counter-attested by majority
|
|
635
|
+
* 3. If attestation is withdrawn by filer
|
|
636
|
+
*/
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Attestation accuracy states
|
|
640
|
+
*/
|
|
641
|
+
export const ATTESTATION_ACCURACY = Object.freeze({
|
|
642
|
+
PENDING: 'pending', // Not yet determined
|
|
643
|
+
VALIDATED: 'validated', // Attestation was correct
|
|
644
|
+
DISPUTED: 'disputed', // Under dispute
|
|
645
|
+
FALSE_POSITIVE: 'false_positive', // Attested node was innocent
|
|
646
|
+
WITHDRAWN: 'withdrawn', // Filer withdrew attestation
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* AttestationAccountability - Tracks who files what and their accuracy
|
|
651
|
+
*/
|
|
652
|
+
export class AttestationAccountability extends EventEmitter {
|
|
653
|
+
constructor(options = {}) {
|
|
654
|
+
super();
|
|
655
|
+
|
|
656
|
+
// nodeId -> AttesterRecord
|
|
657
|
+
this.attesters = new Map();
|
|
658
|
+
|
|
659
|
+
// dokoId -> dispute info
|
|
660
|
+
this.disputes = new Map();
|
|
661
|
+
|
|
662
|
+
this.config = {
|
|
663
|
+
// Accuracy tracking window (ms)
|
|
664
|
+
trackingWindow: options.trackingWindow || 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
665
|
+
|
|
666
|
+
// Minimum attestations before accuracy matters
|
|
667
|
+
minAttestationsForAccuracy: options.minAttestationsForAccuracy || 5,
|
|
668
|
+
|
|
669
|
+
// Accuracy thresholds
|
|
670
|
+
accuracyThreshold: {
|
|
671
|
+
unreliable: options.unreliableThreshold || 0.5, // Below 50% = unreliable
|
|
672
|
+
suspicious: options.suspiciousThreshold || 0.7, // Below 70% = suspicious
|
|
673
|
+
reliable: options.reliableThreshold || 0.9, // Above 90% = reliable
|
|
674
|
+
},
|
|
675
|
+
|
|
676
|
+
// Karma penalties for false attestations
|
|
677
|
+
falseAttestationPenalty: options.falseAttestationPenalty || 0.2,
|
|
678
|
+
|
|
679
|
+
// Karma reward for validated attestations
|
|
680
|
+
validatedReward: options.validatedReward || 0.02,
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
log.info('zimmedari', 'Attestation accountability initialized');
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Record an attestation being filed
|
|
688
|
+
*/
|
|
689
|
+
recordAttestation(attesterId, targetDokoId, reason) {
|
|
690
|
+
let record = this.attesters.get(attesterId);
|
|
691
|
+
if (!record) {
|
|
692
|
+
record = this._createAttesterRecord(attesterId);
|
|
693
|
+
this.attesters.set(attesterId, record);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
record.totalFiled++;
|
|
697
|
+
record.lastFiled = Date.now();
|
|
698
|
+
record.attestations.push({
|
|
699
|
+
targetDokoId,
|
|
700
|
+
reason,
|
|
701
|
+
filedAt: Date.now(),
|
|
702
|
+
accuracy: ATTESTATION_ACCURACY.PENDING,
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
// Limit history size
|
|
706
|
+
if (record.attestations.length > 1000) {
|
|
707
|
+
record.attestations = record.attestations.slice(-500);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Mark an attestation as validated (was correct)
|
|
713
|
+
*/
|
|
714
|
+
markValidated(attesterId, targetDokoId) {
|
|
715
|
+
const record = this.attesters.get(attesterId);
|
|
716
|
+
if (!record) return false;
|
|
717
|
+
|
|
718
|
+
const attestation = record.attestations.find(
|
|
719
|
+
a => a.targetDokoId === targetDokoId && a.accuracy === ATTESTATION_ACCURACY.PENDING
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
if (!attestation) return false;
|
|
723
|
+
|
|
724
|
+
attestation.accuracy = ATTESTATION_ACCURACY.VALIDATED;
|
|
725
|
+
attestation.validatedAt = Date.now();
|
|
726
|
+
record.validated++;
|
|
727
|
+
|
|
728
|
+
this.emit('attestation-validated', {
|
|
729
|
+
attesterId,
|
|
730
|
+
targetDokoId,
|
|
731
|
+
accuracy: this.getAccuracy(attesterId),
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Mark an attestation as false positive (was wrong)
|
|
739
|
+
* This triggers karma penalty
|
|
740
|
+
*/
|
|
741
|
+
markFalsePositive(attesterId, targetDokoId, evidence = null) {
|
|
742
|
+
const record = this.attesters.get(attesterId);
|
|
743
|
+
if (!record) return false;
|
|
744
|
+
|
|
745
|
+
const attestation = record.attestations.find(
|
|
746
|
+
a => a.targetDokoId === targetDokoId && a.accuracy === ATTESTATION_ACCURACY.PENDING
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
if (!attestation) return false;
|
|
750
|
+
|
|
751
|
+
attestation.accuracy = ATTESTATION_ACCURACY.FALSE_POSITIVE;
|
|
752
|
+
attestation.falsifiedAt = Date.now();
|
|
753
|
+
attestation.evidence = evidence;
|
|
754
|
+
record.falsePositives++;
|
|
755
|
+
|
|
756
|
+
this.emit('attestation-false-positive', {
|
|
757
|
+
attesterId,
|
|
758
|
+
targetDokoId,
|
|
759
|
+
evidence,
|
|
760
|
+
falsePositiveCount: record.falsePositives,
|
|
761
|
+
accuracy: this.getAccuracy(attesterId),
|
|
762
|
+
penalty: this.config.falseAttestationPenalty,
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
log.warn('zimmedari', 'False positive attestation recorded', {
|
|
766
|
+
attesterId,
|
|
767
|
+
targetDokoId,
|
|
768
|
+
accuracy: this.getAccuracy(attesterId),
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
return true;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Mark an attestation as disputed (under investigation)
|
|
776
|
+
*/
|
|
777
|
+
markDisputed(attesterId, targetDokoId, disputeReason) {
|
|
778
|
+
const record = this.attesters.get(attesterId);
|
|
779
|
+
if (!record) return false;
|
|
780
|
+
|
|
781
|
+
const attestation = record.attestations.find(
|
|
782
|
+
a => a.targetDokoId === targetDokoId && a.accuracy === ATTESTATION_ACCURACY.PENDING
|
|
783
|
+
);
|
|
784
|
+
|
|
785
|
+
if (!attestation) return false;
|
|
786
|
+
|
|
787
|
+
attestation.accuracy = ATTESTATION_ACCURACY.DISPUTED;
|
|
788
|
+
attestation.disputedAt = Date.now();
|
|
789
|
+
attestation.disputeReason = disputeReason;
|
|
790
|
+
|
|
791
|
+
this.disputes.set(targetDokoId, {
|
|
792
|
+
attesterId,
|
|
793
|
+
disputeReason,
|
|
794
|
+
disputedAt: Date.now(),
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
this.emit('attestation-disputed', {
|
|
798
|
+
attesterId,
|
|
799
|
+
targetDokoId,
|
|
800
|
+
disputeReason,
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* File a dispute against an attestation
|
|
808
|
+
* Returns dispute info for gossip propagation
|
|
809
|
+
*/
|
|
810
|
+
fileDispute(disputerId, targetDokoId, originalAttesterId, evidence) {
|
|
811
|
+
const dispute = {
|
|
812
|
+
type: 'attestation_dispute',
|
|
813
|
+
disputerId,
|
|
814
|
+
targetDokoId,
|
|
815
|
+
originalAttesterId,
|
|
816
|
+
evidence,
|
|
817
|
+
filedAt: Date.now(),
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// Mark the original attestation as disputed
|
|
821
|
+
this.markDisputed(originalAttesterId, targetDokoId, `Disputed by ${disputerId}`);
|
|
822
|
+
|
|
823
|
+
log.info('zimmedari', 'Dispute filed against attestation', {
|
|
824
|
+
disputerId,
|
|
825
|
+
originalAttesterId,
|
|
826
|
+
targetDokoId,
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
return dispute;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Get accuracy ratio for an attester
|
|
834
|
+
*/
|
|
835
|
+
getAccuracy(attesterId) {
|
|
836
|
+
const record = this.attesters.get(attesterId);
|
|
837
|
+
if (!record) return null;
|
|
838
|
+
|
|
839
|
+
const resolved = record.validated + record.falsePositives;
|
|
840
|
+
if (resolved < this.config.minAttestationsForAccuracy) {
|
|
841
|
+
return {
|
|
842
|
+
accuracy: null,
|
|
843
|
+
reason: 'INSUFFICIENT_DATA',
|
|
844
|
+
resolved,
|
|
845
|
+
required: this.config.minAttestationsForAccuracy,
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const accuracyRatio = record.validated / resolved;
|
|
850
|
+
|
|
851
|
+
let status = 'normal';
|
|
852
|
+
if (accuracyRatio >= this.config.accuracyThreshold.reliable) {
|
|
853
|
+
status = 'reliable';
|
|
854
|
+
} else if (accuracyRatio < this.config.accuracyThreshold.unreliable) {
|
|
855
|
+
status = 'unreliable';
|
|
856
|
+
} else if (accuracyRatio < this.config.accuracyThreshold.suspicious) {
|
|
857
|
+
status = 'suspicious';
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
return {
|
|
861
|
+
accuracy: accuracyRatio,
|
|
862
|
+
status,
|
|
863
|
+
validated: record.validated,
|
|
864
|
+
falsePositives: record.falsePositives,
|
|
865
|
+
pending: record.attestations.filter(a => a.accuracy === ATTESTATION_ACCURACY.PENDING).length,
|
|
866
|
+
total: record.totalFiled,
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Get attester record
|
|
872
|
+
*/
|
|
873
|
+
getAttesterRecord(attesterId) {
|
|
874
|
+
return this.attesters.get(attesterId) || null;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Get statistics
|
|
879
|
+
*/
|
|
880
|
+
getStats() {
|
|
881
|
+
let totalAttesters = 0;
|
|
882
|
+
let totalAttestations = 0;
|
|
883
|
+
let totalValidated = 0;
|
|
884
|
+
let totalFalsePositives = 0;
|
|
885
|
+
let unreliableAttesters = 0;
|
|
886
|
+
|
|
887
|
+
for (const record of this.attesters.values()) {
|
|
888
|
+
totalAttesters++;
|
|
889
|
+
totalAttestations += record.totalFiled;
|
|
890
|
+
totalValidated += record.validated;
|
|
891
|
+
totalFalsePositives += record.falsePositives;
|
|
892
|
+
|
|
893
|
+
const accuracy = this.getAccuracy(record.attesterId);
|
|
894
|
+
if (accuracy?.status === 'unreliable') {
|
|
895
|
+
unreliableAttesters++;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return {
|
|
900
|
+
totalAttesters,
|
|
901
|
+
totalAttestations,
|
|
902
|
+
totalValidated,
|
|
903
|
+
totalFalsePositives,
|
|
904
|
+
unreliableAttesters,
|
|
905
|
+
activeDisputes: this.disputes.size,
|
|
906
|
+
overallAccuracy: totalValidated > 0 ? totalValidated / (totalValidated + totalFalsePositives) : null,
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Cleanup old records
|
|
912
|
+
*/
|
|
913
|
+
cleanup() {
|
|
914
|
+
const now = Date.now();
|
|
915
|
+
const cutoff = now - this.config.trackingWindow;
|
|
916
|
+
let removed = 0;
|
|
917
|
+
|
|
918
|
+
for (const [attesterId, record] of this.attesters) {
|
|
919
|
+
// Remove attestations older than tracking window
|
|
920
|
+
const originalLength = record.attestations.length;
|
|
921
|
+
record.attestations = record.attestations.filter(a => a.filedAt > cutoff);
|
|
922
|
+
|
|
923
|
+
// If no recent attestations, remove the record
|
|
924
|
+
if (record.attestations.length === 0 && record.lastFiled < cutoff) {
|
|
925
|
+
this.attesters.delete(attesterId);
|
|
926
|
+
removed++;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// Cleanup old disputes
|
|
931
|
+
for (const [dokoId, dispute] of this.disputes) {
|
|
932
|
+
if (dispute.disputedAt < cutoff) {
|
|
933
|
+
this.disputes.delete(dokoId);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (removed > 0) {
|
|
938
|
+
log.info('zimmedari', `Cleaned up ${removed} stale attester records`);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
return removed;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
_createAttesterRecord(attesterId) {
|
|
945
|
+
return {
|
|
946
|
+
attesterId,
|
|
947
|
+
totalFiled: 0,
|
|
948
|
+
validated: 0,
|
|
949
|
+
falsePositives: 0,
|
|
950
|
+
firstSeen: Date.now(),
|
|
951
|
+
lastFiled: null,
|
|
952
|
+
attestations: [],
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Singleton instance
|
|
958
|
+
let _accountabilityInstance = null;
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Get the singleton accountability tracker instance
|
|
962
|
+
*/
|
|
963
|
+
export function getAttestationAccountability(options) {
|
|
964
|
+
if (!_accountabilityInstance) {
|
|
965
|
+
_accountabilityInstance = new AttestationAccountability(options);
|
|
966
|
+
}
|
|
967
|
+
return _accountabilityInstance;
|
|
968
|
+
}
|
|
969
|
+
|
|
602
970
|
export default MeshRevocation;
|