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.
Files changed (232) hide show
  1. package/CHANGELOG.md +637 -0
  2. package/CONTRIBUTING.md +42 -0
  3. package/Caddyfile +77 -0
  4. package/README.md +119 -29
  5. package/adapters/adapter-mlv-bible/README.md +124 -0
  6. package/adapters/adapter-mlv-bible/index.js +400 -0
  7. package/adapters/chat-mod-adapter.js +532 -0
  8. package/adapters/content-adapter.js +273 -0
  9. package/content/api.js +50 -41
  10. package/content/index.js +2 -2
  11. package/content/store.js +355 -173
  12. package/dashboard/index.html +19 -3
  13. package/database/replication.js +117 -37
  14. package/docs/CRYPTO-AGILITY.md +204 -0
  15. package/docs/MTLS-RESEARCH.md +367 -0
  16. package/docs/NAMCHE-SPEC.md +681 -0
  17. package/docs/PEERQUANTA-YAKMESH-INTEGRATION.md +407 -0
  18. package/docs/PRECISION-DISCLOSURE.md +96 -0
  19. package/docs/README.md +76 -0
  20. package/docs/ROADMAP-2.4.0.md +447 -0
  21. package/docs/ROADMAP-2.5.0.md +244 -0
  22. package/docs/SECURITY-AUDIT-REPORT.md +306 -0
  23. package/docs/SST-INTEGRATION.md +712 -0
  24. package/docs/STEADYWATCH-IMPLEMENTATION.md +303 -0
  25. package/docs/TERNARY-AUDIT-REPORT.md +247 -0
  26. package/docs/TME-FAQ.md +221 -0
  27. package/docs/WHITEPAPER.md +623 -0
  28. package/docs/adapters.html +1001 -0
  29. package/docs/advanced-systems.html +1045 -0
  30. package/docs/annex.html +1046 -0
  31. package/docs/api.html +970 -0
  32. package/docs/business/response-templates.md +160 -0
  33. package/docs/c2c.html +1225 -0
  34. package/docs/cli.html +1332 -0
  35. package/docs/configuration.html +1248 -0
  36. package/docs/darshan.html +1085 -0
  37. package/docs/dharma.html +966 -0
  38. package/docs/docs-bundle.html +1075 -0
  39. package/docs/docs.css +3120 -0
  40. package/docs/docs.js +556 -0
  41. package/docs/doko.html +969 -0
  42. package/docs/geo-proof.html +858 -0
  43. package/docs/getting-started.html +840 -0
  44. package/docs/gumba-tutorial.html +1144 -0
  45. package/docs/gumba.html +1098 -0
  46. package/docs/index.html +914 -0
  47. package/docs/jhilke.html +1312 -0
  48. package/docs/karma.html +1100 -0
  49. package/docs/katha.html +1037 -0
  50. package/docs/lama.html +978 -0
  51. package/docs/mandala.html +1067 -0
  52. package/docs/mani.html +964 -0
  53. package/docs/mantra.html +967 -0
  54. package/docs/mesh.html +1409 -0
  55. package/docs/nakpak.html +869 -0
  56. package/docs/namche.html +928 -0
  57. package/docs/nav-order.json +53 -0
  58. package/docs/prahari.html +1043 -0
  59. package/docs/prism-bash.min.js +1 -0
  60. package/docs/prism-javascript.min.js +1 -0
  61. package/docs/prism-json.min.js +1 -0
  62. package/docs/prism-tomorrow.min.css +1 -0
  63. package/docs/prism.min.js +1 -0
  64. package/docs/privacy.html +699 -0
  65. package/docs/quick-reference.html +1181 -0
  66. package/docs/sakshi.html +1402 -0
  67. package/docs/sandboxing.md +386 -0
  68. package/docs/seva.html +911 -0
  69. package/docs/sherpa.html +871 -0
  70. package/docs/studio.html +860 -0
  71. package/docs/stupa.html +995 -0
  72. package/docs/tailwind.min.css +2 -0
  73. package/docs/tattva.html +1332 -0
  74. package/docs/terms.html +686 -0
  75. package/docs/time-server-deployment.md +166 -0
  76. package/docs/time-sources.html +1392 -0
  77. package/docs/tivra.html +1127 -0
  78. package/docs/trademark-policy.html +686 -0
  79. package/docs/tribhuj.html +1183 -0
  80. package/docs/trust-security.html +1029 -0
  81. package/docs/tutorials/backup-recovery.html +654 -0
  82. package/docs/tutorials/dashboard.html +604 -0
  83. package/docs/tutorials/domain-setup.html +605 -0
  84. package/docs/tutorials/host-website.html +456 -0
  85. package/docs/tutorials/mesh-network.html +505 -0
  86. package/docs/tutorials/mobile-access.html +445 -0
  87. package/docs/tutorials/privacy.html +467 -0
  88. package/docs/tutorials/raspberry-pi.html +600 -0
  89. package/docs/tutorials/security-basics.html +539 -0
  90. package/docs/tutorials/share-files.html +431 -0
  91. package/docs/tutorials/troubleshooting.html +637 -0
  92. package/docs/tutorials/trust-karma.html +419 -0
  93. package/docs/tutorials/yak-protocol.html +456 -0
  94. package/docs/tutorials.html +1034 -0
  95. package/docs/vani.html +1270 -0
  96. package/docs/webserver.html +809 -0
  97. package/docs/yak-protocol.html +940 -0
  98. package/docs/yak-timeserver-design.md +475 -0
  99. package/docs/yakapp.html +1015 -0
  100. package/docs/ypc27.html +1069 -0
  101. package/docs/yurt.html +1344 -0
  102. package/embedded-docs/bundle.js +334 -74
  103. package/gossip/protocol.js +247 -27
  104. package/identity/key-resolver.js +262 -0
  105. package/identity/machine-seed.js +632 -0
  106. package/identity/node-key.js +669 -368
  107. package/identity/tribhuj-ratchet.js +506 -0
  108. package/knowledge-base.js +37 -8
  109. package/launcher/yakmesh.bat +62 -0
  110. package/launcher/yakmesh.sh +70 -0
  111. package/mesh/annex.js +462 -108
  112. package/mesh/beacon-broadcast.js +113 -1
  113. package/mesh/darshan.js +1718 -0
  114. package/mesh/gumba.js +1567 -0
  115. package/mesh/jhilke.js +651 -0
  116. package/mesh/katha.js +1012 -0
  117. package/mesh/nakpak-routing.js +8 -5
  118. package/mesh/network.js +724 -34
  119. package/mesh/pulse-sync.js +4 -1
  120. package/mesh/rate-limiter.js +127 -15
  121. package/mesh/seva.js +526 -0
  122. package/mesh/sherpa-discovery.js +89 -8
  123. package/mesh/sybil-defense.js +19 -5
  124. package/mesh/temporal-encoder.js +4 -3
  125. package/mesh/vani.js +1364 -0
  126. package/mesh/yurt.js +1340 -0
  127. package/models/entropy-sentinel.onnx +0 -0
  128. package/models/karma-trust.onnx +0 -0
  129. package/models/manifest.json +43 -0
  130. package/models/sakshi-anomaly.onnx +0 -0
  131. package/oracle/code-proof-protocol.js +7 -6
  132. package/oracle/codebase-lock.js +257 -28
  133. package/oracle/index.js +74 -15
  134. package/oracle/ma902-snmp.js +678 -0
  135. package/oracle/module-sealer.js +5 -3
  136. package/oracle/network-identity.js +16 -0
  137. package/oracle/packet-checksum.js +201 -0
  138. package/oracle/sst.js +579 -0
  139. package/oracle/ternary-144t.js +714 -0
  140. package/oracle/ternary-ml.js +481 -0
  141. package/oracle/time-api.js +239 -0
  142. package/oracle/time-source.js +137 -47
  143. package/oracle/validation-oracle-hardened.js +1111 -1071
  144. package/oracle/validation-oracle.js +4 -2
  145. package/oracle/ypc27.js +211 -0
  146. package/package.json +20 -3
  147. package/protocol/yak-handler.js +35 -9
  148. package/protocol/yak-protocol.js +28 -13
  149. package/reference/cpp/yakmesh_mceliece_shard.cpp +168 -0
  150. package/reference/cpp/yakmesh_ypc27.cpp +179 -0
  151. package/sbom.json +87 -0
  152. package/scripts/security-audit.mjs +264 -0
  153. package/scripts/update-docs-nav.js +194 -0
  154. package/scripts/update-docs-sidebar.cjs +164 -0
  155. package/security/crypto-config.js +4 -3
  156. package/security/dharma-moderation.js +517 -0
  157. package/security/doko-identity.js +193 -143
  158. package/security/domain-consensus.js +86 -85
  159. package/security/fs-hardening.js +620 -0
  160. package/security/hardware-attestation.js +5 -3
  161. package/security/hybrid-trust.js +227 -87
  162. package/security/karma-rate-limiter.js +692 -0
  163. package/security/khata-protocol.js +22 -21
  164. package/security/khata-trust-integration.js +277 -150
  165. package/security/memory-safety.js +635 -0
  166. package/security/mesh-auth.js +11 -10
  167. package/security/mesh-revocation.js +373 -5
  168. package/security/namche-gateway.js +298 -69
  169. package/security/sakshi.js +460 -3
  170. package/security/sangha.js +770 -0
  171. package/security/secure-config.js +473 -0
  172. package/security/silicon-parity.js +13 -10
  173. package/security/steadywatch.js +1142 -0
  174. package/security/strike-system.js +32 -3
  175. package/security/temporal-signing.js +488 -0
  176. package/security/trit-commitment.js +464 -0
  177. package/server/crypto/annex.js +247 -0
  178. package/server/darshan-api.js +343 -0
  179. package/server/index.js +3259 -362
  180. package/server/komm-api.js +668 -0
  181. package/utils/accel.js +2273 -0
  182. package/utils/ternary-id.js +79 -0
  183. package/utils/verify-worker.js +57 -0
  184. package/webserver/index.js +95 -5
  185. package/assets/yakmesh-logo.png +0 -0
  186. package/assets/yakmesh-logo.svg +0 -80
  187. package/assets/yakmesh-logo2.png +0 -0
  188. package/assets/yakmesh-logo2sm.png +0 -0
  189. package/assets/ymsm.png +0 -0
  190. package/website/assets/silhouettes/adapters.svg +0 -107
  191. package/website/assets/silhouettes/api-endpoints.svg +0 -115
  192. package/website/assets/silhouettes/atomic-clock.svg +0 -83
  193. package/website/assets/silhouettes/base-camp.svg +0 -81
  194. package/website/assets/silhouettes/bridge.svg +0 -69
  195. package/website/assets/silhouettes/docs-bundle.svg +0 -113
  196. package/website/assets/silhouettes/doko-basket.svg +0 -70
  197. package/website/assets/silhouettes/fortress.svg +0 -93
  198. package/website/assets/silhouettes/gateway.svg +0 -54
  199. package/website/assets/silhouettes/gears.svg +0 -93
  200. package/website/assets/silhouettes/globe-satellite.svg +0 -67
  201. package/website/assets/silhouettes/karma-wheel.svg +0 -137
  202. package/website/assets/silhouettes/lama-council.svg +0 -141
  203. package/website/assets/silhouettes/mandala-network.svg +0 -169
  204. package/website/assets/silhouettes/mani-stones.svg +0 -149
  205. package/website/assets/silhouettes/mantra-wheel.svg +0 -116
  206. package/website/assets/silhouettes/mesh-nodes.svg +0 -113
  207. package/website/assets/silhouettes/nakpak.svg +0 -56
  208. package/website/assets/silhouettes/peak-lightning.svg +0 -73
  209. package/website/assets/silhouettes/sherpa.svg +0 -69
  210. package/website/assets/silhouettes/stupa-tower.svg +0 -119
  211. package/website/assets/silhouettes/tattva-eye.svg +0 -78
  212. package/website/assets/silhouettes/terminal.svg +0 -74
  213. package/website/assets/silhouettes/webserver.svg +0 -145
  214. package/website/assets/silhouettes/yak.svg +0 -78
  215. package/website/assets/yakmesh-logo.png +0 -0
  216. package/website/assets/yakmesh-logo.webp +0 -0
  217. package/website/assets/yakmesh-logo128x140.webp +0 -0
  218. package/website/assets/yakmesh-logo2.png +0 -0
  219. package/website/assets/yakmesh-logo2.svg +0 -51
  220. package/website/assets/yakmesh-logo40x44.webp +0 -0
  221. package/website/assets/yakmesh.gif +0 -0
  222. package/website/assets/yakmesh.ico +0 -0
  223. package/website/assets/yakmesh.jpg +0 -0
  224. package/website/assets/yakmesh.pdf +0 -0
  225. package/website/assets/yakmesh.png +0 -0
  226. package/website/assets/yakmesh.svg +0 -70
  227. package/website/assets/yakmesh128.webp +0 -0
  228. package/website/assets/yakmesh32.png +0 -0
  229. package/website/assets/yakmesh32.svg +0 -65
  230. package/website/assets/yakmesh32o.ico +0 -2
  231. package/website/assets/yakmesh32o.svg +0 -65
  232. package/website/assets/yakmesh32o.svgz +0 -0
