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
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ╔═══════════════════════════════════════════════════════════════════════════════╗
|
|
3
|
+
* ║ 🔗 SANGHA - UNIFIED COMPONENT ATTESTATION 🔗 ║
|
|
4
|
+
* ╠═══════════════════════════════════════════════════════════════════════════════╣
|
|
5
|
+
* ║ ║
|
|
6
|
+
* ║ SANGHA (Sanskrit: संघ) means "community" or "assembly" - the Buddhist ║
|
|
7
|
+
* ║ concept of beings who support each other on the path to enlightenment. ║
|
|
8
|
+
* ║ ║
|
|
9
|
+
* ║ In YAKMESH, SANGHA represents a NOVEL security architecture: ║
|
|
10
|
+
* ║ ║
|
|
11
|
+
* ║ PHILOSOPHY: ║
|
|
12
|
+
* ║ Traditional process isolation SEPARATES components - each stands alone. ║
|
|
13
|
+
* ║ SANGHA UNIFIES components - they protect each other through continuous ║
|
|
14
|
+
* ║ mutual attestation. Like a group of travelers in dangerous terrain, ║
|
|
15
|
+
* ║ strength comes from unity, not isolation. ║
|
|
16
|
+
* ║ ║
|
|
17
|
+
* ║ CORE MECHANISMS: ║
|
|
18
|
+
* ║ 1. SYNAPSE - Cryptographic communication channels between components ║
|
|
19
|
+
* ║ 2. ANTIBODY - Circulating verification routines that check integrity ║
|
|
20
|
+
* ║ 3. TEMPORAL BINDING - Operations bound to GPS time windows ║
|
|
21
|
+
* ║ 4. COLLECTIVE RESPONSE - All components react to any detected anomaly ║
|
|
22
|
+
* ║ ║
|
|
23
|
+
* ║ "No component can be compromised silently - the others would notice." ║
|
|
24
|
+
* ║ ║
|
|
25
|
+
* ╚═══════════════════════════════════════════════════════════════════════════════╝
|
|
26
|
+
*
|
|
27
|
+
* @module security/sangha
|
|
28
|
+
* @version 1.0.0
|
|
29
|
+
* @license MIT
|
|
30
|
+
* @copyright 2026 YAKMESH™ Contributors
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { EventEmitter } from 'events';
|
|
34
|
+
import { createHash, randomBytes } from 'crypto';
|
|
35
|
+
import { createLogger } from '../utils/logger.js';
|
|
36
|
+
import { ternaryId } from '../utils/ternary-id.js';
|
|
37
|
+
import { Trit, POSITIVE, NEGATIVE, NEUTRAL } from '../oracle/tribhuj.js';
|
|
38
|
+
|
|
39
|
+
const log = createLogger('security:sangha');
|
|
40
|
+
|
|
41
|
+
// =============================================================================
|
|
42
|
+
// CONSTANTS
|
|
43
|
+
// =============================================================================
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Component identifiers in the SANGHA collective
|
|
47
|
+
* Each component is a member of the assembly
|
|
48
|
+
*/
|
|
49
|
+
export const SANGHA_COMPONENT = Object.freeze({
|
|
50
|
+
CRYPTO: 'crypto', // Cryptographic operations (signing, KEM, hashing)
|
|
51
|
+
MESH: 'mesh', // Network layer (WebSocket, gossip, ANNEX)
|
|
52
|
+
ORACLE: 'oracle', // Consensus, time, validation
|
|
53
|
+
ACCEL: 'accel', // Hardware acceleration (GPU/NPU/ONNX)
|
|
54
|
+
HTTP: 'http', // API server and dashboard
|
|
55
|
+
IDENTITY: 'identity', // Key management, DOKO
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Attestation validity windows (milliseconds)
|
|
60
|
+
* Tighter windows for higher-integrity components
|
|
61
|
+
*/
|
|
62
|
+
export const ATTESTATION_VALIDITY = Object.freeze({
|
|
63
|
+
[SANGHA_COMPONENT.CRYPTO]: 100, // 100ms - crypto must be real-time
|
|
64
|
+
[SANGHA_COMPONENT.ORACLE]: 200, // 200ms - oracle needs tight time
|
|
65
|
+
[SANGHA_COMPONENT.MESH]: 500, // 500ms - network has latency
|
|
66
|
+
[SANGHA_COMPONENT.ACCEL]: 1000, // 1s - GPU ops can be slow
|
|
67
|
+
[SANGHA_COMPONENT.HTTP]: 2000, // 2s - HTTP is user-facing
|
|
68
|
+
[SANGHA_COMPONENT.IDENTITY]: 100, // 100ms - identity is critical
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Minimum attestations required for collective operations
|
|
73
|
+
* Uses 2-of-3 Byzantine tolerance
|
|
74
|
+
*/
|
|
75
|
+
export const QUORUM_THRESHOLD = 3;
|
|
76
|
+
export const QUORUM_MINIMUM = 2;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* SANGHA States (ternary)
|
|
80
|
+
*/
|
|
81
|
+
export const SANGHA_STATE = Object.freeze({
|
|
82
|
+
HARMONIOUS: POSITIVE, // +1: All components in agreement
|
|
83
|
+
DISRUPTED: NEGATIVE, // -1: Attestation chain broken
|
|
84
|
+
CONVERGING: NEUTRAL, // 0: Working toward consensus
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// SYNAPSE - CRYPTOGRAPHIC COMMUNICATION CHANNEL
|
|
89
|
+
// =============================================================================
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Synapse represents a secure, bidirectional communication channel
|
|
93
|
+
* between two SANGHA components.
|
|
94
|
+
*
|
|
95
|
+
* Every message through a synapse is:
|
|
96
|
+
* - Signed by the sender
|
|
97
|
+
* - Timestamped to GPS time
|
|
98
|
+
* - Verified by the receiver
|
|
99
|
+
* - Part of an attestation chain
|
|
100
|
+
*/
|
|
101
|
+
export class Synapse {
|
|
102
|
+
#id;
|
|
103
|
+
#componentA;
|
|
104
|
+
#componentB;
|
|
105
|
+
#sharedSecret;
|
|
106
|
+
#messageChain;
|
|
107
|
+
#lastActivity;
|
|
108
|
+
#healthy;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @param {string} componentA - First component ID
|
|
112
|
+
* @param {string} componentB - Second component ID
|
|
113
|
+
*/
|
|
114
|
+
constructor(componentA, componentB) {
|
|
115
|
+
this.#id = `synapse:${componentA}<->${componentB}`;
|
|
116
|
+
this.#componentA = componentA;
|
|
117
|
+
this.#componentB = componentB;
|
|
118
|
+
this.#sharedSecret = randomBytes(32);
|
|
119
|
+
this.#messageChain = []; // Rolling chain of message hashes
|
|
120
|
+
this.#lastActivity = Date.now();
|
|
121
|
+
this.#healthy = true;
|
|
122
|
+
|
|
123
|
+
Object.seal(this);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
get id() { return this.#id; }
|
|
127
|
+
get componentA() { return this.#componentA; }
|
|
128
|
+
get componentB() { return this.#componentB; }
|
|
129
|
+
get healthy() { return this.#healthy; }
|
|
130
|
+
get lastActivity() { return this.#lastActivity; }
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Generate a message attestation
|
|
134
|
+
* @param {string} sender - Component sending the message
|
|
135
|
+
* @param {Buffer|string} data - Message data
|
|
136
|
+
* @param {number} timestamp - GPS timestamp
|
|
137
|
+
* @returns {{attestation: string, chainHash: string}}
|
|
138
|
+
*/
|
|
139
|
+
attest(sender, data, timestamp) {
|
|
140
|
+
if (sender !== this.#componentA && sender !== this.#componentB) {
|
|
141
|
+
throw new Error(`Invalid sender ${sender} for synapse ${this.#id}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(String(data));
|
|
145
|
+
|
|
146
|
+
// Build attestation: hash(sender | data | timestamp | prevChainHash | secret)
|
|
147
|
+
const prevChainHash = this.#messageChain.length > 0
|
|
148
|
+
? this.#messageChain[this.#messageChain.length - 1]
|
|
149
|
+
: Buffer.alloc(32);
|
|
150
|
+
|
|
151
|
+
const attestation = createHash('sha3-256')
|
|
152
|
+
.update(sender)
|
|
153
|
+
.update(dataBuffer)
|
|
154
|
+
.update(Buffer.from(timestamp.toString()))
|
|
155
|
+
.update(prevChainHash)
|
|
156
|
+
.update(this.#sharedSecret)
|
|
157
|
+
.digest('hex');
|
|
158
|
+
|
|
159
|
+
// Add to chain (rolling window of last 100)
|
|
160
|
+
this.#messageChain.push(Buffer.from(attestation, 'hex'));
|
|
161
|
+
if (this.#messageChain.length > 100) {
|
|
162
|
+
this.#messageChain.shift();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.#lastActivity = Date.now();
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
attestation,
|
|
169
|
+
chainHash: attestation.slice(0, 16),
|
|
170
|
+
chainLength: this.#messageChain.length,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Verify a message attestation
|
|
176
|
+
* @param {string} sender - Expected sender
|
|
177
|
+
* @param {Buffer|string} data - Message data
|
|
178
|
+
* @param {number} timestamp - GPS timestamp
|
|
179
|
+
* @param {string} expectedAttestation - Expected attestation hash
|
|
180
|
+
* @returns {boolean}
|
|
181
|
+
*/
|
|
182
|
+
verify(sender, data, timestamp, expectedAttestation) {
|
|
183
|
+
try {
|
|
184
|
+
const { attestation } = this.attest(sender, data, timestamp);
|
|
185
|
+
const valid = attestation === expectedAttestation;
|
|
186
|
+
|
|
187
|
+
if (!valid) {
|
|
188
|
+
this.#healthy = false;
|
|
189
|
+
log.warn(`Synapse ${this.#id} attestation mismatch`, {
|
|
190
|
+
expected: expectedAttestation.slice(0, 16),
|
|
191
|
+
got: attestation.slice(0, 16),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return valid;
|
|
196
|
+
} catch (e) {
|
|
197
|
+
this.#healthy = false;
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get synapse health metrics
|
|
204
|
+
*/
|
|
205
|
+
getHealth() {
|
|
206
|
+
return {
|
|
207
|
+
id: this.#id,
|
|
208
|
+
healthy: this.#healthy,
|
|
209
|
+
chainLength: this.#messageChain.length,
|
|
210
|
+
lastActivity: this.#lastActivity,
|
|
211
|
+
age: Date.now() - this.#lastActivity,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Reset synapse after recovery
|
|
217
|
+
*/
|
|
218
|
+
reset() {
|
|
219
|
+
this.#sharedSecret = randomBytes(32);
|
|
220
|
+
this.#messageChain = [];
|
|
221
|
+
this.#healthy = true;
|
|
222
|
+
this.#lastActivity = Date.now();
|
|
223
|
+
log.info(`Synapse ${this.#id} reset`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// =============================================================================
|
|
228
|
+
// ANTIBODY - CIRCULATING VERIFICATION ROUTINE
|
|
229
|
+
// =============================================================================
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Antibody is a verification routine that circulates between components,
|
|
233
|
+
* collecting attestations and detecting inconsistencies.
|
|
234
|
+
*
|
|
235
|
+
* Like biological antibodies, they:
|
|
236
|
+
* - Patrol continuously
|
|
237
|
+
* - Detect foreign/corrupt elements
|
|
238
|
+
* - Trigger collective immune response
|
|
239
|
+
*/
|
|
240
|
+
export class Antibody {
|
|
241
|
+
#id;
|
|
242
|
+
#createdAt;
|
|
243
|
+
#visited;
|
|
244
|
+
#attestations;
|
|
245
|
+
#anomalies;
|
|
246
|
+
#state;
|
|
247
|
+
|
|
248
|
+
constructor() {
|
|
249
|
+
this.#id = ternaryId(8);
|
|
250
|
+
this.#createdAt = Date.now();
|
|
251
|
+
this.#visited = new Set();
|
|
252
|
+
this.#attestations = new Map();
|
|
253
|
+
this.#anomalies = [];
|
|
254
|
+
this.#state = new Trit(NEUTRAL); // Start converging
|
|
255
|
+
|
|
256
|
+
Object.seal(this);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
get id() { return this.#id; }
|
|
260
|
+
get createdAt() { return this.#createdAt; }
|
|
261
|
+
get visited() { return Array.from(this.#visited); }
|
|
262
|
+
get attestations() { return new Map(this.#attestations); }
|
|
263
|
+
get anomalies() { return [...this.#anomalies]; }
|
|
264
|
+
get state() { return this.#state; }
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Visit a component and collect its attestation
|
|
268
|
+
* @param {string} component - Component ID
|
|
269
|
+
* @param {object} componentState - Component's current state
|
|
270
|
+
* @param {number} timestamp - GPS timestamp
|
|
271
|
+
* @returns {object} - Attestation result
|
|
272
|
+
*/
|
|
273
|
+
visit(component, componentState, timestamp) {
|
|
274
|
+
this.#visited.add(component);
|
|
275
|
+
|
|
276
|
+
// Generate attestation from component state
|
|
277
|
+
const attestation = createHash('sha3-256')
|
|
278
|
+
.update(this.#id)
|
|
279
|
+
.update(component)
|
|
280
|
+
.update(JSON.stringify(componentState))
|
|
281
|
+
.update(Buffer.from(timestamp.toString()))
|
|
282
|
+
.digest('hex');
|
|
283
|
+
|
|
284
|
+
this.#attestations.set(component, {
|
|
285
|
+
attestation,
|
|
286
|
+
timestamp,
|
|
287
|
+
stateHash: createHash('sha3-256')
|
|
288
|
+
.update(JSON.stringify(componentState))
|
|
289
|
+
.digest('hex')
|
|
290
|
+
.slice(0, 16),
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return { component, attestation: attestation.slice(0, 16) };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Record an anomaly detected during circulation
|
|
298
|
+
* @param {string} component - Component where anomaly detected
|
|
299
|
+
* @param {string} type - Anomaly type
|
|
300
|
+
* @param {object} details - Anomaly details
|
|
301
|
+
*/
|
|
302
|
+
recordAnomaly(component, type, details = {}) {
|
|
303
|
+
this.#anomalies.push({
|
|
304
|
+
component,
|
|
305
|
+
type,
|
|
306
|
+
details,
|
|
307
|
+
timestamp: Date.now(),
|
|
308
|
+
});
|
|
309
|
+
this.#state = new Trit(NEGATIVE); // Disrupted
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Finalize circulation and determine collective state
|
|
314
|
+
* @returns {{state: Trit, visited: string[], anomalies: object[]}}
|
|
315
|
+
*/
|
|
316
|
+
finalize() {
|
|
317
|
+
if (this.#anomalies.length > 0) {
|
|
318
|
+
this.#state = new Trit(NEGATIVE);
|
|
319
|
+
} else if (this.#visited.size >= QUORUM_MINIMUM) {
|
|
320
|
+
this.#state = new Trit(POSITIVE);
|
|
321
|
+
} else {
|
|
322
|
+
this.#state = new Trit(NEUTRAL);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
id: this.#id,
|
|
327
|
+
state: this.#state,
|
|
328
|
+
visited: this.visited,
|
|
329
|
+
attestationCount: this.#attestations.size,
|
|
330
|
+
anomalies: this.#anomalies,
|
|
331
|
+
duration: Date.now() - this.#createdAt,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// =============================================================================
|
|
337
|
+
// SANGHA - THE UNIFIED COLLECTIVE
|
|
338
|
+
// =============================================================================
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* SANGHA manages the collective of components and their mutual attestations.
|
|
342
|
+
*
|
|
343
|
+
* It maintains:
|
|
344
|
+
* - Synapses between all component pairs
|
|
345
|
+
* - Circulating antibodies for continuous verification
|
|
346
|
+
* - Temporal binding to GPS time
|
|
347
|
+
* - Collective response mechanisms
|
|
348
|
+
*/
|
|
349
|
+
export class Sangha extends EventEmitter {
|
|
350
|
+
#components;
|
|
351
|
+
#synapses;
|
|
352
|
+
#antibodies;
|
|
353
|
+
#timeSource;
|
|
354
|
+
#state;
|
|
355
|
+
#lastCirculation;
|
|
356
|
+
#circulationInterval;
|
|
357
|
+
#circulationIntervalMs;
|
|
358
|
+
#started;
|
|
359
|
+
|
|
360
|
+
constructor() {
|
|
361
|
+
super();
|
|
362
|
+
|
|
363
|
+
this.#components = new Map();
|
|
364
|
+
this.#synapses = new Map();
|
|
365
|
+
this.#antibodies = [];
|
|
366
|
+
this.#timeSource = null;
|
|
367
|
+
this.#state = new Trit(NEUTRAL);
|
|
368
|
+
this.#lastCirculation = 0;
|
|
369
|
+
this.#circulationInterval = null;
|
|
370
|
+
this.#circulationIntervalMs = 5000;
|
|
371
|
+
this.#started = false;
|
|
372
|
+
|
|
373
|
+
Object.seal(this);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
377
|
+
// Component Registration
|
|
378
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Register a component with the SANGHA collective
|
|
382
|
+
* @param {string} componentId - Component identifier
|
|
383
|
+
* @param {object} componentRef - Reference to component instance
|
|
384
|
+
* @param {function} stateGetter - Function to get component's current state
|
|
385
|
+
*/
|
|
386
|
+
register(componentId, componentRef, stateGetter) {
|
|
387
|
+
if (this.#components.has(componentId)) {
|
|
388
|
+
log.warn(`Component ${componentId} already registered, updating`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
this.#components.set(componentId, {
|
|
392
|
+
id: componentId,
|
|
393
|
+
ref: componentRef,
|
|
394
|
+
getState: stateGetter,
|
|
395
|
+
registeredAt: Date.now(),
|
|
396
|
+
lastAttestation: 0,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Create synapses to all existing components
|
|
400
|
+
for (const [existingId] of this.#components) {
|
|
401
|
+
if (existingId !== componentId) {
|
|
402
|
+
const synapseId = this.#makeSynapseId(componentId, existingId);
|
|
403
|
+
if (!this.#synapses.has(synapseId)) {
|
|
404
|
+
this.#synapses.set(synapseId, new Synapse(componentId, existingId));
|
|
405
|
+
log.debug(`Created synapse ${synapseId}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
log.info(`Component ${componentId} joined SANGHA`, {
|
|
411
|
+
totalComponents: this.#components.size,
|
|
412
|
+
totalSynapses: this.#synapses.size,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
this.emit('componentJoined', componentId);
|
|
416
|
+
return this;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Unregister a component
|
|
421
|
+
* @param {string} componentId
|
|
422
|
+
*/
|
|
423
|
+
unregister(componentId) {
|
|
424
|
+
if (!this.#components.has(componentId)) return;
|
|
425
|
+
|
|
426
|
+
this.#components.delete(componentId);
|
|
427
|
+
|
|
428
|
+
// Remove associated synapses
|
|
429
|
+
for (const [synapseId] of this.#synapses) {
|
|
430
|
+
if (synapseId.includes(componentId)) {
|
|
431
|
+
this.#synapses.delete(synapseId);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
log.info(`Component ${componentId} left SANGHA`);
|
|
436
|
+
this.emit('componentLeft', componentId);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
440
|
+
// Time Source Binding
|
|
441
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Bind to a time source for temporal attestations
|
|
445
|
+
* @param {object} timeSource - MANI time source instance
|
|
446
|
+
*/
|
|
447
|
+
bindTimeSource(timeSource) {
|
|
448
|
+
this.#timeSource = timeSource;
|
|
449
|
+
log.info('SANGHA bound to time source', {
|
|
450
|
+
type: timeSource?.getSourceType?.() || 'unknown',
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Get current GPS timestamp for attestations
|
|
456
|
+
* @returns {number}
|
|
457
|
+
*/
|
|
458
|
+
getTimestamp() {
|
|
459
|
+
if (this.#timeSource?.getCurrentTime) {
|
|
460
|
+
return this.#timeSource.getCurrentTime();
|
|
461
|
+
}
|
|
462
|
+
return Date.now();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
466
|
+
// Synapse Operations
|
|
467
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Create standardized synapse ID from two component IDs
|
|
471
|
+
*/
|
|
472
|
+
#makeSynapseId(a, b) {
|
|
473
|
+
return a < b ? `${a}<->${b}` : `${b}<->${a}`;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Get synapse between two components
|
|
478
|
+
* @param {string} componentA
|
|
479
|
+
* @param {string} componentB
|
|
480
|
+
* @returns {Synapse|null}
|
|
481
|
+
*/
|
|
482
|
+
getSynapse(componentA, componentB) {
|
|
483
|
+
return this.#synapses.get(this.#makeSynapseId(componentA, componentB));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Send an attested message between components
|
|
488
|
+
* @param {string} from - Sender component
|
|
489
|
+
* @param {string} to - Receiver component
|
|
490
|
+
* @param {string} action - Action/method name
|
|
491
|
+
* @param {any} payload - Message payload
|
|
492
|
+
* @returns {object} - Attested message envelope
|
|
493
|
+
*/
|
|
494
|
+
attestedCall(from, to, action, payload) {
|
|
495
|
+
const synapse = this.getSynapse(from, to);
|
|
496
|
+
if (!synapse) {
|
|
497
|
+
throw new Error(`No synapse between ${from} and ${to}`);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const timestamp = this.getTimestamp();
|
|
501
|
+
const validity = ATTESTATION_VALIDITY[from] || 1000;
|
|
502
|
+
|
|
503
|
+
const messageData = JSON.stringify({ action, payload });
|
|
504
|
+
const { attestation, chainHash } = synapse.attest(from, messageData, timestamp);
|
|
505
|
+
|
|
506
|
+
return {
|
|
507
|
+
from,
|
|
508
|
+
to,
|
|
509
|
+
action,
|
|
510
|
+
payload,
|
|
511
|
+
timestamp,
|
|
512
|
+
validUntil: timestamp + validity,
|
|
513
|
+
attestation,
|
|
514
|
+
chainHash,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Verify an attested message
|
|
520
|
+
* @param {object} envelope - Message envelope from attestedCall
|
|
521
|
+
* @returns {boolean}
|
|
522
|
+
*/
|
|
523
|
+
verifyAttestedCall(envelope) {
|
|
524
|
+
const { from, to, action, payload, timestamp, validUntil, attestation } = envelope;
|
|
525
|
+
|
|
526
|
+
// Check temporal validity
|
|
527
|
+
const now = this.getTimestamp();
|
|
528
|
+
if (now > validUntil) {
|
|
529
|
+
log.warn('Attested call expired', { from, to, action, expired: now - validUntil });
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const synapse = this.getSynapse(from, to);
|
|
534
|
+
if (!synapse) {
|
|
535
|
+
log.warn('No synapse for verification', { from, to });
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const messageData = JSON.stringify({ action, payload });
|
|
540
|
+
return synapse.verify(from, messageData, timestamp, attestation);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
544
|
+
// Antibody Circulation
|
|
545
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Circulate an antibody through all components
|
|
549
|
+
* @returns {Promise<object>} - Circulation result
|
|
550
|
+
*/
|
|
551
|
+
async circulate() {
|
|
552
|
+
const antibody = new Antibody();
|
|
553
|
+
const timestamp = this.getTimestamp();
|
|
554
|
+
|
|
555
|
+
log.debug(`Antibody ${antibody.id} starting circulation`);
|
|
556
|
+
|
|
557
|
+
for (const [componentId, component] of this.#components) {
|
|
558
|
+
try {
|
|
559
|
+
const state = await component.getState();
|
|
560
|
+
antibody.visit(componentId, state, timestamp);
|
|
561
|
+
|
|
562
|
+
// Check attestation age - use 2x circulation interval as threshold
|
|
563
|
+
// (ATTESTATION_VALIDITY is for message expiry, not component liveness)
|
|
564
|
+
const age = timestamp - component.lastAttestation;
|
|
565
|
+
const maxAge = this.#circulationIntervalMs * 2;
|
|
566
|
+
|
|
567
|
+
if (component.lastAttestation > 0 && age > maxAge) {
|
|
568
|
+
antibody.recordAnomaly(componentId, 'STALE_ATTESTATION', {
|
|
569
|
+
age,
|
|
570
|
+
maxAge,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
component.lastAttestation = timestamp;
|
|
575
|
+
} catch (e) {
|
|
576
|
+
antibody.recordAnomaly(componentId, 'STATE_UNAVAILABLE', {
|
|
577
|
+
error: e.message,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const result = antibody.finalize();
|
|
583
|
+
|
|
584
|
+
// Store for analysis (rolling window)
|
|
585
|
+
this.#antibodies.push(result);
|
|
586
|
+
if (this.#antibodies.length > 100) {
|
|
587
|
+
this.#antibodies.shift();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Update collective state
|
|
591
|
+
this.#state = result.state;
|
|
592
|
+
this.#lastCirculation = timestamp;
|
|
593
|
+
|
|
594
|
+
// Emit events based on result
|
|
595
|
+
if (result.anomalies.length > 0) {
|
|
596
|
+
log.warn(`Antibody ${antibody.id} detected ${result.anomalies.length} anomalies`);
|
|
597
|
+
this.emit('anomalyDetected', result.anomalies);
|
|
598
|
+
this.#triggerCollectiveResponse(result.anomalies);
|
|
599
|
+
} else {
|
|
600
|
+
this.emit('circulationComplete', result);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return result;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Trigger collective response to anomalies
|
|
608
|
+
* All components are notified and can take defensive action
|
|
609
|
+
*/
|
|
610
|
+
#triggerCollectiveResponse(anomalies) {
|
|
611
|
+
log.error('🚨 SANGHA COLLECTIVE RESPONSE TRIGGERED', {
|
|
612
|
+
anomalyCount: anomalies.length,
|
|
613
|
+
types: anomalies.map(a => a.type),
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
this.emit('collectiveResponse', {
|
|
617
|
+
timestamp: this.getTimestamp(),
|
|
618
|
+
anomalies,
|
|
619
|
+
action: 'ALERT', // Could be: ALERT, ISOLATE, SHUTDOWN
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
// Notify all components
|
|
623
|
+
for (const [componentId, component] of this.#components) {
|
|
624
|
+
if (component.ref?.onSanghaAlert) {
|
|
625
|
+
try {
|
|
626
|
+
component.ref.onSanghaAlert(anomalies);
|
|
627
|
+
} catch (e) {
|
|
628
|
+
log.error(`Failed to alert ${componentId}`, { error: e.message });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
635
|
+
// Lifecycle
|
|
636
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Start the SANGHA collective
|
|
640
|
+
* @param {object} options
|
|
641
|
+
* @param {number} options.circulationIntervalMs - How often to circulate antibodies
|
|
642
|
+
*/
|
|
643
|
+
start({ circulationIntervalMs = 5000 } = {}) {
|
|
644
|
+
if (this.#started) return;
|
|
645
|
+
|
|
646
|
+
this.#started = true;
|
|
647
|
+
this.#circulationIntervalMs = circulationIntervalMs;
|
|
648
|
+
this.#circulationInterval = setInterval(() => {
|
|
649
|
+
this.circulate().catch(e => {
|
|
650
|
+
log.error('Circulation failed', { error: e.message });
|
|
651
|
+
});
|
|
652
|
+
}, circulationIntervalMs);
|
|
653
|
+
|
|
654
|
+
log.info('SANGHA collective started', {
|
|
655
|
+
components: this.#components.size,
|
|
656
|
+
synapses: this.#synapses.size,
|
|
657
|
+
circulationInterval: circulationIntervalMs,
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
// Initial circulation
|
|
661
|
+
this.circulate();
|
|
662
|
+
|
|
663
|
+
this.emit('started');
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Stop the SANGHA collective
|
|
668
|
+
*/
|
|
669
|
+
stop() {
|
|
670
|
+
if (!this.#started) return;
|
|
671
|
+
|
|
672
|
+
this.#started = false;
|
|
673
|
+
if (this.#circulationInterval) {
|
|
674
|
+
clearInterval(this.#circulationInterval);
|
|
675
|
+
this.#circulationInterval = null;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
log.info('SANGHA collective stopped');
|
|
679
|
+
this.emit('stopped');
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
683
|
+
// Status & Diagnostics
|
|
684
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Get collective health status
|
|
688
|
+
*/
|
|
689
|
+
getStatus() {
|
|
690
|
+
const synapseHealth = [];
|
|
691
|
+
for (const [id, synapse] of this.#synapses) {
|
|
692
|
+
synapseHealth.push(synapse.getHealth());
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const recentAnomalies = this.#antibodies
|
|
696
|
+
.slice(-10)
|
|
697
|
+
.flatMap(a => a.anomalies);
|
|
698
|
+
|
|
699
|
+
return {
|
|
700
|
+
state: this.#state.value,
|
|
701
|
+
stateLabel: this.#state.value === 1 ? 'HARMONIOUS' :
|
|
702
|
+
this.#state.value === -1 ? 'DISRUPTED' : 'CONVERGING',
|
|
703
|
+
started: this.#started,
|
|
704
|
+
components: Array.from(this.#components.keys()),
|
|
705
|
+
componentCount: this.#components.size,
|
|
706
|
+
synapseCount: this.#synapses.size,
|
|
707
|
+
synapseHealth,
|
|
708
|
+
lastCirculation: this.#lastCirculation,
|
|
709
|
+
circulationCount: this.#antibodies.length,
|
|
710
|
+
recentAnomalies,
|
|
711
|
+
healthy: this.#state.value >= 0 && recentAnomalies.length === 0,
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Get recent antibody results
|
|
717
|
+
* @param {number} count
|
|
718
|
+
*/
|
|
719
|
+
getRecentCirculations(count = 10) {
|
|
720
|
+
return this.#antibodies.slice(-count);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// =============================================================================
|
|
725
|
+
// SINGLETON & EXPORTS
|
|
726
|
+
// =============================================================================
|
|
727
|
+
|
|
728
|
+
let sanghaInstance = null;
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Get the global SANGHA instance
|
|
732
|
+
* @returns {Sangha}
|
|
733
|
+
*/
|
|
734
|
+
export function getSangha() {
|
|
735
|
+
if (!sanghaInstance) {
|
|
736
|
+
sanghaInstance = new Sangha();
|
|
737
|
+
}
|
|
738
|
+
return sanghaInstance;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Register a component with the global SANGHA
|
|
743
|
+
* @param {string} componentId
|
|
744
|
+
* @param {object} componentRef
|
|
745
|
+
* @param {function} stateGetter
|
|
746
|
+
*/
|
|
747
|
+
export function joinSangha(componentId, componentRef, stateGetter) {
|
|
748
|
+
return getSangha().register(componentId, componentRef, stateGetter);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Create an attested call between components
|
|
753
|
+
* @param {string} from
|
|
754
|
+
* @param {string} to
|
|
755
|
+
* @param {string} action
|
|
756
|
+
* @param {any} payload
|
|
757
|
+
*/
|
|
758
|
+
export function attestedCall(from, to, action, payload) {
|
|
759
|
+
return getSangha().attestedCall(from, to, action, payload);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Verify an attested call
|
|
764
|
+
* @param {object} envelope
|
|
765
|
+
*/
|
|
766
|
+
export function verifyAttestedCall(envelope) {
|
|
767
|
+
return getSangha().verifyAttestedCall(envelope);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
export default Sangha;
|