enigma-memory 0.1.17 → 0.1.22

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.
Files changed (290) hide show
  1. package/README.md +85 -27
  2. package/apps/cli/bin/enigma-desktop.mjs +140 -0
  3. package/apps/cli/bin/enigma-terminal.mjs +78 -0
  4. package/apps/cli/bin/enigma.mjs +4979 -3263
  5. package/apps/desktop/electron-main.cjs +217 -0
  6. package/apps/desktop/package.json +12 -0
  7. package/apps/desktop/src/app.js +264 -7
  8. package/apps/desktop/src/index.html +3514 -1373
  9. package/apps/desktop/src/launch-electron.mjs +51 -0
  10. package/apps/desktop/src/server.mjs +2914 -0
  11. package/apps/desktop/src/styles.css +2972 -260
  12. package/apps/desktop/src/zk-browser-prove.mjs +53 -0
  13. package/apps/desktop/src/zk-state.mjs +1789 -0
  14. package/apps/gateway/bin/enigma-gateway.mjs +102 -5
  15. package/apps/gateway/src/server.mjs +271 -8
  16. package/apps/ios/EnigmaCore/Package.swift +12 -0
  17. package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
  18. package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
  19. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
  20. package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
  21. package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
  22. package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
  23. package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
  24. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
  25. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
  26. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
  27. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
  28. package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
  29. package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
  30. package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
  31. package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
  32. package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
  33. package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
  34. package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
  35. package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
  36. package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
  37. package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
  38. package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
  39. package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
  40. package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
  41. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
  42. package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
  43. package/apps/native-host/README.md +19 -8
  44. package/apps/native-host/bin/enigma-native-host.mjs +229 -13
  45. package/apps/relay/bin/enigma-relay.mjs +103 -5
  46. package/apps/relay/src/federation-runtime.mjs +618 -0
  47. package/apps/relay/src/server.mjs +310 -9
  48. package/apps/verifier/bin/enigma-verify.mjs +327 -11
  49. package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
  50. package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
  51. package/cortex-v3/circuits/build/vk_bytes.json +32 -0
  52. package/cortex-v3/proving-assets.json +64 -0
  53. package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
  54. package/cortex-v3/zk/action-transition-vk.json +119 -0
  55. package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
  56. package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
  57. package/cortex-v3/zk/intent-witness.mjs +365 -0
  58. package/cortex-v3/zk/intent-witness.test.mjs +485 -0
  59. package/cortex-v3/zk/proving-assets.mjs +203 -0
  60. package/cortex-v3/zk/sale-witness.mjs +783 -0
  61. package/cortex-v3/zk/sale-witness.test.mjs +784 -0
  62. package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
  63. package/cortex-v3/zk/settlement-evidence.mjs +722 -0
  64. package/cortex-v3/zk/setup-intent.mjs +688 -0
  65. package/cortex-v3/zk/setup-sale.mjs +666 -0
  66. package/cortex-v3/zk/setup.mjs +594 -0
  67. package/cortex-v3/zk/witness.mjs +184 -0
  68. package/cortex-v3/zk/zk-codec.mjs +232 -0
  69. package/cortex-v3/zk/zk-codec.test.mjs +293 -0
  70. package/cortex-v3/zk/zk-settle.mjs +370 -0
  71. package/cortex-v3/zk/zk-tree.mjs +256 -0
  72. package/cortex-v3/zk/zk-tree.test.mjs +419 -0
  73. package/deploy/SIMULATION.md +14 -9
  74. package/deploy/docker-compose.local-production-simulation.yml +54 -12
  75. package/docs/benchmark-attestation-network.md +487 -487
  76. package/docs/benchmark-reproducibility.md +289 -289
  77. package/docs/blockchain-only-mechanisms.md +400 -400
  78. package/docs/browser-extension-install.md +8 -6
  79. package/docs/client-connectors.md +16 -12
  80. package/docs/demo-proof-network.md +275 -275
  81. package/docs/developer-ecosystem.md +15 -13
  82. package/docs/developer-proof-quickstart.md +325 -325
  83. package/docs/enigma-memory-ready-conformance.md +378 -376
  84. package/docs/install-anywhere.md +64 -30
  85. package/docs/installers-and-desktop.md +8 -7
  86. package/docs/memory-benchmarks.md +1 -1
  87. package/docs/memory-drive-health-model.md +690 -690
  88. package/docs/novelty-invention-candidates.md +161 -161
  89. package/docs/proof-network-build-notes.md +240 -240
  90. package/docs/proof-network-claim-boundaries.md +320 -318
  91. package/docs/proof-network.md +339 -339
  92. package/docs/sdk-api.md +324 -324
  93. package/docs/solana-proof-rail.md +453 -453
  94. package/examples/01-quickstart-agent/index.mjs +49 -0
  95. package/examples/01_agent_memory_quickstart.mjs +57 -0
  96. package/examples/02-multi-agent-swarm/index.mjs +57 -0
  97. package/examples/02_cross_model_passport.mjs +64 -0
  98. package/examples/03-langchain-memory/index.mjs +41 -0
  99. package/examples/03_poseidon_commitment_verification.mjs +71 -0
  100. package/examples/04-python-trading-agent/trader.py +49 -0
  101. package/examples/README.md +27 -0
  102. package/examples/ci/github-actions.yml +7 -2
  103. package/package.json +410 -278
  104. package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
  105. package/packages/connectors/src/index.js +196 -4
  106. package/packages/connectors/swarm-router.mjs +168 -0
  107. package/packages/core/src/index.js +249 -2
  108. package/packages/core/src/version.mjs +7 -0
  109. package/packages/dev-tools/package.json +19 -0
  110. package/packages/dev-tools/src/index.js +4 -0
  111. package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
  112. package/packages/dev-tools/src/swarm-simulator.js +101 -0
  113. package/packages/dev-tools/src/vault-inspector.js +114 -0
  114. package/packages/dev-tools/src/vector-benchmark.js +100 -0
  115. package/packages/developer-platform/src/access-credentials.js +341 -0
  116. package/packages/developer-platform/src/http.js +132 -0
  117. package/packages/developer-platform/src/index.js +4 -0
  118. package/packages/developer-platform/src/usage-http.js +60 -0
  119. package/packages/developer-platform/src/usage.js +295 -0
  120. package/packages/enclave-runtime/attestation.mjs +159 -0
  121. package/packages/enclave-runtime/index.mjs +47 -0
  122. package/packages/enclave-runtime/session-manager.mjs +253 -0
  123. package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
  124. package/packages/enigma-reflex/package.json +14 -0
  125. package/packages/enigma-reflex/src/index.js +204 -0
  126. package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
  127. package/packages/enigma-reflex/training/requirements.txt +8 -0
  128. package/packages/enigma-reflex/training/train.py +314 -0
  129. package/packages/enigma-weave/LICENSE +22 -0
  130. package/packages/enigma-weave/UPSTREAM.json +21 -0
  131. package/packages/enigma-weave/package.json +14 -0
  132. package/packages/enigma-weave/src/index.js +286 -0
  133. package/packages/hosted-cloud/src/index.js +80 -5
  134. package/packages/importers/src/index.js +432 -0
  135. package/packages/inference-runtime/src/browser.js +401 -0
  136. package/packages/inference-runtime/src/chat.js +265 -0
  137. package/packages/inference-runtime/src/code.js +407 -0
  138. package/packages/inference-runtime/src/contracts.js +162 -0
  139. package/packages/inference-runtime/src/http.js +232 -0
  140. package/packages/inference-runtime/src/image.js +186 -0
  141. package/packages/inference-runtime/src/index.js +10 -0
  142. package/packages/inference-runtime/src/model-router.js +320 -0
  143. package/packages/inference-runtime/src/platform.js +125 -0
  144. package/packages/inference-runtime/src/privacy.js +400 -0
  145. package/packages/inference-runtime/src/video.js +253 -0
  146. package/packages/mcp-server/README.md +22 -6
  147. package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
  148. package/packages/mcp-server/src/index.js +2498 -1185
  149. package/packages/mcp-server/src/oauth.js +561 -0
  150. package/packages/mcp-server/src/private-handoff.js +84 -0
  151. package/packages/mcp-server/src/remote-http.js +273 -0
  152. package/packages/mcp-server/src/remote-policy.js +72 -0
  153. package/packages/mcp-server/swarm-bridge.mjs +361 -0
  154. package/packages/mesh/index.d.ts +283 -0
  155. package/packages/mesh/package.json +23 -0
  156. package/packages/mesh/src/crypto.js +189 -0
  157. package/packages/mesh/src/federation-packets.js +353 -0
  158. package/packages/mesh/src/gossip.js +311 -0
  159. package/packages/mesh/src/index.js +6 -0
  160. package/packages/mesh/src/protocol.js +255 -0
  161. package/packages/mesh/src/router.js +279 -0
  162. package/packages/mesh/src/transport.js +306 -0
  163. package/packages/passport/src/index.js +436 -7
  164. package/packages/private-economy/src/credits-http.js +100 -0
  165. package/packages/private-economy/src/credits.js +447 -0
  166. package/packages/private-economy/src/index.js +5 -0
  167. package/packages/private-economy/src/payments-http.js +120 -0
  168. package/packages/private-economy/src/payments.js +509 -0
  169. package/packages/private-economy/src/x402.js +346 -0
  170. package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
  171. package/packages/rag/index.d.ts +182 -0
  172. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
  173. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
  174. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
  175. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
  176. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
  177. package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
  178. package/packages/rag/package.json +27 -0
  179. package/packages/rag/src/blinded-search.js +109 -0
  180. package/packages/rag/src/bm25.js +169 -0
  181. package/packages/rag/src/embeddings.js +459 -0
  182. package/packages/rag/src/hybrid.js +76 -0
  183. package/packages/rag/src/index.js +38 -0
  184. package/packages/rag/src/reranker.js +61 -0
  185. package/packages/rag/src/research.js +107 -0
  186. package/packages/rag/src/vector-store.js +430 -0
  187. package/packages/rag/src/verify-model-artifacts.mjs +4 -0
  188. package/packages/sdk/index.d.ts +760 -0
  189. package/packages/sdk/package.json +33 -0
  190. package/packages/sdk/python/README.md +24 -0
  191. package/packages/sdk/python/enigma_sdk.py +250 -0
  192. package/packages/sdk/python/pyproject.toml +34 -0
  193. package/packages/sdk/python/requirements.txt +1 -0
  194. package/packages/sdk/python/setup.py +20 -0
  195. package/packages/sdk/src/federation/capability-grant.js +389 -0
  196. package/packages/sdk/src/federation/federation-router.js +360 -0
  197. package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
  198. package/packages/sdk/src/federation/index.js +3 -0
  199. package/packages/sdk/src/index.js +1796 -0
  200. package/packages/sdk/src/intelligence/contradiction.js +337 -0
  201. package/packages/sdk/src/intelligence/decision-engine.js +155 -0
  202. package/packages/sdk/src/intelligence/index.js +4 -0
  203. package/packages/sdk/src/intelligence/ontology.js +122 -0
  204. package/packages/sdk/src/intelligence/temporal.js +123 -0
  205. package/packages/sdk/src/market-client.js +142 -0
  206. package/packages/sdk/src/mesh-client.js +110 -0
  207. package/packages/sdk/src/middleware/index.js +3 -0
  208. package/packages/sdk/src/middleware/langchain.js +159 -0
  209. package/packages/sdk/src/middleware/llamaindex.js +101 -0
  210. package/packages/sdk/src/middleware/vercel-ai.js +112 -0
  211. package/packages/sdk/src/rag-client.js +85 -0
  212. package/packages/sdk/src/swarm-orchestrator.js +260 -0
  213. package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
  214. package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
  215. package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
  216. package/packages/snapcompact/index.d.ts +284 -0
  217. package/packages/snapcompact/package.json +25 -0
  218. package/packages/snapcompact/src/index.js +716 -0
  219. package/packages/storage/PACKAGE_CONTRACT.md +1 -1
  220. package/packages/terminal-console/animations.mjs +240 -0
  221. package/packages/terminal-console/auto-anchor.mjs +220 -0
  222. package/packages/terminal-console/banner.mjs +91 -0
  223. package/packages/terminal-console/commands.mjs +459 -0
  224. package/packages/terminal-console/delegation.mjs +152 -0
  225. package/packages/terminal-console/index.mjs +5 -0
  226. package/packages/terminal-console/outbox.mjs +143 -0
  227. package/packages/terminal-console/phantom-bridge.mjs +637 -0
  228. package/packages/terminal-console/repl.mjs +136 -0
  229. package/packages/terminal-console/signer-store.mjs +130 -0
  230. package/packages/terminal-console/solana-rpc.mjs +214 -0
  231. package/packages/terminal-console/solana-transport.mjs +189 -0
  232. package/packages/terminal-tui/dashboard.mjs +214 -0
  233. package/packages/terminal-tui/index.mjs +28 -0
  234. package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
  235. package/packages/terminal-tui/telemetry-hud.mjs +137 -0
  236. package/packages/vault/index.d.ts +449 -0
  237. package/packages/vault/package.json +27 -0
  238. package/packages/vault/src/e2ee.mjs +393 -0
  239. package/packages/vault/src/enclave.js +481 -0
  240. package/packages/vault/src/erasure.js +207 -0
  241. package/packages/vault/src/index.js +1150 -125
  242. package/packages/vault/src/persistence.js +307 -0
  243. package/packages/vault/src/poseidon.js +354 -0
  244. package/packages/vault/src/receipt.js +459 -0
  245. package/scripts/benchmark-optical-context.mjs +166 -0
  246. package/scripts/bootstrap-enigma.mjs +502 -0
  247. package/scripts/build-edge-backend-workers.mjs +20 -5
  248. package/scripts/build-goal-completion-audit.mjs +72 -25
  249. package/scripts/build-hosted-api-key-lifecycle.mjs +292 -274
  250. package/scripts/build-hosted-customer-lifecycle.mjs +493 -476
  251. package/scripts/build-hosted-probe-worker.mjs +19 -4
  252. package/scripts/build-installer-assets.mjs +409 -389
  253. package/scripts/build-operator-evidence-starter.mjs +59 -1
  254. package/scripts/build-production-backend-env-kit.mjs +2 -0
  255. package/scripts/build-production-unblocker.mjs +4 -1
  256. package/scripts/build-proof-network-packet.mjs +213 -213
  257. package/scripts/check.mjs +25 -4
  258. package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
  259. package/scripts/install-enigma-local.mjs +18 -5
  260. package/scripts/release-audit.mjs +74 -115
  261. package/scripts/release-provenance.mjs +12 -2
  262. package/scripts/run-backend-readiness-smoke.mjs +112 -10
  263. package/scripts/run-standard-memory-benchmarks.mjs +1354 -1352
  264. package/scripts/scan-secrets.mjs +178 -0
  265. package/scripts/simulate-production-env.mjs +71 -10
  266. package/scripts/validate-hosted-backend-live.mjs +112 -1
  267. package/specs/antibody-pack-v1.schema.json +95 -0
  268. package/specs/antigen-envelope-v1.schema.json +81 -0
  269. package/specs/boundary-manifest-v1.schema.json +35 -35
  270. package/specs/capsule-v1.schema.json +55 -55
  271. package/specs/claim-boundary-manifest-v1.schema.json +22 -22
  272. package/specs/claim-ledger-v1.schema.json +291 -0
  273. package/specs/context-passport-v1.schema.json +59 -0
  274. package/specs/deletion-tombstone-v1.schema.json +26 -26
  275. package/specs/evidence-packet-v1.schema.json +177 -0
  276. package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
  277. package/specs/immune-scan-report-v1.schema.json +112 -0
  278. package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
  279. package/specs/memory-atom-v1.schema.json +59 -0
  280. package/specs/memory-event-v1.schema.json +42 -42
  281. package/specs/passport-v1.schema.json +50 -50
  282. package/specs/proof-of-non-use-v1.schema.json +65 -0
  283. package/specs/quarantine-record-v1.schema.json +126 -0
  284. package/specs/receipt-v1.schema.json +61 -61
  285. package/specs/state-checkpoint-v1.schema.json +37 -37
  286. package/specs/trust-bundle-v1.schema.json +56 -56
  287. package/specs/trust-card-v1.schema.json +119 -0
  288. package/docs/proof-network-launch-plan.md +0 -421
  289. package/packages/metering/PACKAGE_CONTRACT.md +0 -20
  290. package/scripts/build-ai-orchestration-plan.mjs +0 -248