@@ -0,0 +1,714 @@
1
+ /**
2
+ * TERNARY-144T — 144-Trit Hierarchical Mesh Addressing
3
+ *
4
+ * A 4-tier hierarchical addressing system using 144 balanced ternary digits,
5
+ * structured as 4 × 36-trit segments (or equivalently 4 levels of 3^3 sub-blocks).
6
+ *
7
+ * Why 144 trits?
8
+ * - 144 = 12² = 4 × 36 = 4 × 4 × 9 = (2²)(2²)(3²) — rich factorization
9
+ * - 3^144 ≈ 1.55 × 10^68 unique addresses (exceeds SHA-256 space)
10
+ * - Natural 4-tier hierarchy: Galaxy → Cluster → Zone → Node
11
+ * - Each 36-trit tier ≈ 57 bits — fits in a 64-bit word with room for metadata
12
+ * - 144 = 6 × 24 — aligns with SST's 24-cycle Fibonacci (6 full cycles)
13
+ * - 144 = 4 × 36 → each tier holds 4 × 9-trit sub-blocks (hexagonal addressing)
14
+ *
15
+ * POST-QUANTUM HARDENING:
16
+ * - 144 trits × log₂(3) ≈ 228 bits of classical entropy
17
+ * - Grover's algorithm (quantum search) halves binary bit-security but gains
18
+ * NO advantage over balanced ternary — ternary search is already optimal
19
+ * - Result: 144T provides ~256-bit equivalent post-quantum routing security
20
+ * - Combined with ML-DSA-65 signatures and ML-KEM-768 key exchange,
21
+ * the entire mesh addressing layer is quantum-resistant
22
+ *
23
+ * Hierarchy (4 tiers, 36 trits each):
24
+ *
25
+ * ┌──────────────────────────────────────────────────────────────────────────────┐
26
+ * │ Galaxy (36 trits) │ Cluster (36 trits) │ Zone (36 trits) │ Node (36 trits) │
27
+ * │ ≈ 57 bits │ ≈ 57 bits │ ≈ 57 bits │ ≈ 57 bits │
28
+ * └──────────────────────────────────────────────────────────────────────────────┘
29
+ *
30
+ * Within each tier, 36 trits decompose into 4 × 9-trit sub-blocks:
31
+ * [region:9] [sector:9] [cell:9] [local:9]
32
+ *
33
+ * This maps naturally to the SST hexagonal tessellation (HexCoord):
34
+ * - 9 trits = 3^9 = 19683 addresses per sub-block
35
+ * - 4 sub-blocks per tier = 3^36 ≈ 1.5 × 10^17 per tier
36
+ *
37
+ * Integration:
38
+ * TRIBHUJ (trit algebra) → 144T (addressing) → YPC-27 (integrity per tier)
39
+ * SST (hex geometry) → 144T (spatial mapping) → PRAHARI (satellite seeds)
40
+ * MANDALA (mesh topology) → 144T (hierarchical routing)
41
+ *
42
+ * @module oracle/ternary-144t
43
+ * @license MIT
44
+ * @copyright 2026 YAKMESH™ Contributors
45
+ */
46
+
47
+ import { Trit, TritArray, POSITIVE, NEUTRAL, NEGATIVE } from './tribhuj.js';
48
+ import { digitalRoot, getFamilyOf, SSTFamily, FIBONACCI_CYCLE_24 } from './sst.js';
49
+ import { Poly27, YPC27Checksum, N as YPC27_N, DEFAULT_SEED, bytesToTrits } from './ypc27.js';
50
+
51
+ // =============================================================================
52
+ // CONSTANTS
53
+ // =============================================================================
54
+
55
+ /** Total trit length of a full 144T address */
56
+ export const TOTAL_TRITS = 144;
57
+
58
+ /** Number of hierarchical tiers */
59
+ export const TIER_COUNT = 4;
60
+
61
+ /** Trits per tier */
62
+ export const TRITS_PER_TIER = 36;
63
+
64
+ /** Sub-blocks per tier */
65
+ export const SUB_BLOCKS_PER_TIER = 4;
66
+
67
+ /** Trits per sub-block */
68
+ export const TRITS_PER_SUB_BLOCK = 9;
69
+
70
+ /** Tier names (Himalayan naming convention) */
71
+ export const TierName = Object.freeze({
72
+ GALAXY: 0, // Broadest scope — intercontinental / satellite
73
+ CLUSTER: 1, // Regional mesh clusters
74
+ ZONE: 2, // Local zone (city-scale, maps to HexCoord)
75
+ NODE: 3, // Individual node identity
76
+ });
77
+
78
+ /** Tier name labels */
79
+ export const TIER_LABELS = Object.freeze(['galaxy', 'cluster', 'zone', 'node']);
80
+
81
+ // =============================================================================
82
+ // TRIT ADDRESS CLASS
83
+ // =============================================================================
84
+
85
+ /**
86
+ * A 144-trit hierarchical mesh address.
87
+ * Immutable value object with tier-aware operations.
88
+ */
89
+ export class TritAddress {
90
+ /** @type {Int8Array} — 144 balanced ternary digits */
91
+ #trits;
92
+
93
+ /**
94
+ * Create a TritAddress.
95
+ * @param {Int8Array | number[] | string} value — 144 trits or trit string
96
+ */
97
+ constructor(value) {
98
+ if (typeof value === 'string') {
99
+ this.#trits = TritAddress.#parseString(value);
100
+ } else if (value instanceof Int8Array) {
101
+ if (value.length !== TOTAL_TRITS) {
102
+ throw new Error(`TritAddress requires ${TOTAL_TRITS} trits, got ${value.length}`);
103
+ }
104
+ this.#trits = new Int8Array(value);
105
+ } else if (Array.isArray(value)) {
106
+ if (value.length !== TOTAL_TRITS) {
107
+ throw new Error(`TritAddress requires ${TOTAL_TRITS} trits, got ${value.length}`);
108
+ }
109
+ this.#trits = new Int8Array(value);
110
+ } else {
111
+ throw new Error('TritAddress requires Int8Array, number[], or string');
112
+ }
113
+
114
+ // Validate all values are balanced ternary
115
+ for (let i = 0; i < TOTAL_TRITS; i++) {
116
+ if (this.#trits[i] < -1 || this.#trits[i] > 1) {
117
+ throw new Error(`Invalid trit value at index ${i}: ${this.#trits[i]}`);
118
+ }
119
+ }
120
+
121
+ Object.freeze(this);
122
+ }
123
+
124
+ /**
125
+ * Get the full 144-trit array.
126
+ * @returns {Int8Array}
127
+ */
128
+ toTrits() {
129
+ return new Int8Array(this.#trits);
130
+ }
131
+
132
+ /**
133
+ * Get a specific tier's 36-trit segment.
134
+ * @param {number} tier — 0-3 (GALAXY, CLUSTER, ZONE, NODE)
135
+ * @returns {Int8Array} — 36 trits
136
+ */
137
+ getTier(tier) {
138
+ if (tier < 0 || tier >= TIER_COUNT) {
139
+ throw new Error(`Invalid tier: ${tier}. Must be 0-${TIER_COUNT - 1}`);
140
+ }
141
+ const start = tier * TRITS_PER_TIER;
142
+ return new Int8Array(this.#trits.slice(start, start + TRITS_PER_TIER));
143
+ }
144
+
145
+ /**
146
+ * Get a sub-block within a tier.
147
+ * @param {number} tier — 0-3
148
+ * @param {number} subBlock — 0-3 (region, sector, cell, local)
149
+ * @returns {Int8Array} — 9 trits
150
+ */
151
+ getSubBlock(tier, subBlock) {
152
+ if (subBlock < 0 || subBlock >= SUB_BLOCKS_PER_TIER) {
153
+ throw new Error(`Invalid sub-block: ${subBlock}`);
154
+ }
155
+ const start = tier * TRITS_PER_TIER + subBlock * TRITS_PER_SUB_BLOCK;
156
+ return new Int8Array(this.#trits.slice(start, start + TRITS_PER_SUB_BLOCK));
157
+ }
158
+
159
+ /**
160
+ * Get the tier name label.
161
+ * @param {number} tier
162
+ * @returns {string}
163
+ */
164
+ static tierLabel(tier) {
165
+ return TIER_LABELS[tier] || 'unknown';
166
+ }
167
+
168
+ /**
169
+ * Check if two addresses share a common prefix up to a given tier.
170
+ * Addresses in the same galaxy share tier 0.
171
+ * Addresses in the same cluster share tiers 0+1.
172
+ *
173
+ * @param {TritAddress} other
174
+ * @param {number} tier — check up to and including this tier (0-3)
175
+ * @returns {boolean}
176
+ */
177
+ sharesTier(other, tier) {
178
+ const endTrit = (tier + 1) * TRITS_PER_TIER;
179
+ for (let i = 0; i < endTrit; i++) {
180
+ if (this.#trits[i] !== other.#trits[i]) return false;
181
+ }
182
+ return true;
183
+ }
184
+
185
+ /**
186
+ * Compute the hierarchical distance between two addresses.
187
+ * Returns the tier at which the addresses diverge.
188
+ *
189
+ * Distance 0 = identical
190
+ * Distance 1 = different node, same zone
191
+ * Distance 2 = different zone, same cluster
192
+ * Distance 3 = different cluster, same galaxy
193
+ * Distance 4 = different galaxy
194
+ *
195
+ * @param {TritAddress} other
196
+ * @returns {number} — 0-4
197
+ */
198
+ tierDistance(other) {
199
+ for (let tier = 0; tier < TIER_COUNT; tier++) {
200
+ const start = tier * TRITS_PER_TIER;
201
+ const end = start + TRITS_PER_TIER;
202
+ for (let i = start; i < end; i++) {
203
+ if (this.#trits[i] !== other.#trits[i]) {
204
+ return TIER_COUNT - tier;
205
+ }
206
+ }
207
+ }
208
+ return 0; // Identical addresses
209
+ }
210
+
211
+ /**
212
+ * Compute the XOR-like ternary distance (trit-wise difference).
213
+ * For balanced ternary, "XOR" is: diff = (a - b + 3) % 3 mapped to {-1,0,+1}
214
+ * The Hamming-like distance counts non-zero differences.
215
+ *
216
+ * @param {TritAddress} other
217
+ * @returns {{ distance: number, maxDistance: number, similarity: number }}
218
+ */
219
+ tritDistance(other) {
220
+ let distance = 0;
221
+ for (let i = 0; i < TOTAL_TRITS; i++) {
222
+ if (this.#trits[i] !== other.#trits[i]) distance++;
223
+ }
224
+ return {
225
+ distance,
226
+ maxDistance: TOTAL_TRITS,
227
+ similarity: +((1 - distance / TOTAL_TRITS).toFixed(4)),
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Get the SST family balance for each tier.
233
+ * Useful for detecting address anomalies or locality properties.
234
+ *
235
+ * @returns {Array<{tier: string, a: number, b: number, c: number, balance: number}>}
236
+ */
237
+ tierFamilyBalance() {
238
+ const results = [];
239
+ for (let tier = 0; tier < TIER_COUNT; tier++) {
240
+ const tierTrits = this.getTier(tier);
241
+ let a = 0, b = 0, c = 0;
242
+ for (let i = 0; i < TRITS_PER_TIER; i++) {
243
+ // Map trit to SST family: -1 → A, 0 → C, +1 → B
244
+ if (tierTrits[i] === NEGATIVE) a++;
245
+ else if (tierTrits[i] === POSITIVE) b++;
246
+ else c++;
247
+ }
248
+ const expected = TRITS_PER_TIER / 3;
249
+ const balance = Math.sqrt(
250
+ Math.pow(a - expected, 2) +
251
+ Math.pow(b - expected, 2) +
252
+ Math.pow(c - expected, 2)
253
+ ) / expected;
254
+
255
+ results.push({
256
+ tier: TIER_LABELS[tier],
257
+ a, b, c,
258
+ balance: +balance.toFixed(4),
259
+ });
260
+ }
261
+ return results;
262
+ }
263
+
264
+ /**
265
+ * Compute a YPC-27 checksum for the full address.
266
+ * Uses the first 27 trits of each tier as a Poly27 input.
267
+ *
268
+ * @returns {Poly27} — 27-trit checksum
269
+ */
270
+ checksum() {
271
+ const hasher = new YPC27Checksum();
272
+ // Feed each tier's trits as bytes (trit→byte conversion)
273
+ for (let tier = 0; tier < TIER_COUNT; tier++) {
274
+ const tierTrits = this.getTier(tier);
275
+ // Pack trits as int8 bytes for checksum input
276
+ hasher.update(new Uint8Array(tierTrits.buffer));
277
+ }
278
+ return hasher.digest();
279
+ }
280
+
281
+ /**
282
+ * Convert to string representation.
283
+ * Format: Galaxy.Cluster.Zone.Node (each tier as trit string)
284
+ * Uses T for -1, 0 for 0, 1 for +1.
285
+ *
286
+ * @param {boolean} [compact=true] — use 9-char sub-block grouping
287
+ * @returns {string}
288
+ */
289
+ toString(compact = true) {
290
+ const tiers = [];
291
+ for (let tier = 0; tier < TIER_COUNT; tier++) {
292
+ const tierTrits = this.getTier(tier);
293
+ const chars = [];
294
+ for (let i = 0; i < TRITS_PER_TIER; i++) {
295
+ chars.push(tierTrits[i] === NEGATIVE ? 'T' : String(tierTrits[i]));
296
+ }
297
+ if (compact) {
298
+ // Group into sub-blocks separated by colons
299
+ const grouped = [];
300
+ for (let s = 0; s < SUB_BLOCKS_PER_TIER; s++) {
301
+ grouped.push(chars.slice(s * TRITS_PER_SUB_BLOCK, (s + 1) * TRITS_PER_SUB_BLOCK).join(''));
302
+ }
303
+ tiers.push(grouped.join(':'));
304
+ } else {
305
+ tiers.push(chars.join(''));
306
+ }
307
+ }
308
+ return tiers.join('.');
309
+ }
310
+
311
+ /**
312
+ * Check equality.
313
+ * @param {TritAddress} other
314
+ * @returns {boolean}
315
+ */
316
+ equals(other) {
317
+ for (let i = 0; i < TOTAL_TRITS; i++) {
318
+ if (this.#trits[i] !== other.#trits[i]) return false;
319
+ }
320
+ return true;
321
+ }
322
+
323
+ // ---------------------------------------------------------------------------
324
+ // Static constructors
325
+ // ---------------------------------------------------------------------------
326
+
327
+ /**
328
+ * Create a TritAddress from a hex peer ID (e.g., 64-char SHA3 hash).
329
+ * Converts hex bytes → trits (5 trits per byte), pads/truncates to 144.
330
+ *
331
+ * @param {string} hexId — hex-encoded peer ID
332
+ * @returns {TritAddress}
333
+ */
334
+ static fromHex(hexId) {
335
+ const bytes = [];
336
+ const cleanHex = hexId.replace(/^0x/, '');
337
+ for (let i = 0; i < cleanHex.length; i += 2) {
338
+ bytes.push(parseInt(cleanHex.substr(i, 2), 16) || 0);
339
+ }
340
+
341
+ const allTrits = bytesToTrits(new Uint8Array(bytes));
342
+
343
+ // Pad or truncate to exactly 144
344
+ const trits = new Int8Array(TOTAL_TRITS);
345
+ for (let i = 0; i < TOTAL_TRITS && i < allTrits.length; i++) {
346
+ trits[i] = allTrits[i];
347
+ }
348
+
349
+ return new TritAddress(trits);
350
+ }
351
+
352
+ /**
353
+ * Create a TritAddress from tier components.
354
+ * @param {Int8Array} galaxy — 36 trits
355
+ * @param {Int8Array} cluster — 36 trits
356
+ * @param {Int8Array} zone — 36 trits
357
+ * @param {Int8Array} node — 36 trits
358
+ * @returns {TritAddress}
359
+ */
360
+ static fromTiers(galaxy, cluster, zone, node) {
361
+ const trits = new Int8Array(TOTAL_TRITS);
362
+ trits.set(galaxy, 0);
363
+ trits.set(cluster, TRITS_PER_TIER);
364
+ trits.set(zone, TRITS_PER_TIER * 2);
365
+ trits.set(node, TRITS_PER_TIER * 3);
366
+ return new TritAddress(trits);
367
+ }
368
+
369
+ /**
370
+ * Create a zero address (all NEUTRAL trits).
371
+ * Used as the "origin" in the ternary address space.
372
+ * @returns {TritAddress}
373
+ */
374
+ static zero() {
375
+ return new TritAddress(new Int8Array(TOTAL_TRITS));
376
+ }
377
+
378
+ /**
379
+ * Create a random address (for testing).
380
+ * Each trit is randomly -1, 0, or +1.
381
+ * @returns {TritAddress}
382
+ */
383
+ static random() {
384
+ const trits = new Int8Array(TOTAL_TRITS);
385
+ for (let i = 0; i < TOTAL_TRITS; i++) {
386
+ trits[i] = Math.floor(Math.random() * 3) - 1;
387
+ }
388
+ return new TritAddress(trits);
389
+ }
390
+
391
+ /**
392
+ * Parse a trit string (T=−1, 0=0, 1=+1).
393
+ * Strips dots and colons used as separators.
394
+ * @param {string} s
395
+ * @returns {Int8Array}
396
+ */
397
+ static #parseString(s) {
398
+ const clean = s.replace(/[.:]/g, '');
399
+ if (clean.length !== TOTAL_TRITS) {
400
+ throw new Error(`TritAddress string must have ${TOTAL_TRITS} trit chars, got ${clean.length}`);
401
+ }
402
+
403
+ const trits = new Int8Array(TOTAL_TRITS);
404
+ for (let i = 0; i < TOTAL_TRITS; i++) {
405
+ const c = clean[i].toUpperCase();
406
+ if (c === 'T' || c === '-') trits[i] = NEGATIVE;
407
+ else if (c === '1' || c === '+') trits[i] = POSITIVE;
408
+ else trits[i] = NEUTRAL;
409
+ }
410
+ return trits;
411
+ }
412
+ }
413
+
414
+ // =============================================================================
415
+ // ROUTING TABLE — Hierarchical Ternary Routing
416
+ // =============================================================================
417
+
418
+ /**
419
+ * TernaryRoutingTable — XOR-metric routing for 144T addresses.
420
+ *
421
+ * Similar to Kademlia but using balanced ternary distance instead of
422
+ * binary XOR. Each "k-bucket" is replaced by a "t-bucket" that groups
423
+ * peers by tier distance (galaxy, cluster, zone, node).
424
+ *
425
+ * Routing priority:
426
+ * 1. Same zone (tier distance 1) — direct connection preferred
427
+ * 2. Same cluster (distance 2) — relay via zone peer
428
+ * 3. Same galaxy (distance 3) — relay via cluster peer
429
+ * 4. Different galaxy (distance 4) — relay via galaxy peer
430
+ */
431
+ export class TernaryRoutingTable {
432
+ /**
433
+ * @param {TritAddress} selfAddress — this node's 144T address
434
+ * @param {number} [bucketSize=6] — max peers per bucket (k=6 for hexagonal)
435
+ */
436
+ constructor(selfAddress, bucketSize = 6) {
437
+ this.selfAddress = selfAddress;
438
+ this.bucketSize = bucketSize;
439
+
440
+ // 4 buckets — one per tier distance (1=zone, 2=cluster, 3=galaxy, 4=remote)
441
+ /** @type {Map<string, {address: TritAddress, lastSeen: number, rtt: number}>[]} */
442
+ this._buckets = new Array(TIER_COUNT).fill(null).map(() => new Map());
443
+ }
444
+
445
+ /**
446
+ * Add a peer to the routing table.
447
+ * Placed in the bucket corresponding to tier distance from self.
448
+ *
449
+ * @param {string} peerId — unique peer identifier
450
+ * @param {TritAddress} peerAddress — peer's 144T address
451
+ * @param {number} [rtt=0] — round-trip time in ms
452
+ * @returns {boolean} — true if added (false if bucket full)
453
+ */
454
+ addPeer(peerId, peerAddress, rtt = 0) {
455
+ const distance = this.selfAddress.tierDistance(peerAddress);
456
+ if (distance === 0) return false; // Can't add self
457
+
458
+ const bucketIdx = distance - 1; // 1-4 → 0-3
459
+ const bucket = this._buckets[bucketIdx];
460
+
461
+ // Update if already present
462
+ if (bucket.has(peerId)) {
463
+ const entry = bucket.get(peerId);
464
+ entry.lastSeen = Date.now();
465
+ entry.rtt = rtt;
466
+ return true;
467
+ }
468
+
469
+ // Add if bucket has room
470
+ if (bucket.size < this.bucketSize) {
471
+ bucket.set(peerId, {
472
+ address: peerAddress,
473
+ lastSeen: Date.now(),
474
+ rtt,
475
+ });
476
+ return true;
477
+ }
478
+
479
+ // Bucket full — evict oldest if new peer is closer (lower RTT)
480
+ let oldestKey = null;
481
+ let oldestTime = Infinity;
482
+ for (const [key, entry] of bucket) {
483
+ if (entry.lastSeen < oldestTime) {
484
+ oldestTime = entry.lastSeen;
485
+ oldestKey = key;
486
+ }
487
+ }
488
+
489
+ if (oldestKey) {
490
+ bucket.delete(oldestKey);
491
+ bucket.set(peerId, {
492
+ address: peerAddress,
493
+ lastSeen: Date.now(),
494
+ rtt,
495
+ });
496
+ return true;
497
+ }
498
+
499
+ return false;
500
+ }
501
+
502
+ /**
503
+ * Remove a peer from the routing table.
504
+ * @param {string} peerId
505
+ * @returns {boolean}
506
+ */
507
+ removePeer(peerId) {
508
+ for (const bucket of this._buckets) {
509
+ if (bucket.delete(peerId)) return true;
510
+ }
511
+ return false;
512
+ }
513
+
514
+ /**
515
+ * Find the closest peers to a target address.
516
+ * Returns peers sorted by trit distance (ascending).
517
+ *
518
+ * @param {TritAddress} target — address to find peers near
519
+ * @param {number} [count=6] — max peers to return
520
+ * @returns {Array<{peerId: string, address: TritAddress, distance: number, rtt: number}>}
521
+ */
522
+ findClosest(target, count = 6) {
523
+ const candidates = [];
524
+
525
+ for (const bucket of this._buckets) {
526
+ for (const [peerId, entry] of bucket) {
527
+ const { distance } = entry.address.tritDistance(target);
528
+ candidates.push({
529
+ peerId,
530
+ address: entry.address,
531
+ distance,
532
+ rtt: entry.rtt,
533
+ });
534
+ }
535
+ }
536
+
537
+ // Sort by trit distance, then by RTT
538
+ candidates.sort((a, b) => a.distance - b.distance || a.rtt - b.rtt);
539
+
540
+ return candidates.slice(0, count);
541
+ }
542
+
543
+ /**
544
+ * Get the next hop toward a target address.
545
+ * Selects the peer with minimized tier distance to the target.
546
+ *
547
+ * @param {TritAddress} target
548
+ * @returns {{peerId: string, address: TritAddress, tierDistance: number, rtt: number} | null}
549
+ */
550
+ nextHop(target) {
551
+ const targetTierDist = this.selfAddress.tierDistance(target);
552
+
553
+ // Try to find a peer closer to the target than we are
554
+ let best = null;
555
+ let bestDist = targetTierDist;
556
+
557
+ for (const bucket of this._buckets) {
558
+ for (const [peerId, entry] of bucket) {
559
+ const dist = entry.address.tierDistance(target);
560
+ if (dist < bestDist || (dist === bestDist && (!best || entry.rtt < best.rtt))) {
561
+ bestDist = dist;
562
+ best = { peerId, address: entry.address, tierDistance: dist, rtt: entry.rtt };
563
+ }
564
+ }
565
+ }
566
+
567
+ return best;
568
+ }
569
+
570
+ /**
571
+ * Get routing table status.
572
+ * @returns {Object}
573
+ */
574
+ getStatus() {
575
+ const buckets = this._buckets.map((bucket, i) => ({
576
+ tier: TIER_LABELS[TIER_COUNT - 1 - i] || `distance-${i + 1}`,
577
+ peerCount: bucket.size,
578
+ capacity: this.bucketSize,
579
+ }));
580
+
581
+ const totalPeers = this._buckets.reduce((sum, b) => sum + b.size, 0);
582
+
583
+ return {
584
+ selfAddress: this.selfAddress.toString(),
585
+ buckets,
586
+ totalPeers,
587
+ capacity: this.bucketSize * TIER_COUNT,
588
+ };
589
+ }
590
+ }
591
+
592
+ // =============================================================================
593
+ // UTILITY FUNCTIONS
594
+ // =============================================================================
595
+
596
+ /**
597
+ * Convert a standard 64-char hex node ID into a 144T address with
598
+ * SST-based tier assignment.
599
+ *
600
+ * The hex ID is converted to trits, then the tiers are assigned
601
+ * using an SST Fibonacci cycle rotation to ensure good distribution
602
+ * across the address space.
603
+ *
604
+ * @param {string} hexId — 64-char hex peer ID
605
+ * @param {Object} [locality] — optional locality hints
606
+ * @param {number} [locality.galaxy=0] — galaxy index (0 for default)
607
+ * @param {number} [locality.cluster=0] — cluster index
608
+ * @returns {TritAddress}
609
+ */
610
+ export function hexIdToAddress(hexId, locality = {}) {
611
+ // Convert hex to raw trits
612
+ const bytes = [];
613
+ const clean = hexId.replace(/^0x/, '').slice(0, 64); // Max 32 bytes = 160 trits
614
+ for (let i = 0; i < clean.length; i += 2) {
615
+ bytes.push(parseInt(clean.substr(i, 2), 16) || 0);
616
+ }
617
+ const rawTrits = bytesToTrits(new Uint8Array(bytes));
618
+
619
+ const trits = new Int8Array(TOTAL_TRITS);
620
+
621
+ // Galaxy tier: use locality hint or derive from first bytes
622
+ if (locality.galaxy !== undefined) {
623
+ const galaxyTrits = new TritArray(locality.galaxy, TRITS_PER_TIER).toJSON();
624
+ for (let i = 0; i < TRITS_PER_TIER && i < galaxyTrits.length; i++) {
625
+ trits[i] = galaxyTrits[i];
626
+ }
627
+ } else {
628
+ // Derive galaxy from SST digital root of first 4 bytes
629
+ for (let i = 0; i < TRITS_PER_TIER && i < rawTrits.length; i++) {
630
+ trits[i] = rawTrits[i];
631
+ }
632
+ }
633
+
634
+ // Cluster tier: derive or use hint
635
+ if (locality.cluster !== undefined) {
636
+ const clusterTrits = new TritArray(locality.cluster, TRITS_PER_TIER).toJSON();
637
+ for (let i = 0; i < TRITS_PER_TIER && i < clusterTrits.length; i++) {
638
+ trits[TRITS_PER_TIER + i] = clusterTrits[i];
639
+ }
640
+ } else {
641
+ for (let i = 0; i < TRITS_PER_TIER; i++) {
642
+ const srcIdx = TRITS_PER_TIER + i;
643
+ trits[TRITS_PER_TIER + i] = srcIdx < rawTrits.length ? rawTrits[srcIdx] : 0;
644
+ }
645
+ }
646
+
647
+ // Zone tier: always derived from hex ID
648
+ for (let i = 0; i < TRITS_PER_TIER; i++) {
649
+ const srcIdx = TRITS_PER_TIER * 2 + i;
650
+ trits[TRITS_PER_TIER * 2 + i] = srcIdx < rawTrits.length ? rawTrits[srcIdx] : 0;
651
+ }
652
+
653
+ // Node tier: always derived from hex ID (unique portion)
654
+ for (let i = 0; i < TRITS_PER_TIER; i++) {
655
+ const srcIdx = TRITS_PER_TIER * 3 + i;
656
+ trits[TRITS_PER_TIER * 3 + i] = srcIdx < rawTrits.length ? rawTrits[srcIdx] : 0;
657
+ }
658
+
659
+ return new TritAddress(trits);
660
+ }
661
+
662
+ /**
663
+ * Compute the 216-hypercycle position for an address.
664
+ * The hypercycle is LCM(27, 24) = 216 — the full SST-YPC-27 alignment.
665
+ *
666
+ * @param {TritAddress} address
667
+ * @returns {number} — position 0-215
668
+ */
669
+ export function hypercyclePosition(address) {
670
+ const trits = address.toTrits();
671
+ // Sum all trit absolute values, then mod 216
672
+ let sum = 0;
673
+ for (let i = 0; i < TOTAL_TRITS; i++) {
674
+ sum += Math.abs(trits[i]) * (i + 1);
675
+ }
676
+ return sum % 216;
677
+ }
678
+
679
+ /**
680
+ * Verify the integrity of a 144T address using YPC-27.
681
+ * Computes a checksum of all 4 tiers and compares.
682
+ *
683
+ * @param {TritAddress} address
684
+ * @param {Poly27} expectedChecksum
685
+ * @returns {boolean}
686
+ */
687
+ export function verifyAddressIntegrity(address, expectedChecksum) {
688
+ const computed = address.checksum();
689
+ return computed.equals(expectedChecksum);
690
+ }
691
+
692
+ // =============================================================================
693
+ // EXPORTS
694
+ // =============================================================================
695
+
696
+ export default {
697
+ // Constants
698
+ TOTAL_TRITS,
699
+ TIER_COUNT,
700
+ TRITS_PER_TIER,
701
+ SUB_BLOCKS_PER_TIER,
702
+ TRITS_PER_SUB_BLOCK,
703
+ TierName,
704
+ TIER_LABELS,
705
+
706
+ // Classes
707
+ TritAddress,
708
+ TernaryRoutingTable,
709
+
710
+ // Utilities
711
+ hexIdToAddress,
712
+ hypercyclePosition,
713
+ verifyAddressIntegrity,
714
+ };