@@ -0,0 +1,279 @@
1
+ // Enigma Encrypted Mesh Protocol ("GhostMesh") — Packet Router & Distance-Vector Engine.
2
+ // Maintains on-demand hop-by-hop routing tables, packet deduplication caches,
3
+ // and store-and-forward queues for multi-hop mesh propagation.
4
+ import { PACKET_TYPES, decodePacket, deriveDestination, encodePacket, getPacketReplayIdentity } from './protocol.js';
5
+ import { verifyPacketSignature } from './crypto.js';
6
+
7
+ export const ROUTE_TTL_MS = 600_000; // 10 minutes route expiry
8
+ export const DEDUP_CACHE_TTL_MS = 120_000; // 2 minutes deduplication cache
9
+ export const MAX_STORE_FORWARD_ITEMS = 1000;
10
+ export const MAX_DEDUP_CACHE_ITEMS = 10_000;
11
+ export const MAX_KNOWN_PEERS = 1_024;
12
+
13
+ export class PacketRouter {
14
+ /**
15
+ * @param {Object} options
16
+ * @param {Buffer} options.nodeIdentity - This node's Ed25519 identity keypair
17
+ * @param {string} [options.nodeId] - Optional readable label
18
+ */
19
+ constructor(options = {}) {
20
+ this.identity = options.nodeIdentity;
21
+ this.nodeId = options.nodeId || this.identity?.destinationHex?.slice(0, 12) || 'mesh-node';
22
+ this.authenticatePacket = options.verifyPacketSignature ?? verifyPacketSignature;
23
+
24
+ // Destination routing table: destinationHex -> { peerId, hopCount, transportId, updatedAt, expiresAt }
25
+ this.routes = new Map();
26
+
27
+ // Known peer identities: peerId -> { pubkey, destinationHex, transportId, lastSeen }
28
+ this.peers = new Map();
29
+
30
+ // Packet deduplication cache: packetHashHex -> timestamp
31
+ this.seenPackets = new Map();
32
+
33
+ // Store-and-Forward queue for offline / delayed hops: id -> { packet, destinationHex, queuedAt, attempts }
34
+ this.storeForwardQueue = new Map();
35
+
36
+ // Event listeners
37
+ this.handlers = {
38
+ packet: new Set(),
39
+ delivered: new Set(),
40
+ routed: new Set(),
41
+ announce: new Set(),
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Registers a transport interface or direct peer connection.
47
+ */
48
+ registerPeer(peerId, peerInfo) {
49
+ if (!this.peers.has(peerId) && this.peers.size >= MAX_KNOWN_PEERS) return false;
50
+ this.peers.set(peerId, {
51
+ ...this.peers.get(peerId),
52
+ ...peerInfo,
53
+ lastSeen: Date.now(),
54
+ });
55
+ return true;
56
+ }
57
+
58
+ unregisterPeer(peerId) {
59
+ this.peers.delete(peerId);
60
+ for (const [dest, route] of this.routes.entries()) {
61
+ if (route.peerId === peerId) {
62
+ this.routes.delete(dest);
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Processes an incoming raw packet buffer from a peer/transport.
69
+ * @param {Buffer} rawPacket
70
+ * @param {string} fromPeerId
71
+ * @param {string} transportId
72
+ * @returns {Promise<{action: 'DELIVERED'|'ROUTED'|'DROPPED'|'QUEUED'|'ANNOUNCED', packet?: Object, reason?: string}>}
73
+ */
74
+ async handleIncomingPacket(rawPacket, fromPeerId, transportId) {
75
+ // Decode the bounded frame once before deriving its stable replay identity.
76
+ // Authenticated cache hits may skip Ed25519 work, but unauthenticated frames
77
+ // are never inserted and therefore cannot poison the replay cache.
78
+ let decoded;
79
+ let packetHash;
80
+ try {
81
+ decoded = decodePacket(rawPacket);
82
+ packetHash = getPacketReplayIdentity(decoded.rawBytes);
83
+ } catch (err) {
84
+ return { action: 'DROPPED', reason: `decode_error: ${err.message}` };
85
+ }
86
+
87
+ const now = Date.now();
88
+ this.pruneDedupCache(now);
89
+ if (this.seenPackets.has(packetHash)) {
90
+ return { action: 'DROPPED', reason: 'duplicate_packet' };
91
+ }
92
+
93
+ const registeredPeer = this.peers.get(fromPeerId);
94
+ if (
95
+ registeredPeer?.publicKey
96
+ && !Buffer.from(registeredPeer.publicKey).equals(decoded.senderPubkey)
97
+ ) {
98
+ return { action: 'DROPPED', reason: 'peer_identity_mismatch' };
99
+ }
100
+ if (!this.authenticatePacket(rawPacket)) {
101
+ return { action: 'DROPPED', reason: 'invalid_signature' };
102
+ }
103
+
104
+ let announceMetadata = null;
105
+ if (decoded.type === PACKET_TYPES.ANNOUNCE) {
106
+ try {
107
+ announceMetadata = JSON.parse(decoded.payload.toString('utf8'));
108
+ const scope = announceMetadata?.scope;
109
+ const encryptionKeyHex = announceMetadata?.encryptionPublicKeyHex;
110
+ if (
111
+ !announceMetadata
112
+ || typeof announceMetadata !== 'object'
113
+ || Array.isArray(announceMetadata)
114
+ || typeof scope !== 'string'
115
+ || scope.length === 0
116
+ || typeof encryptionKeyHex !== 'string'
117
+ || !/^[a-f0-9]{64}$/i.test(encryptionKeyHex)
118
+ ) {
119
+ throw new Error('announce metadata must contain a scope and 32-byte X25519 key');
120
+ }
121
+ const expectedDestination = deriveDestination(
122
+ decoded.senderPubkey,
123
+ Buffer.from(encryptionKeyHex, 'hex'),
124
+ scope,
125
+ );
126
+ if (!expectedDestination.equals(decoded.destination)) {
127
+ throw new Error('announce destination does not match its signing key, encryption key, and scope');
128
+ }
129
+ } catch (error) {
130
+ return { action: 'DROPPED', reason: `invalid_announce_binding: ${error.message}` };
131
+ }
132
+ }
133
+
134
+ if (this.seenPackets.size >= MAX_DEDUP_CACHE_ITEMS) {
135
+ this.seenPackets.delete(this.seenPackets.keys().next().value);
136
+ }
137
+ this.seenPackets.set(packetHash, now);
138
+
139
+ // 4. Update route if packet is an ANNOUNCE
140
+ if (decoded.type === PACKET_TYPES.ANNOUNCE) {
141
+ this.updateRoute(decoded.destinationHex, fromPeerId, decoded.hopTtl, transportId, now);
142
+ this.emit('announce', { packet: decoded, metadata: announceMetadata, fromPeerId });
143
+ }
144
+
145
+ // 5. Check if packet is addressed to THIS node or is a broadcast packet
146
+ const isBroadcast = Buffer.from(decoded.destination).every((b) => b === 0xff) ||
147
+ decoded.type === PACKET_TYPES.SEALED_SALE_OFFER ||
148
+ decoded.type === PACKET_TYPES.ZK_PROOF_GOSSIP;
149
+
150
+ const isForMe = (this.identity && Buffer.from(decoded.destination).equals(this.identity.destination)) || isBroadcast;
151
+
152
+ if (isForMe) {
153
+ this.emit('delivered', { packet: decoded, fromPeerId });
154
+ if (!isBroadcast) {
155
+ return { action: 'DELIVERED', packet: decoded };
156
+ }
157
+ }
158
+ // 6. Multi-hop Forwarding / Routing
159
+ if (decoded.hopTtl <= 1) {
160
+ return { action: 'DROPPED', reason: 'ttl_expired' };
161
+ }
162
+
163
+ // Decrement TTL for the next hop
164
+ const nextHopTtl = decoded.hopTtl - 1;
165
+ const forwardBuffer = encodePacket({
166
+ ...decoded,
167
+ hopTtl: nextHopTtl,
168
+ signature: decoded.signature,
169
+ });
170
+
171
+ const route = this.getBestRoute(decoded.destinationHex);
172
+ if (route && route.peerId !== fromPeerId) {
173
+ this.emit('routed', { packet: decoded, nextHopPeerId: route.peerId, transportId: route.transportId, forwardBuffer });
174
+ return { action: 'ROUTED', packet: decoded, nextHopPeerId: route.peerId, transportId: route.transportId, forwardBuffer };
175
+ }
176
+
177
+ // 7. Store-and-forward queue if route is currently unreachable
178
+ this.queueStoreForward(packetHash, forwardBuffer, decoded.destinationHex, now);
179
+ return { action: 'QUEUED', packet: decoded, reason: 'route_unreachable_stored_for_relay' };
180
+ }
181
+
182
+ /**
183
+ * Updates the routing table with distance-vector next-hop data.
184
+ */
185
+ updateRoute(destinationHex, peerId, hopCount, transportId, now = Date.now()) {
186
+ const existing = this.routes.get(destinationHex);
187
+ // Lower hop count or fresher update takes precedence
188
+ if (!existing || hopCount < existing.hopCount || (hopCount === existing.hopCount && now > existing.updatedAt)) {
189
+ this.routes.set(destinationHex, {
190
+ peerId,
191
+ hopCount,
192
+ transportId,
193
+ updatedAt: now,
194
+ expiresAt: now + ROUTE_TTL_MS,
195
+ });
196
+ }
197
+ }
198
+
199
+ getBestRoute(destinationHex) {
200
+ const route = this.routes.get(destinationHex);
201
+ if (!route) return null;
202
+ if (Date.now() > route.expiresAt) {
203
+ this.routes.delete(destinationHex);
204
+ return null;
205
+ }
206
+ return route;
207
+ }
208
+
209
+ queueStoreForward(id, packetBuffer, destinationHex, now = Date.now()) {
210
+ if (this.storeForwardQueue.size >= MAX_STORE_FORWARD_ITEMS) {
211
+ const oldestKey = this.storeForwardQueue.keys().next().value;
212
+ this.storeForwardQueue.delete(oldestKey);
213
+ }
214
+ this.storeForwardQueue.set(id, {
215
+ packet: packetBuffer,
216
+ destinationHex,
217
+ queuedAt: now,
218
+ attempts: 0,
219
+ });
220
+ }
221
+
222
+ /**
223
+ * Flushes store-and-forward packets when a new peer/route becomes available.
224
+ * @param {Function} forwardCallback - async (packetBuffer, peerId, transportId) => boolean
225
+ */
226
+ async flushStoreForward(forwardCallback) {
227
+ let flushed = 0;
228
+ for (const [id, item] of Array.from(this.storeForwardQueue.entries())) {
229
+ const route = this.getBestRoute(item.destinationHex);
230
+ if (route) {
231
+ try {
232
+ const success = await forwardCallback(item.packet, route.peerId, route.transportId);
233
+ if (success) {
234
+ this.storeForwardQueue.delete(id);
235
+ flushed++;
236
+ }
237
+ } catch {
238
+ item.attempts++;
239
+ }
240
+ }
241
+ }
242
+ return flushed;
243
+ }
244
+
245
+ pruneDedupCache(now = Date.now()) {
246
+ for (const [hash, time] of this.seenPackets.entries()) {
247
+ if (now - time > DEDUP_CACHE_TTL_MS) {
248
+ this.seenPackets.delete(hash);
249
+ }
250
+ }
251
+ }
252
+
253
+ on(event, handler) {
254
+ if (this.handlers[event]) this.handlers[event].add(handler);
255
+ }
256
+
257
+ off(event, handler) {
258
+ if (this.handlers[event]) this.handlers[event].delete(handler);
259
+ }
260
+
261
+ emit(event, data) {
262
+ if (this.handlers[event]) {
263
+ for (const h of this.handlers[event]) {
264
+ try { h(data); } catch {}
265
+ }
266
+ }
267
+ }
268
+
269
+ getRoutingStats() {
270
+ return {
271
+ nodeId: this.nodeId,
272
+ destinationHex: this.identity?.destinationHex || null,
273
+ activePeers: this.peers.size,
274
+ knownRoutes: this.routes.size,
275
+ storeForwardQueueSize: this.storeForwardQueue.size,
276
+ seenPacketsCacheSize: this.seenPackets.size,
277
+ };
278
+ }
279
+ }
@@ -0,0 +1,306 @@
1
+ // Enigma Encrypted Mesh Protocol ("GhostMesh") — Multi-Transport Layer.
2
+ // Provides AbstractTransport, InMemoryTransport (for multi-node simulation),
3
+ import crypto from 'node:crypto';
4
+ import { EventEmitter } from 'node:events';
5
+ import WebSocket, { WebSocketServer } from 'ws';
6
+ import { generateMeshIdentity, importRawEd25519PrivateKey, importRawEd25519PublicKey } from './crypto.js';
7
+ import { MAX_PACKET_SIZE } from './protocol.js';
8
+ function receivedBinaryFrame(data, isBinary) {
9
+ if (typeof isBinary === 'boolean') return isBinary;
10
+ return Buffer.isBuffer(data) || ArrayBuffer.isView(data) || data instanceof ArrayBuffer;
11
+ }
12
+
13
+ export class AbstractTransport extends EventEmitter {
14
+ constructor(id) {
15
+ super();
16
+ this.id = id || 'transport-' + Math.random().toString(16).slice(2, 8);
17
+ this.running = false;
18
+ }
19
+
20
+ async start() { this.running = true; }
21
+ async stop() { this.running = false; }
22
+ async send(rawPacketBuffer, peerId) { throw new Error('Not implemented'); }
23
+ async broadcast(rawPacketBuffer) { throw new Error('Not implemented'); }
24
+ }
25
+
26
+ /**
27
+ * In-Memory Transport Bus for multi-node testing and simulation.
28
+ * Allows multiple MeshNode instances to communicate over a shared memory bus with configurable packet drops/delays.
29
+ */
30
+ export class InMemoryBus {
31
+ constructor() {
32
+ this.nodes = new Map(); // nodeId -> InMemoryTransport
33
+ }
34
+
35
+ register(nodeId, transport) {
36
+ this.nodes.set(nodeId, transport);
37
+ }
38
+
39
+ unregister(nodeId) {
40
+ this.nodes.delete(nodeId);
41
+ }
42
+
43
+ deliver(fromNodeId, toNodeId, packetBuffer) {
44
+ const target = this.nodes.get(toNodeId);
45
+ if (target && target.running) {
46
+ target.emit('packet', { packetBuffer, fromPeerId: fromNodeId, transportId: target.id });
47
+ }
48
+ }
49
+
50
+ broadcast(fromNodeId, packetBuffer) {
51
+ for (const [nodeId, transport] of this.nodes.entries()) {
52
+ if (nodeId !== fromNodeId && transport.running) {
53
+ transport.emit('packet', { packetBuffer, fromPeerId: fromNodeId, transportId: transport.id });
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ export class InMemoryTransport extends AbstractTransport {
60
+ constructor(nodeId, bus, id) {
61
+ super(id || `inmem-${nodeId}`);
62
+ this.nodeId = nodeId;
63
+ this.bus = bus;
64
+ this.bus.register(nodeId, this);
65
+ }
66
+
67
+ async send(rawPacketBuffer, peerId) {
68
+ if (!this.running) return false;
69
+ this.bus.deliver(this.nodeId, peerId, rawPacketBuffer);
70
+ return true;
71
+ }
72
+
73
+ async broadcast(rawPacketBuffer) {
74
+ if (!this.running) return false;
75
+ this.bus.broadcast(this.nodeId, rawPacketBuffer);
76
+ return true;
77
+ }
78
+
79
+ async stop() {
80
+ await super.stop();
81
+ this.bus.unregister(this.nodeId);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * WebSocket Peer-to-Peer Transport for LAN and WAN Mesh Peering.
87
+ */
88
+ export class WebSocketTransport extends AbstractTransport {
89
+ constructor(options = {}) {
90
+ super(options.id || 'ws-transport');
91
+ this.port = options.port ?? 0;
92
+ this.host = options.host || '127.0.0.1';
93
+ this.server = null;
94
+ this.actualPort = null;
95
+ this.sockets = new Map();
96
+ this.pendingSockets = new Set();
97
+ this.peerAddresses = new Set(options.peers || []);
98
+ this.identity = options.identity || generateMeshIdentity(options.scope || 'enigma.transport');
99
+ this.allowedPeerPublicKeys = new Set(
100
+ (options.allowedPeerPublicKeys || []).map((key) => (
101
+ Buffer.isBuffer(key) || key instanceof Uint8Array
102
+ ? Buffer.from(key).toString('hex')
103
+ : String(key).toLowerCase()
104
+ )),
105
+ );
106
+ this.allowUnlistedPeers = options.allowUnlistedPeers
107
+ ?? (this.host === '127.0.0.1' || this.host === 'localhost' || this.host === '::1');
108
+ this.maxConnections = Number.isSafeInteger(options.maxConnections) && options.maxConnections > 0
109
+ ? options.maxConnections
110
+ : 64;
111
+ this.handshakeTimeoutMs = Number.isSafeInteger(options.handshakeTimeoutMs) && options.handshakeTimeoutMs > 0
112
+ ? options.handshakeTimeoutMs
113
+ : 5_000;
114
+ }
115
+
116
+ async start() {
117
+ if (!this.allowUnlistedPeers && this.allowedPeerPublicKeys.size === 0) {
118
+ throw new Error('externally reachable WebSocket transport requires an allowedPeerPublicKeys allowlist');
119
+ }
120
+ await super.start();
121
+ if (this.port !== null && this.port !== undefined) {
122
+ await new Promise((resolve, reject) => {
123
+ this.server = new WebSocketServer({
124
+ port: this.port,
125
+ host: this.host,
126
+ maxPayload: MAX_PACKET_SIZE,
127
+ }, () => {
128
+ this.actualPort = this.server.address().port;
129
+ resolve();
130
+ });
131
+ this.server.on('error', reject);
132
+ this.server.on('connection', (ws, req) => {
133
+ if (this.sockets.size + this.pendingSockets.size >= this.maxConnections) {
134
+ ws.close(1013, 'connection capacity reached');
135
+ return;
136
+ }
137
+ this.setupSocket(ws, `${req.socket.remoteAddress}:${req.socket.remotePort}`);
138
+ });
139
+ });
140
+ }
141
+ for (const addr of this.peerAddresses) this.connectToPeer(addr).catch(() => {});
142
+ }
143
+
144
+ handshakeBytes(nonce) {
145
+ return Buffer.concat([Buffer.from('enigma.ghostmesh.ws.handshake.v1\0'), nonce]);
146
+ }
147
+
148
+ setupSocket(ws, address, onAuthenticated = null, onError = null) {
149
+ const state = {
150
+ address,
151
+ authenticated: false,
152
+ peerId: null,
153
+ localNonce: crypto.randomBytes(32),
154
+ };
155
+ this.pendingSockets.add(ws);
156
+ const timer = setTimeout(() => ws.close(1008, 'handshake timeout'), this.handshakeTimeoutMs);
157
+ ws.send(JSON.stringify({
158
+ kind: 'enigma.ghostmesh.ws.challenge.v1',
159
+ nonce: state.localNonce.toString('base64'),
160
+ }));
161
+
162
+ ws.on('message', (data, isBinary) => {
163
+ if (!state.authenticated) {
164
+ if (receivedBinaryFrame(data, isBinary)) {
165
+ ws.close(1003, 'handshake requires text frames');
166
+ return;
167
+ }
168
+ try {
169
+ const message = JSON.parse(Buffer.from(data).toString('utf8'));
170
+ if (message.kind === 'enigma.ghostmesh.ws.challenge.v1') {
171
+ const nonce = Buffer.from(message.nonce || '', 'base64');
172
+ if (nonce.length !== 32) throw new Error('invalid handshake nonce');
173
+ const signature = crypto.sign(
174
+ null,
175
+ this.handshakeBytes(nonce),
176
+ importRawEd25519PrivateKey(this.identity.secretKey),
177
+ );
178
+ ws.send(JSON.stringify({
179
+ kind: 'enigma.ghostmesh.ws.response.v1',
180
+ nonce: message.nonce,
181
+ public_key: this.identity.publicKey.toString('hex'),
182
+ signature: signature.toString('base64'),
183
+ }));
184
+ return;
185
+ }
186
+ if (message.kind !== 'enigma.ghostmesh.ws.response.v1') throw new Error('unexpected handshake message');
187
+ const publicKey = Buffer.from(message.public_key || '', 'hex');
188
+ const nonce = Buffer.from(message.nonce || '', 'base64');
189
+ const signature = Buffer.from(message.signature || '', 'base64');
190
+ if (
191
+ publicKey.length !== 32
192
+ || nonce.length !== 32
193
+ || signature.length !== 64
194
+ || !crypto.timingSafeEqual(nonce, state.localNonce)
195
+ || !crypto.verify(null, this.handshakeBytes(nonce), importRawEd25519PublicKey(publicKey), signature)
196
+ ) {
197
+ throw new Error('invalid signed handshake response');
198
+ }
199
+ const publicKeyHex = publicKey.toString('hex');
200
+ if (!this.allowUnlistedPeers && !this.allowedPeerPublicKeys.has(publicKeyHex)) {
201
+ throw new Error('peer identity is not allowlisted');
202
+ }
203
+ if (this.sockets.size >= this.maxConnections && !this.sockets.has(publicKeyHex)) {
204
+ throw new Error('connection capacity reached');
205
+ }
206
+ state.authenticated = true;
207
+ state.peerId = publicKeyHex;
208
+ clearTimeout(timer);
209
+ this.pendingSockets.delete(ws);
210
+ const prior = this.sockets.get(publicKeyHex);
211
+ if (prior && prior !== ws) prior.close(1000, 'superseded authenticated connection');
212
+ this.sockets.set(publicKeyHex, ws);
213
+ this.emit('peer:connect', { peerId: publicKeyHex, publicKey, address });
214
+ onAuthenticated?.(publicKeyHex);
215
+ } catch (error) {
216
+ onError?.(error);
217
+ ws.close(1008, 'authentication failed');
218
+ }
219
+ return;
220
+ }
221
+ if (!receivedBinaryFrame(data, isBinary)) {
222
+ ws.close(1003, 'GhostMesh packets must be binary');
223
+ return;
224
+ }
225
+ const packetBuffer = Buffer.from(data);
226
+ this.emit('packet', { packetBuffer, fromPeerId: state.peerId, transportId: this.id });
227
+ });
228
+
229
+ ws.on('close', () => {
230
+ clearTimeout(timer);
231
+ this.pendingSockets.delete(ws);
232
+ if (!state.authenticated) onError?.(new Error('WebSocket peer closed before authentication completed'));
233
+ if (state.peerId && this.sockets.get(state.peerId) === ws) {
234
+ this.sockets.delete(state.peerId);
235
+ this.emit('peer:disconnect', { peerId: state.peerId });
236
+ }
237
+ });
238
+ ws.on('error', (error) => {
239
+ clearTimeout(timer);
240
+ this.pendingSockets.delete(ws);
241
+ onError?.(error);
242
+ });
243
+ }
244
+
245
+ async connectToPeer(address) {
246
+ return await new Promise((resolve, reject) => {
247
+ const url = address.startsWith('ws://') || address.startsWith('wss://') ? address : `ws://${address}`;
248
+ const ws = new WebSocket(url, { maxPayload: MAX_PACKET_SIZE });
249
+ let settled = false;
250
+ const finish = (fn, value) => {
251
+ if (settled) return;
252
+ settled = true;
253
+ fn(value);
254
+ };
255
+ ws.once('open', () => this.setupSocket(
256
+ ws,
257
+ address,
258
+ (peerId) => finish(resolve, peerId),
259
+ (error) => finish(reject, error),
260
+ ));
261
+ ws.once('error', (error) => finish(reject, error));
262
+ });
263
+ }
264
+
265
+ async send(rawPacketBuffer, peerId) {
266
+ const ws = this.sockets.get(peerId);
267
+ if (!ws || ws.readyState !== WebSocket.OPEN) return false;
268
+ ws.send(rawPacketBuffer, { binary: true });
269
+ return true;
270
+ }
271
+
272
+ async broadcast(rawPacketBuffer) {
273
+ let sent = 0;
274
+ for (const ws of this.sockets.values()) {
275
+ if (ws.readyState === WebSocket.OPEN) {
276
+ ws.send(rawPacketBuffer, { binary: true });
277
+ sent++;
278
+ }
279
+ }
280
+ return sent > 0;
281
+ }
282
+
283
+ async stop() {
284
+ await super.stop();
285
+ for (const ws of [...this.sockets.values(), ...this.pendingSockets]) {
286
+ try { ws.close(); } catch {}
287
+ }
288
+ this.sockets.clear();
289
+ this.pendingSockets.clear();
290
+ if (this.server) {
291
+ await new Promise((resolve) => this.server.close(resolve));
292
+ this.server = null;
293
+ }
294
+ }
295
+
296
+ getStats() {
297
+ return {
298
+ id: this.id,
299
+ running: this.running,
300
+ listenPort: this.actualPort,
301
+ connectedPeers: this.sockets.size,
302
+ pendingHandshakes: this.pendingSockets.size,
303
+ maxConnections: this.maxConnections,
304
+ };
305
+ }
306
+ }