@optimystic/db-core 0.13.4 → 0.14.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 (447) hide show
  1. package/dist/src/cluster/structs.d.ts +29 -0
  2. package/dist/src/cluster/structs.d.ts.map +1 -1
  3. package/dist/src/cohort-topic/addressing.d.ts +53 -0
  4. package/dist/src/cohort-topic/addressing.d.ts.map +1 -0
  5. package/dist/src/cohort-topic/addressing.js +95 -0
  6. package/dist/src/cohort-topic/addressing.js.map +1 -0
  7. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts +105 -0
  8. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.d.ts.map +1 -0
  9. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js +197 -0
  10. package/dist/src/cohort-topic/antidos/bootstrap-evidence-envelope.js.map +1 -0
  11. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts +50 -0
  12. package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts.map +1 -0
  13. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js +62 -0
  14. package/dist/src/cohort-topic/antidos/bootstrap-evidence.js.map +1 -0
  15. package/dist/src/cohort-topic/antidos/index.d.ts +6 -0
  16. package/dist/src/cohort-topic/antidos/index.d.ts.map +1 -0
  17. package/dist/src/cohort-topic/antidos/index.js +6 -0
  18. package/dist/src/cohort-topic/antidos/index.js.map +1 -0
  19. package/dist/src/cohort-topic/antidos/rate-limiter.d.ts +95 -0
  20. package/dist/src/cohort-topic/antidos/rate-limiter.d.ts.map +1 -0
  21. package/dist/src/cohort-topic/antidos/rate-limiter.js +146 -0
  22. package/dist/src/cohort-topic/antidos/rate-limiter.js.map +1 -0
  23. package/dist/src/cohort-topic/antidos/replay-guard.d.ts +37 -0
  24. package/dist/src/cohort-topic/antidos/replay-guard.d.ts.map +1 -0
  25. package/dist/src/cohort-topic/antidos/replay-guard.js +78 -0
  26. package/dist/src/cohort-topic/antidos/replay-guard.js.map +1 -0
  27. package/dist/src/cohort-topic/antidos/topic-budget.d.ts +50 -0
  28. package/dist/src/cohort-topic/antidos/topic-budget.d.ts.map +1 -0
  29. package/dist/src/cohort-topic/antidos/topic-budget.js +95 -0
  30. package/dist/src/cohort-topic/antidos/topic-budget.js.map +1 -0
  31. package/dist/src/cohort-topic/antiflood/index.d.ts +3 -0
  32. package/dist/src/cohort-topic/antiflood/index.d.ts.map +1 -0
  33. package/dist/src/cohort-topic/antiflood/index.js +3 -0
  34. package/dist/src/cohort-topic/antiflood/index.js.map +1 -0
  35. package/dist/src/cohort-topic/antiflood/invariants.d.ts +70 -0
  36. package/dist/src/cohort-topic/antiflood/invariants.d.ts.map +1 -0
  37. package/dist/src/cohort-topic/antiflood/invariants.js +86 -0
  38. package/dist/src/cohort-topic/antiflood/invariants.js.map +1 -0
  39. package/dist/src/cohort-topic/antiflood/jitter.d.ts +65 -0
  40. package/dist/src/cohort-topic/antiflood/jitter.d.ts.map +1 -0
  41. package/dist/src/cohort-topic/antiflood/jitter.js +75 -0
  42. package/dist/src/cohort-topic/antiflood/jitter.js.map +1 -0
  43. package/dist/src/cohort-topic/coldstart.d.ts +117 -0
  44. package/dist/src/cohort-topic/coldstart.d.ts.map +1 -0
  45. package/dist/src/cohort-topic/coldstart.js +127 -0
  46. package/dist/src/cohort-topic/coldstart.js.map +1 -0
  47. package/dist/src/cohort-topic/dmax.d.ts +44 -0
  48. package/dist/src/cohort-topic/dmax.d.ts.map +1 -0
  49. package/dist/src/cohort-topic/dmax.js +70 -0
  50. package/dist/src/cohort-topic/dmax.js.map +1 -0
  51. package/dist/src/cohort-topic/gossip/bus.d.ts +85 -0
  52. package/dist/src/cohort-topic/gossip/bus.d.ts.map +1 -0
  53. package/dist/src/cohort-topic/gossip/bus.js +146 -0
  54. package/dist/src/cohort-topic/gossip/bus.js.map +1 -0
  55. package/dist/src/cohort-topic/gossip/index.d.ts +4 -0
  56. package/dist/src/cohort-topic/gossip/index.d.ts.map +1 -0
  57. package/dist/src/cohort-topic/gossip/index.js +4 -0
  58. package/dist/src/cohort-topic/gossip/index.js.map +1 -0
  59. package/dist/src/cohort-topic/gossip/records.d.ts +12 -0
  60. package/dist/src/cohort-topic/gossip/records.d.ts.map +1 -0
  61. package/dist/src/cohort-topic/gossip/records.js +41 -0
  62. package/dist/src/cohort-topic/gossip/records.js.map +1 -0
  63. package/dist/src/cohort-topic/gossip/view.d.ts +39 -0
  64. package/dist/src/cohort-topic/gossip/view.d.ts.map +1 -0
  65. package/dist/src/cohort-topic/gossip/view.js +30 -0
  66. package/dist/src/cohort-topic/gossip/view.js.map +1 -0
  67. package/dist/src/cohort-topic/index.d.ts +21 -0
  68. package/dist/src/cohort-topic/index.d.ts.map +1 -0
  69. package/dist/src/cohort-topic/index.js +21 -0
  70. package/dist/src/cohort-topic/index.js.map +1 -0
  71. package/dist/src/cohort-topic/load/barometer.d.ts +78 -0
  72. package/dist/src/cohort-topic/load/barometer.d.ts.map +1 -0
  73. package/dist/src/cohort-topic/load/barometer.js +93 -0
  74. package/dist/src/cohort-topic/load/barometer.js.map +1 -0
  75. package/dist/src/cohort-topic/load/index.d.ts +2 -0
  76. package/dist/src/cohort-topic/load/index.d.ts.map +1 -0
  77. package/dist/src/cohort-topic/load/index.js +2 -0
  78. package/dist/src/cohort-topic/load/index.js.map +1 -0
  79. package/dist/src/cohort-topic/member-engine.d.ts +134 -0
  80. package/dist/src/cohort-topic/member-engine.d.ts.map +1 -0
  81. package/dist/src/cohort-topic/member-engine.js +264 -0
  82. package/dist/src/cohort-topic/member-engine.js.map +1 -0
  83. package/dist/src/cohort-topic/membership/index.d.ts +4 -0
  84. package/dist/src/cohort-topic/membership/index.d.ts.map +1 -0
  85. package/dist/src/cohort-topic/membership/index.js +4 -0
  86. package/dist/src/cohort-topic/membership/index.js.map +1 -0
  87. package/dist/src/cohort-topic/membership/publisher.d.ts +82 -0
  88. package/dist/src/cohort-topic/membership/publisher.d.ts.map +1 -0
  89. package/dist/src/cohort-topic/membership/publisher.js +107 -0
  90. package/dist/src/cohort-topic/membership/publisher.js.map +1 -0
  91. package/dist/src/cohort-topic/membership/source.d.ts +31 -0
  92. package/dist/src/cohort-topic/membership/source.d.ts.map +1 -0
  93. package/dist/src/cohort-topic/membership/source.js +32 -0
  94. package/dist/src/cohort-topic/membership/source.js.map +1 -0
  95. package/dist/src/cohort-topic/membership/verifier.d.ts +96 -0
  96. package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -0
  97. package/dist/src/cohort-topic/membership/verifier.js +250 -0
  98. package/dist/src/cohort-topic/membership/verifier.js.map +1 -0
  99. package/dist/src/cohort-topic/ports.d.ts +149 -0
  100. package/dist/src/cohort-topic/ports.d.ts.map +1 -0
  101. package/dist/src/cohort-topic/ports.js +25 -0
  102. package/dist/src/cohort-topic/ports.js.map +1 -0
  103. package/dist/src/cohort-topic/promotion.d.ts +135 -0
  104. package/dist/src/cohort-topic/promotion.d.ts.map +1 -0
  105. package/dist/src/cohort-topic/promotion.js +269 -0
  106. package/dist/src/cohort-topic/promotion.js.map +1 -0
  107. package/dist/src/cohort-topic/registration/bytes.d.ts +16 -0
  108. package/dist/src/cohort-topic/registration/bytes.d.ts.map +1 -0
  109. package/dist/src/cohort-topic/registration/bytes.js +32 -0
  110. package/dist/src/cohort-topic/registration/bytes.js.map +1 -0
  111. package/dist/src/cohort-topic/registration/handoff.d.ts +69 -0
  112. package/dist/src/cohort-topic/registration/handoff.d.ts.map +1 -0
  113. package/dist/src/cohort-topic/registration/handoff.js +98 -0
  114. package/dist/src/cohort-topic/registration/handoff.js.map +1 -0
  115. package/dist/src/cohort-topic/registration/index.d.ts +7 -0
  116. package/dist/src/cohort-topic/registration/index.d.ts.map +1 -0
  117. package/dist/src/cohort-topic/registration/index.js +7 -0
  118. package/dist/src/cohort-topic/registration/index.js.map +1 -0
  119. package/dist/src/cohort-topic/registration/renewal.d.ts +115 -0
  120. package/dist/src/cohort-topic/registration/renewal.d.ts.map +1 -0
  121. package/dist/src/cohort-topic/registration/renewal.js +306 -0
  122. package/dist/src/cohort-topic/registration/renewal.js.map +1 -0
  123. package/dist/src/cohort-topic/registration/sharding.d.ts +31 -0
  124. package/dist/src/cohort-topic/registration/sharding.d.ts.map +1 -0
  125. package/dist/src/cohort-topic/registration/sharding.js +48 -0
  126. package/dist/src/cohort-topic/registration/sharding.js.map +1 -0
  127. package/dist/src/cohort-topic/registration/store.d.ts +13 -0
  128. package/dist/src/cohort-topic/registration/store.d.ts.map +1 -0
  129. package/dist/src/cohort-topic/registration/store.js +72 -0
  130. package/dist/src/cohort-topic/registration/store.js.map +1 -0
  131. package/dist/src/cohort-topic/registration/types.d.ts +71 -0
  132. package/dist/src/cohort-topic/registration/types.d.ts.map +1 -0
  133. package/dist/src/cohort-topic/registration/types.js +27 -0
  134. package/dist/src/cohort-topic/registration/types.js.map +1 -0
  135. package/dist/src/cohort-topic/ring-hash.d.ts +27 -0
  136. package/dist/src/cohort-topic/ring-hash.d.ts.map +1 -0
  137. package/dist/src/cohort-topic/ring-hash.js +45 -0
  138. package/dist/src/cohort-topic/ring-hash.js.map +1 -0
  139. package/dist/src/cohort-topic/service.d.ts +157 -0
  140. package/dist/src/cohort-topic/service.d.ts.map +1 -0
  141. package/dist/src/cohort-topic/service.js +237 -0
  142. package/dist/src/cohort-topic/service.js.map +1 -0
  143. package/dist/src/cohort-topic/sig/index.d.ts +3 -0
  144. package/dist/src/cohort-topic/sig/index.d.ts.map +1 -0
  145. package/dist/src/cohort-topic/sig/index.js +3 -0
  146. package/dist/src/cohort-topic/sig/index.js.map +1 -0
  147. package/dist/src/cohort-topic/sig/payloads.d.ts +25 -0
  148. package/dist/src/cohort-topic/sig/payloads.d.ts.map +1 -0
  149. package/dist/src/cohort-topic/sig/payloads.js +25 -0
  150. package/dist/src/cohort-topic/sig/payloads.js.map +1 -0
  151. package/dist/src/cohort-topic/sig/threshold.d.ts +33 -0
  152. package/dist/src/cohort-topic/sig/threshold.d.ts.map +1 -0
  153. package/dist/src/cohort-topic/sig/threshold.js +51 -0
  154. package/dist/src/cohort-topic/sig/threshold.js.map +1 -0
  155. package/dist/src/cohort-topic/tiers.d.ts +58 -0
  156. package/dist/src/cohort-topic/tiers.d.ts.map +1 -0
  157. package/dist/src/cohort-topic/tiers.js +49 -0
  158. package/dist/src/cohort-topic/tiers.js.map +1 -0
  159. package/dist/src/cohort-topic/traffic.d.ts +90 -0
  160. package/dist/src/cohort-topic/traffic.d.ts.map +1 -0
  161. package/dist/src/cohort-topic/traffic.js +146 -0
  162. package/dist/src/cohort-topic/traffic.js.map +1 -0
  163. package/dist/src/cohort-topic/walk.d.ts +123 -0
  164. package/dist/src/cohort-topic/walk.d.ts.map +1 -0
  165. package/dist/src/cohort-topic/walk.js +150 -0
  166. package/dist/src/cohort-topic/walk.js.map +1 -0
  167. package/dist/src/cohort-topic/willingness.d.ts +123 -0
  168. package/dist/src/cohort-topic/willingness.d.ts.map +1 -0
  169. package/dist/src/cohort-topic/willingness.js +143 -0
  170. package/dist/src/cohort-topic/willingness.js.map +1 -0
  171. package/dist/src/cohort-topic/wire/codec.d.ts +42 -0
  172. package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -0
  173. package/dist/src/cohort-topic/wire/codec.js +163 -0
  174. package/dist/src/cohort-topic/wire/codec.js.map +1 -0
  175. package/dist/src/cohort-topic/wire/index.d.ts +6 -0
  176. package/dist/src/cohort-topic/wire/index.d.ts.map +1 -0
  177. package/dist/src/cohort-topic/wire/index.js +6 -0
  178. package/dist/src/cohort-topic/wire/index.js.map +1 -0
  179. package/dist/src/cohort-topic/wire/payloads.d.ts +38 -0
  180. package/dist/src/cohort-topic/wire/payloads.d.ts.map +1 -0
  181. package/dist/src/cohort-topic/wire/payloads.js +102 -0
  182. package/dist/src/cohort-topic/wire/payloads.js.map +1 -0
  183. package/dist/src/cohort-topic/wire/types.d.ts +304 -0
  184. package/dist/src/cohort-topic/wire/types.d.ts.map +1 -0
  185. package/dist/src/cohort-topic/wire/types.js +16 -0
  186. package/dist/src/cohort-topic/wire/types.js.map +1 -0
  187. package/dist/src/cohort-topic/wire/validate.d.ts +25 -0
  188. package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -0
  189. package/dist/src/cohort-topic/wire/validate.js +427 -0
  190. package/dist/src/cohort-topic/wire/validate.js.map +1 -0
  191. package/dist/src/collection/collection.d.ts +58 -1
  192. package/dist/src/collection/collection.d.ts.map +1 -1
  193. package/dist/src/collection/collection.js +76 -0
  194. package/dist/src/collection/collection.js.map +1 -1
  195. package/dist/src/collections/tree/tree.d.ts +58 -2
  196. package/dist/src/collections/tree/tree.d.ts.map +1 -1
  197. package/dist/src/collections/tree/tree.js +59 -2
  198. package/dist/src/collections/tree/tree.js.map +1 -1
  199. package/dist/src/index.d.ts +3 -0
  200. package/dist/src/index.d.ts.map +1 -1
  201. package/dist/src/index.js +3 -0
  202. package/dist/src/index.js.map +1 -1
  203. package/dist/src/log/log.d.ts +42 -4
  204. package/dist/src/log/log.d.ts.map +1 -1
  205. package/dist/src/log/log.js +71 -3
  206. package/dist/src/log/log.js.map +1 -1
  207. package/dist/src/log/struct.d.ts +122 -2
  208. package/dist/src/log/struct.d.ts.map +1 -1
  209. package/dist/src/log/struct.js.map +1 -1
  210. package/dist/src/matchmaking/capability-filter.d.ts +24 -0
  211. package/dist/src/matchmaking/capability-filter.d.ts.map +1 -0
  212. package/dist/src/matchmaking/capability-filter.js +37 -0
  213. package/dist/src/matchmaking/capability-filter.js.map +1 -0
  214. package/dist/src/matchmaking/config.d.ts +77 -0
  215. package/dist/src/matchmaking/config.d.ts.map +1 -0
  216. package/dist/src/matchmaking/config.js +69 -0
  217. package/dist/src/matchmaking/config.js.map +1 -0
  218. package/dist/src/matchmaking/index.d.ts +21 -0
  219. package/dist/src/matchmaking/index.d.ts.map +1 -0
  220. package/dist/src/matchmaking/index.js +21 -0
  221. package/dist/src/matchmaking/index.js.map +1 -0
  222. package/dist/src/matchmaking/multi-cohort-seeker.d.ts +133 -0
  223. package/dist/src/matchmaking/multi-cohort-seeker.d.ts.map +1 -0
  224. package/dist/src/matchmaking/multi-cohort-seeker.js +142 -0
  225. package/dist/src/matchmaking/multi-cohort-seeker.js.map +1 -0
  226. package/dist/src/matchmaking/provider.d.ts +70 -0
  227. package/dist/src/matchmaking/provider.d.ts.map +1 -0
  228. package/dist/src/matchmaking/provider.js +93 -0
  229. package/dist/src/matchmaking/provider.js.map +1 -0
  230. package/dist/src/matchmaking/query-eval.d.ts +53 -0
  231. package/dist/src/matchmaking/query-eval.d.ts.map +1 -0
  232. package/dist/src/matchmaking/query-eval.js +69 -0
  233. package/dist/src/matchmaking/query-eval.js.map +1 -0
  234. package/dist/src/matchmaking/seeker-walk.d.ts +95 -0
  235. package/dist/src/matchmaking/seeker-walk.d.ts.map +1 -0
  236. package/dist/src/matchmaking/seeker-walk.js +75 -0
  237. package/dist/src/matchmaking/seeker-walk.js.map +1 -0
  238. package/dist/src/matchmaking/seeker.d.ts +51 -0
  239. package/dist/src/matchmaking/seeker.d.ts.map +1 -0
  240. package/dist/src/matchmaking/seeker.js +62 -0
  241. package/dist/src/matchmaking/seeker.js.map +1 -0
  242. package/dist/src/matchmaking/topic-anchor.d.ts +54 -0
  243. package/dist/src/matchmaking/topic-anchor.d.ts.map +1 -0
  244. package/dist/src/matchmaking/topic-anchor.js +67 -0
  245. package/dist/src/matchmaking/topic-anchor.js.map +1 -0
  246. package/dist/src/matchmaking/voting-quorum.d.ts +233 -0
  247. package/dist/src/matchmaking/voting-quorum.d.ts.map +1 -0
  248. package/dist/src/matchmaking/voting-quorum.js +238 -0
  249. package/dist/src/matchmaking/voting-quorum.js.map +1 -0
  250. package/dist/src/matchmaking/wire.d.ts +259 -0
  251. package/dist/src/matchmaking/wire.d.ts.map +1 -0
  252. package/dist/src/matchmaking/wire.js +484 -0
  253. package/dist/src/matchmaking/wire.js.map +1 -0
  254. package/dist/src/network/i-repo.d.ts +19 -0
  255. package/dist/src/network/i-repo.d.ts.map +1 -1
  256. package/dist/src/network/repo-protocol.d.ts +3 -1
  257. package/dist/src/network/repo-protocol.d.ts.map +1 -1
  258. package/dist/src/network/struct.d.ts +23 -1
  259. package/dist/src/network/struct.d.ts.map +1 -1
  260. package/dist/src/reactivity/backfill.d.ts +118 -0
  261. package/dist/src/reactivity/backfill.d.ts.map +1 -0
  262. package/dist/src/reactivity/backfill.js +137 -0
  263. package/dist/src/reactivity/backfill.js.map +1 -0
  264. package/dist/src/reactivity/backpressure.d.ts +99 -0
  265. package/dist/src/reactivity/backpressure.d.ts.map +1 -0
  266. package/dist/src/reactivity/backpressure.js +153 -0
  267. package/dist/src/reactivity/backpressure.js.map +1 -0
  268. package/dist/src/reactivity/checkpoint.d.ts +165 -0
  269. package/dist/src/reactivity/checkpoint.d.ts.map +1 -0
  270. package/dist/src/reactivity/checkpoint.js +232 -0
  271. package/dist/src/reactivity/checkpoint.js.map +1 -0
  272. package/dist/src/reactivity/config.d.ts +131 -0
  273. package/dist/src/reactivity/config.d.ts.map +1 -0
  274. package/dist/src/reactivity/config.js +135 -0
  275. package/dist/src/reactivity/config.js.map +1 -0
  276. package/dist/src/reactivity/dedupe.d.ts +51 -0
  277. package/dist/src/reactivity/dedupe.d.ts.map +1 -0
  278. package/dist/src/reactivity/dedupe.js +90 -0
  279. package/dist/src/reactivity/dedupe.js.map +1 -0
  280. package/dist/src/reactivity/forwarder.d.ts +50 -0
  281. package/dist/src/reactivity/forwarder.d.ts.map +1 -0
  282. package/dist/src/reactivity/forwarder.js +52 -0
  283. package/dist/src/reactivity/forwarder.js.map +1 -0
  284. package/dist/src/reactivity/index.d.ts +34 -0
  285. package/dist/src/reactivity/index.d.ts.map +1 -0
  286. package/dist/src/reactivity/index.js +34 -0
  287. package/dist/src/reactivity/index.js.map +1 -0
  288. package/dist/src/reactivity/notification.d.ts +72 -0
  289. package/dist/src/reactivity/notification.d.ts.map +1 -0
  290. package/dist/src/reactivity/notification.js +92 -0
  291. package/dist/src/reactivity/notification.js.map +1 -0
  292. package/dist/src/reactivity/policy.d.ts +59 -0
  293. package/dist/src/reactivity/policy.d.ts.map +1 -0
  294. package/dist/src/reactivity/policy.js +64 -0
  295. package/dist/src/reactivity/policy.js.map +1 -0
  296. package/dist/src/reactivity/push-state.d.ts +140 -0
  297. package/dist/src/reactivity/push-state.d.ts.map +1 -0
  298. package/dist/src/reactivity/push-state.js +270 -0
  299. package/dist/src/reactivity/push-state.js.map +1 -0
  300. package/dist/src/reactivity/recover.d.ts +75 -0
  301. package/dist/src/reactivity/recover.d.ts.map +1 -0
  302. package/dist/src/reactivity/recover.js +105 -0
  303. package/dist/src/reactivity/recover.js.map +1 -0
  304. package/dist/src/reactivity/replay-buffer.d.ts +57 -0
  305. package/dist/src/reactivity/replay-buffer.d.ts.map +1 -0
  306. package/dist/src/reactivity/replay-buffer.js +88 -0
  307. package/dist/src/reactivity/replay-buffer.js.map +1 -0
  308. package/dist/src/reactivity/resume.d.ts +214 -0
  309. package/dist/src/reactivity/resume.d.ts.map +1 -0
  310. package/dist/src/reactivity/resume.js +358 -0
  311. package/dist/src/reactivity/resume.js.map +1 -0
  312. package/dist/src/reactivity/rotation.d.ts +283 -0
  313. package/dist/src/reactivity/rotation.d.ts.map +1 -0
  314. package/dist/src/reactivity/rotation.js +265 -0
  315. package/dist/src/reactivity/rotation.js.map +1 -0
  316. package/dist/src/reactivity/subscriber.d.ts +66 -0
  317. package/dist/src/reactivity/subscriber.d.ts.map +1 -0
  318. package/dist/src/reactivity/subscriber.js +75 -0
  319. package/dist/src/reactivity/subscriber.js.map +1 -0
  320. package/dist/src/reactivity/subscription.d.ts +52 -0
  321. package/dist/src/reactivity/subscription.d.ts.map +1 -0
  322. package/dist/src/reactivity/subscription.js +28 -0
  323. package/dist/src/reactivity/subscription.js.map +1 -0
  324. package/dist/src/reactivity/topic-anchor.d.ts +46 -0
  325. package/dist/src/reactivity/topic-anchor.d.ts.map +1 -0
  326. package/dist/src/reactivity/topic-anchor.js +61 -0
  327. package/dist/src/reactivity/topic-anchor.js.map +1 -0
  328. package/dist/src/reactivity/verify.d.ts +41 -0
  329. package/dist/src/reactivity/verify.d.ts.map +1 -0
  330. package/dist/src/reactivity/verify.js +48 -0
  331. package/dist/src/reactivity/verify.js.map +1 -0
  332. package/dist/src/reactivity/wire-validate.d.ts +28 -0
  333. package/dist/src/reactivity/wire-validate.d.ts.map +1 -0
  334. package/dist/src/reactivity/wire-validate.js +79 -0
  335. package/dist/src/reactivity/wire-validate.js.map +1 -0
  336. package/dist/src/reactivity/wire.d.ts +102 -0
  337. package/dist/src/reactivity/wire.d.ts.map +1 -0
  338. package/dist/src/reactivity/wire.js +129 -0
  339. package/dist/src/reactivity/wire.js.map +1 -0
  340. package/dist/src/transaction/coordinator.d.ts +9 -5
  341. package/dist/src/transaction/coordinator.d.ts.map +1 -1
  342. package/dist/src/transaction/coordinator.js +48 -25
  343. package/dist/src/transaction/coordinator.js.map +1 -1
  344. package/dist/src/transactor/change-notifier.d.ts +74 -0
  345. package/dist/src/transactor/change-notifier.d.ts.map +1 -0
  346. package/dist/src/transactor/change-notifier.js +4 -0
  347. package/dist/src/transactor/change-notifier.js.map +1 -0
  348. package/dist/src/transactor/index.d.ts +1 -0
  349. package/dist/src/transactor/index.d.ts.map +1 -1
  350. package/dist/src/transactor/index.js +1 -0
  351. package/dist/src/transactor/index.js.map +1 -1
  352. package/dist/src/transactor/network-transactor.d.ts +44 -2
  353. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  354. package/dist/src/transactor/network-transactor.js +132 -17
  355. package/dist/src/transactor/network-transactor.js.map +1 -1
  356. package/package.json +3 -2
  357. package/src/cluster/structs.ts +86 -57
  358. package/src/cohort-topic/addressing.ts +112 -0
  359. package/src/cohort-topic/antidos/bootstrap-evidence-envelope.ts +253 -0
  360. package/src/cohort-topic/antidos/bootstrap-evidence.ts +94 -0
  361. package/src/cohort-topic/antidos/index.ts +5 -0
  362. package/src/cohort-topic/antidos/rate-limiter.ts +210 -0
  363. package/src/cohort-topic/antidos/replay-guard.ts +109 -0
  364. package/src/cohort-topic/antidos/topic-budget.ts +145 -0
  365. package/src/cohort-topic/antiflood/index.ts +2 -0
  366. package/src/cohort-topic/antiflood/invariants.ts +108 -0
  367. package/src/cohort-topic/antiflood/jitter.ts +117 -0
  368. package/src/cohort-topic/coldstart.ts +204 -0
  369. package/src/cohort-topic/dmax.ts +88 -0
  370. package/src/cohort-topic/gossip/bus.ts +217 -0
  371. package/src/cohort-topic/gossip/index.ts +3 -0
  372. package/src/cohort-topic/gossip/records.ts +45 -0
  373. package/src/cohort-topic/gossip/view.ts +66 -0
  374. package/src/cohort-topic/index.ts +20 -0
  375. package/src/cohort-topic/load/barometer.ts +134 -0
  376. package/src/cohort-topic/load/index.ts +1 -0
  377. package/src/cohort-topic/member-engine.ts +409 -0
  378. package/src/cohort-topic/membership/index.ts +3 -0
  379. package/src/cohort-topic/membership/publisher.ts +171 -0
  380. package/src/cohort-topic/membership/source.ts +41 -0
  381. package/src/cohort-topic/membership/verifier.ts +345 -0
  382. package/src/cohort-topic/ports.ts +157 -0
  383. package/src/cohort-topic/promotion.ts +384 -0
  384. package/src/cohort-topic/registration/bytes.ts +37 -0
  385. package/src/cohort-topic/registration/handoff.ts +154 -0
  386. package/src/cohort-topic/registration/index.ts +6 -0
  387. package/src/cohort-topic/registration/renewal.ts +423 -0
  388. package/src/cohort-topic/registration/sharding.ts +61 -0
  389. package/src/cohort-topic/registration/store.ts +81 -0
  390. package/src/cohort-topic/registration/types.ts +76 -0
  391. package/src/cohort-topic/ring-hash.ts +50 -0
  392. package/src/cohort-topic/service.ts +390 -0
  393. package/src/cohort-topic/sig/index.ts +2 -0
  394. package/src/cohort-topic/sig/payloads.ts +39 -0
  395. package/src/cohort-topic/sig/threshold.ts +64 -0
  396. package/src/cohort-topic/tiers.ts +74 -0
  397. package/src/cohort-topic/traffic.ts +211 -0
  398. package/src/cohort-topic/walk.ts +255 -0
  399. package/src/cohort-topic/willingness.ts +237 -0
  400. package/src/cohort-topic/wire/codec.ts +204 -0
  401. package/src/cohort-topic/wire/index.ts +16 -0
  402. package/src/cohort-topic/wire/payloads.ts +117 -0
  403. package/src/cohort-topic/wire/types.ts +343 -0
  404. package/src/cohort-topic/wire/validate.ts +478 -0
  405. package/src/collection/collection.ts +93 -1
  406. package/src/collections/tree/tree.ts +84 -3
  407. package/src/index.ts +3 -0
  408. package/src/log/log.ts +86 -7
  409. package/src/log/struct.ts +126 -2
  410. package/src/matchmaking/capability-filter.ts +45 -0
  411. package/src/matchmaking/config.ts +98 -0
  412. package/src/matchmaking/index.ts +21 -0
  413. package/src/matchmaking/multi-cohort-seeker.ts +234 -0
  414. package/src/matchmaking/provider.ts +123 -0
  415. package/src/matchmaking/query-eval.ts +105 -0
  416. package/src/matchmaking/seeker-walk.ts +127 -0
  417. package/src/matchmaking/seeker.ts +86 -0
  418. package/src/matchmaking/topic-anchor.ts +90 -0
  419. package/src/matchmaking/voting-quorum.ts +394 -0
  420. package/src/matchmaking/wire.ts +680 -0
  421. package/src/network/i-repo.ts +19 -0
  422. package/src/network/repo-protocol.ts +3 -2
  423. package/src/network/struct.ts +24 -1
  424. package/src/reactivity/backfill.ts +220 -0
  425. package/src/reactivity/backpressure.ts +191 -0
  426. package/src/reactivity/checkpoint.ts +308 -0
  427. package/src/reactivity/config.ts +172 -0
  428. package/src/reactivity/dedupe.ts +132 -0
  429. package/src/reactivity/forwarder.ts +87 -0
  430. package/src/reactivity/index.ts +34 -0
  431. package/src/reactivity/notification.ts +123 -0
  432. package/src/reactivity/policy.ts +79 -0
  433. package/src/reactivity/push-state.ts +343 -0
  434. package/src/reactivity/recover.ts +153 -0
  435. package/src/reactivity/replay-buffer.ts +141 -0
  436. package/src/reactivity/resume.ts +549 -0
  437. package/src/reactivity/rotation.ts +415 -0
  438. package/src/reactivity/subscriber.ts +132 -0
  439. package/src/reactivity/subscription.ts +66 -0
  440. package/src/reactivity/topic-anchor.ts +71 -0
  441. package/src/reactivity/verify.ts +73 -0
  442. package/src/reactivity/wire-validate.ts +87 -0
  443. package/src/reactivity/wire.ts +224 -0
  444. package/src/transaction/coordinator.ts +53 -27
  445. package/src/transactor/change-notifier.ts +80 -0
  446. package/src/transactor/index.ts +1 -0
  447. package/src/transactor/network-transactor.ts +162 -19
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Cohort-topic substrate — cold-start instantiation.
3
+ *
4
+ * Transcribed from `docs/cohort-topic.md` §Cold-start instantiation and folded back from the
5
+ * simulator-validated `packages/substrate-simulator/src/walk.ts` (the `followOn` / `bootstrap`
6
+ * instantiation paths). A **cold** cohort — one holding no forwarder state for a topic — instantiates
7
+ * as a forwarder for `T` when:
8
+ *
9
+ * - it receives a `RegisterV1` for `T` it does not yet serve, AND
10
+ * - the registration is a legitimate growth point: its `bootstrap: true` flag is set (the root
11
+ * cold-start case) **or** it arrived as a follow-on to a parent cohort's `Promoted` redirect, AND
12
+ * - a quorum of cohort members is willing to serve `T` at the registration's tier.
13
+ *
14
+ * The `followOn` signal is **not** carried on the wire (`RegisterV1` has only `bootstrap`); a cold
15
+ * high-tier cohort returns `NoState` to a *speculative* `d_max` probe, but the single-direction walk
16
+ * means the only registrations that reach a cold tier-`(d+1)` cohort are those that followed the
17
+ * parent's `Promoted` redirect. Determining `followOn` for a given inbound register therefore belongs
18
+ * to the db-p2p cohort host (it has the routing context); db-core takes it as an explicit input to
19
+ * {@link shouldInstantiate}, keeping this module FRET-free. (See the implement handoff for the wiring
20
+ * gap this leaves for the db-p2p binding.)
21
+ *
22
+ * Once instantiated, the new forwarder **registers itself with its tier-`(d − 1)` parent at first
23
+ * opportunity**; until that registration is acked it {@link Forwarder.acceptsParticipants | accepts
24
+ * participants} but {@link Forwarder.servesParentOps | holds} notifications/queries that need parent
25
+ * involvement. The root (tree tier 0) has no parent and is serving immediately.
26
+ *
27
+ * **Just-promoted burst (GROUNDING-resolved: bounce, don't buffer).** A cohort that has just promoted
28
+ * but whose tier-`(d+1)` isn't fully instantiated yet, on a burst of new same-tier registrations,
29
+ * replies `Promoted(d+1)` ({@link promotedRedirectReply}) — a cheap single RPC. It does **not** buffer
30
+ * the registrations and does **not** decline with `UnwillingCohort`; the promotion sticky window
31
+ * (`T_promote_sticky`) keeps it in promoted mode through the burst.
32
+ */
33
+
34
+ import { attachTopicTraffic } from "./traffic.js";
35
+ import { bytesKey } from "./registration/bytes.js";
36
+ import type { RegisterReplyV1, TopicTrafficV1 } from "./wire/types.js";
37
+
38
+ /** Inputs to the cold-start admission gate. */
39
+ export interface ColdStartTrigger {
40
+ /** `RegisterV1.bootstrap` — the root cold-start request flag. */
41
+ readonly bootstrap: boolean;
42
+ /** This register arrived as a follow-on to a parent's `Promoted` redirect (db-p2p-determined). */
43
+ readonly followOn: boolean;
44
+ /** A quorum of cohort members is willing to serve the topic at this tier. */
45
+ readonly quorumWilling: boolean;
46
+ }
47
+
48
+ /**
49
+ * Whether a cold cohort should instantiate forwarder state for an inbound register: it must be a
50
+ * legitimate growth point (`bootstrap` root case **or** a `Promoted` follow-on) **and** have a willing
51
+ * quorum. A speculative `d_max` probe (neither flag set) yields `false`, so the walk gets `NoState`
52
+ * and steps toward the root instead of forking a parallel branch (§Cold-start; §Why the caller
53
+ * doesn't walk on UnwillingCohort).
54
+ */
55
+ export function shouldInstantiate(trigger: ColdStartTrigger): boolean {
56
+ return (trigger.bootstrap || trigger.followOn) && trigger.quorumWilling;
57
+ }
58
+
59
+ /** Lifecycle phase of a freshly-instantiated forwarder. */
60
+ export type ForwarderPhase =
61
+ /** Instantiated, registering with the parent; accepts participants, holds parent-involving ops. */
62
+ | "awaiting_parent"
63
+ /** Fully linked (parent acked, or root with no parent); serves everything. */
64
+ | "serving";
65
+
66
+ /**
67
+ * A freshly cold-started forwarder for one topic at this cohort. Holds the "registered with parent
68
+ * yet?" state that gates parent-involving operations during the link-up window.
69
+ */
70
+ export interface Forwarder {
71
+ /** Tree tier `d` this forwarder serves the topic at. */
72
+ readonly tier: number;
73
+ /** Current lifecycle phase. */
74
+ phase(): ForwarderPhase;
75
+ /** Accept new participants? True from instantiation onward (even before the parent ack). */
76
+ acceptsParticipants(): boolean;
77
+ /** Serve an operation needing parent involvement (notifications/queries)? Only once `serving`. */
78
+ servesParentOps(): boolean;
79
+ /** Mark the parent-registration RPC acked → transition to `serving`. Idempotent; no-op at the root. */
80
+ onParentAck(): void;
81
+ }
82
+
83
+ class ForwarderState implements Forwarder {
84
+ private serving: boolean;
85
+
86
+ constructor(readonly tier: number, hasParent: boolean) {
87
+ // The root (tree tier 0 / no parent) has nothing to hand off to — it serves immediately.
88
+ this.serving = !hasParent;
89
+ }
90
+
91
+ phase(): ForwarderPhase {
92
+ return this.serving ? "serving" : "awaiting_parent";
93
+ }
94
+
95
+ acceptsParticipants(): boolean {
96
+ return true;
97
+ }
98
+
99
+ servesParentOps(): boolean {
100
+ return this.serving;
101
+ }
102
+
103
+ onParentAck(): void {
104
+ this.serving = true;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Construct a forwarder. A tier-`0` forwarder (the root) has no parent and starts `serving`; a deeper
110
+ * forwarder starts `awaiting_parent` until {@link Forwarder.onParentAck} fires.
111
+ */
112
+ export function createForwarder(tier: number): Forwarder {
113
+ if (!Number.isInteger(tier) || tier < 0) {
114
+ throw new RangeError(`forwarder tier must be a non-negative integer, got ${tier}`);
115
+ }
116
+ return new ForwarderState(tier, tier > 0);
117
+ }
118
+
119
+ /**
120
+ * Build a `Promoted(targetTier)` redirect reply — used both for the normal promotion redirect and for
121
+ * the just-promoted-burst bounce (the registrations arriving at a promoted cohort while its child tier
122
+ * is still instantiating). The outgoing cohort's `topicTraffic` is attached when supplied, so the
123
+ * redirected participant can estimate whether the target tier is hot (§Topic traffic signal).
124
+ */
125
+ export function promotedRedirectReply(targetTier: number, traffic?: TopicTrafficV1): RegisterReplyV1 {
126
+ if (!Number.isInteger(targetTier) || targetTier < 1) {
127
+ throw new RangeError(`promoted targetTier must be an integer ≥ 1, got ${targetTier}`);
128
+ }
129
+ const reply: RegisterReplyV1 = { v: 1, result: "promoted", targetTier };
130
+ return traffic !== undefined ? attachTopicTraffic(reply, traffic) : reply;
131
+ }
132
+
133
+ /** Registers a newly-instantiated forwarder with its tier-`(d − 1)` parent; resolves on the parent ack. */
134
+ export interface ParentRegistrar {
135
+ /**
136
+ * Register the forwarder for `topicId` (served at tree tier `tier`, i.e. `d`) with the cohort at
137
+ * `parentCoord`; resolves on ack. `opTier` is the topic's *capacity* tier (T0–T3), threaded so the
138
+ * transport can stamp a well-formed forwarder-link frame; absent when the caller has no op-tier
139
+ * context (the cohort host always supplies it from the instantiating `RegisterV1`).
140
+ */
141
+ registerWithParent(topicId: Uint8Array, parentCoord: Uint8Array, tier: number, opTier?: number): Promise<void>;
142
+ }
143
+
144
+ export interface ColdStartManagerDeps {
145
+ /** Drives the parent-registration RPC (db-p2p binds it to the router). */
146
+ parentRegistrar: ParentRegistrar;
147
+ }
148
+
149
+ /**
150
+ * Tracks cold-started forwarders for a cohort and drives each one's parent registration. Keyed by
151
+ * `topicId`; idempotent per topic (a second instantiate for an already-tracked topic returns the
152
+ * existing forwarder).
153
+ */
154
+ export interface ColdStartManager {
155
+ /**
156
+ * Instantiate (or return the existing) forwarder for `topicId` at tree tier `tier` (`d`). For a
157
+ * deeper-than-root forwarder this kicks off parent registration in the background and flips the
158
+ * forwarder to `serving` on ack. The returned forwarder accepts participants immediately. `opTier`
159
+ * (the topic's T0–T3 capacity tier) is forwarded to the parent registrar for the link frame.
160
+ */
161
+ instantiate(topicId: Uint8Array, tier: number, parentCoord?: Uint8Array, opTier?: number): Forwarder;
162
+ /** The tracked forwarder for `topicId`, or `undefined`. */
163
+ get(topicId: Uint8Array): Forwarder | undefined;
164
+ }
165
+
166
+ class TrackingColdStartManager implements ColdStartManager {
167
+ private readonly forwarders = new Map<string, Forwarder>();
168
+
169
+ constructor(private readonly deps: ColdStartManagerDeps) {}
170
+
171
+ instantiate(topicId: Uint8Array, tier: number, parentCoord?: Uint8Array, opTier?: number): Forwarder {
172
+ const key = bytesKey(topicId);
173
+ const existing = this.forwarders.get(key);
174
+ if (existing !== undefined) {
175
+ return existing;
176
+ }
177
+ const forwarder = createForwarder(tier);
178
+ this.forwarders.set(key, forwarder);
179
+ if (tier > 0) {
180
+ if (parentCoord === undefined) {
181
+ throw new Error(`cold-start of a tier-${tier} forwarder requires a parentCoord`);
182
+ }
183
+ // Register with the parent at first opportunity; flip to serving on ack. A failed parent
184
+ // registration leaves the forwarder accepting participants but holding parent-involving ops,
185
+ // so a later retry (driven by the host) can complete the link-up — surfaced, not swallowed.
186
+ void this.deps.parentRegistrar
187
+ .registerWithParent(topicId, parentCoord, tier, opTier)
188
+ .then(() => forwarder.onParentAck())
189
+ .catch((err: unknown) => {
190
+ console.warn(`cohort-topic cold-start: parent registration for tier-${tier} forwarder failed`, err);
191
+ });
192
+ }
193
+ return forwarder;
194
+ }
195
+
196
+ get(topicId: Uint8Array): Forwarder | undefined {
197
+ return this.forwarders.get(bytesKey(topicId));
198
+ }
199
+ }
200
+
201
+ /** Build a {@link ColdStartManager} over the injected parent registrar. */
202
+ export function createColdStartManager(deps: ColdStartManagerDeps): ColdStartManager {
203
+ return new TrackingColdStartManager(deps);
204
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Cohort-topic substrate — network-size-driven `d_max`.
3
+ *
4
+ * Transcribed from `docs/cohort-topic.md` §Maximum useful depth:
5
+ *
6
+ * ```
7
+ * d_max = max(0, ⌊log_F(n_est)⌋ − 1)
8
+ * ```
9
+ *
10
+ * If `n_est` confidence falls below `confidence_min` (default 0.3) the computation caps `d_max` at
11
+ * `⌊d_max_cap / 2⌋` (`d_max_cap` default 60) as an upper bound — i.e. `min(formula, ⌊d_max_cap / 2⌋)` —
12
+ * to avoid pathological deep probes from an over-estimated population. Small/sparse populations
13
+ * (where the formula already yields a small value) are unaffected.
14
+ *
15
+ * `d_max` is the start tier of the walk-toward-root and is recomputed **lazily** — participants
16
+ * don't need it precise. The size estimate is read through the injected {@link ISizeEstimator}
17
+ * (db-p2p wraps FRET's `estimateSizeAndConfidence`; db-core never imports FRET).
18
+ */
19
+
20
+ import type { ISizeEstimator } from "./ports.js";
21
+
22
+ /** Lazily computes the walk start tier `d_max` from the current network-size estimate. */
23
+ export interface DMaxComputer {
24
+ /** Reads the size estimate, applies the low-confidence cap, returns `d_max`. */
25
+ dMax(): number;
26
+ }
27
+
28
+ /** Default below which `n_est` confidence triggers the `d_max` cap. */
29
+ export const DEFAULT_CONFIDENCE_MIN = 0.3;
30
+ /** Default hard cap on the walk-toward-root start tier. */
31
+ export const DEFAULT_D_MAX_CAP = 60;
32
+
33
+ export interface DMaxConfig {
34
+ /** Network-size estimate source (db-p2p wraps FRET `estimateSizeAndConfidence`). */
35
+ estimator: ISizeEstimator;
36
+ /** Fan-out per tier (the `F` in `log_F`). */
37
+ F: number;
38
+ /** Confidence floor; below it `d_max` is capped at `⌊d_max_cap / 2⌋` (upper bound). Default 0.3. */
39
+ confidenceMin?: number;
40
+ /** Hard cap on `d_max`; also drives the low-confidence cap value. Default 60. */
41
+ dMaxCap?: number;
42
+ }
43
+
44
+ /**
45
+ * `⌊log_F(n)⌋` computed without floating-point drift near exact powers of `F`. Multiplies back up to
46
+ * confirm the floor (`Math.log` can return e.g. `2.9999999` for `log_16(4096)`), so the result is
47
+ * exact for all `n` representable as a JS integer.
48
+ */
49
+ function floorLogF(n: number, F: number): number {
50
+ if (n < 1) return 0;
51
+ let d = Math.floor(Math.log(n) / Math.log(F));
52
+ if (d < 0) d = 0;
53
+ // Correct floating-point error at the boundary in both directions.
54
+ while (Math.pow(F, d + 1) <= n) d++;
55
+ while (d > 0 && Math.pow(F, d) > n) d--;
56
+ return d;
57
+ }
58
+
59
+ /**
60
+ * Build a {@link DMaxComputer}. The estimate is read on every {@link DMaxComputer.dMax} call so the
61
+ * value tracks the latest FRET sample; callers cache it themselves if they want stability.
62
+ */
63
+ export function makeDMaxComputer(config: DMaxConfig): DMaxComputer {
64
+ const { estimator, F } = config;
65
+ if (!Number.isInteger(F) || F < 2) {
66
+ throw new RangeError(`fan-out F must be an integer ≥ 2, got ${F}`);
67
+ }
68
+ const confidenceMin = config.confidenceMin ?? DEFAULT_CONFIDENCE_MIN;
69
+ const dMaxCap = config.dMaxCap ?? DEFAULT_D_MAX_CAP;
70
+ if (!Number.isInteger(dMaxCap) || dMaxCap < 0) {
71
+ throw new RangeError(`d_max_cap must be a non-negative integer, got ${dMaxCap}`);
72
+ }
73
+ const capValue = Math.floor(dMaxCap / 2);
74
+
75
+ return {
76
+ dMax(): number {
77
+ const { nEst, confidence } = estimator.estimate();
78
+ const formula = Math.min(Math.max(0, floorLogF(nEst, F) - 1), dMaxCap);
79
+ // Low confidence caps d_max as an upper bound (not a set-to): an over-estimated
80
+ // population can't push the walk deeper than ⌊d_max_cap / 2⌋, while small
81
+ // populations keep their (smaller) formula value.
82
+ if (confidence < confidenceMin) {
83
+ return Math.min(formula, capValue);
84
+ }
85
+ return formula;
86
+ },
87
+ };
88
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Cohort-topic substrate — intra-cohort gossip bus.
3
+ *
4
+ * Per `docs/cohort-topic.md` §Cohort gossip. Each member periodically broadcasts a `CohortGossipV1`
5
+ * carrying its willingness vector, load buckets, exact per-topic summaries, and any registration
6
+ * record deltas (new/touched records + evictions). The bus:
7
+ *
8
+ * - **broadcasts** outbound gossip over the injected {@link ICohortGossipTransport} (FRET cohort
9
+ * gossip underneath — db-core never imports FRET);
10
+ * - **routes by coord**: a delivered frame is fanned to every coord engine's bus on a node, so each
11
+ * bus merges only the gossip whose `coord` names its own cohort (epoch alone never isolates two
12
+ * coords — they can share a member set, hence an epoch);
13
+ * - **merges inbound** gossip — record deltas into the {@link RegistrationStore} (last-writer-wins
14
+ * by `lastPing`; evictions remove), and willingness/load/summaries into the per-member
15
+ * {@link CohortView};
16
+ * - **detects epoch drift**: an inbound `cohortEpoch` that differs from the local epoch fires the
17
+ * drift handlers, and record deltas under a foreign epoch are *not* merged (their slot
18
+ * assignments belong to a different membership snapshot);
19
+ * - **optionally authenticates** inbound transport frames via an injected {@link GossipInboundVerifier}
20
+ * (db-p2p binds peer-sig + cohort membership), dropping forged or non-member gossip before any merge.
21
+ *
22
+ * Convergence target (doc): a single gossip round spreads a touched record / willingness flip to all
23
+ * members — one {@link CohortGossipBus.applyInbound} of that gossip makes it visible locally.
24
+ */
25
+
26
+ import { bytesEqual, bytesKey } from "../registration/bytes.js";
27
+ import type { RegistrationStore } from "../registration/types.js";
28
+ import type { ICohortGossipTransport, PeerRef, RingCoord } from "../ports.js";
29
+ import { b64urlToBytes, decodeCohortGossipV1, encodeCohortMessage } from "../wire/codec.js";
30
+ import type { CohortGossipV1 } from "../wire/types.js";
31
+ import { fromGossipRecord } from "./records.js";
32
+ import { createCohortView, type CohortView, type MutableCohortView } from "./view.js";
33
+
34
+ /** Handler for inbound gossip (after merge). */
35
+ export type GossipHandler = (g: CohortGossipV1) => void;
36
+ /** Handler for a detected cohort-epoch drift on an inbound gossip. */
37
+ export type DriftHandler = (inboundEpoch: Uint8Array, localEpoch: Uint8Array, from: string) => void;
38
+ /** Authenticity gate for an inbound gossip arriving over the transport — `false` drops the frame. */
39
+ export type GossipInboundVerifier = (g: CohortGossipV1) => boolean;
40
+
41
+ export interface CohortGossipBusDeps {
42
+ /** FRET-backed cohort gossip transport (broadcast / subscribe). */
43
+ transport: ICohortGossipTransport;
44
+ /** Local registration store record deltas merge into. */
45
+ store: RegistrationStore;
46
+ /** This cohort's coord — the broadcast key **and** the inbound routing key (see {@link CohortGossipV1.coord}). */
47
+ coord: RingCoord;
48
+ /** The local cohort epoch, read fresh each merge so rotation is observed. */
49
+ localEpoch: () => Uint8Array;
50
+ /** Wall clock for inbound merges via the transport path; injectable for tests. */
51
+ now?: () => number;
52
+ /** Frame ceiling for encode/decode (defaults to the codec default). */
53
+ maxMessageBytes?: number;
54
+ /**
55
+ * Optional authenticity gate run on every gossip arriving over the transport (db-p2p binds it to the
56
+ * peer-sig primitive: verify `fromMember`'s signature over the gossip image **and** that `fromMember`
57
+ * is a member of this coord's cohort). A `false` verdict drops the frame — no merge, no handler fire —
58
+ * so a forged or non-member gossip can neither spoof willingness/load nor replicate fake records.
59
+ * Absent → the gate is skipped (unit tests / key-less interim mode); the `coord` routing check below
60
+ * still runs regardless. Not applied to direct {@link CohortGossipBus.applyInbound} calls (the trusted
61
+ * test/internal merge entry point).
62
+ */
63
+ verifyInbound?: GossipInboundVerifier;
64
+ /**
65
+ * Optional hook fired after a merge that deleted one or more records via gossiped evictions, carrying
66
+ * the **distinct** topic ids touched. db-p2p wires it to re-`touch` the per-cohort topic budget down
67
+ * (`topicBudget.touch(t, store.directParticipants(t))`) so a topic whose participants drain on a
68
+ * *sibling* (the drain arrives here as a gossip eviction, never this member's own TTL sweep) still
69
+ * releases its budget slot — mirroring the engine's own `sweepStale` re-touch. Kept as a decoupled
70
+ * callback so this gossip/replication module carries no anti-DoS dependency. Absent → skipped.
71
+ */
72
+ onRecordsEvicted?: (topicIds: readonly Uint8Array[]) => void;
73
+ }
74
+
75
+ /** Intra-cohort gossip bus (merge logic over an injected transport). */
76
+ export interface CohortGossipBus {
77
+ /** Broadcast `g` to the cohort. */
78
+ broadcast(g: CohortGossipV1): void;
79
+ /** Subscribe to inbound gossip (fired after merge); returns an unsubscribe handle. */
80
+ onGossip(handler: GossipHandler): () => void;
81
+ /** Subscribe to epoch-drift detections; returns an unsubscribe handle. */
82
+ onDrift(handler: DriftHandler): () => void;
83
+ /** Merge one inbound gossip: records/willingness/load, with epoch drift detection. */
84
+ applyInbound(g: CohortGossipV1, now: number): void;
85
+ /** The merged per-member view (willingness / load / summaries). */
86
+ view(): CohortView;
87
+ /** Detach the transport subscription. */
88
+ close(): void;
89
+ }
90
+
91
+ class TransportCohortGossipBus implements CohortGossipBus {
92
+ private readonly gossipHandlers = new Set<GossipHandler>();
93
+ private readonly driftHandlers = new Set<DriftHandler>();
94
+ private readonly memberView: MutableCohortView = createCohortView();
95
+ private readonly now: () => number;
96
+ private readonly detach: () => void;
97
+
98
+ constructor(private readonly deps: CohortGossipBusDeps) {
99
+ this.now = deps.now ?? ((): number => Date.now());
100
+ this.detach = deps.transport.onMessage((_from: PeerRef, msg: Uint8Array) => this.onInbound(msg));
101
+ }
102
+
103
+ broadcast(g: CohortGossipV1): void {
104
+ this.deps.transport.broadcast(this.deps.coord, encodeCohortMessage(g, this.deps.maxMessageBytes));
105
+ }
106
+
107
+ onGossip(handler: GossipHandler): () => void {
108
+ this.gossipHandlers.add(handler);
109
+ return () => this.gossipHandlers.delete(handler);
110
+ }
111
+
112
+ onDrift(handler: DriftHandler): () => void {
113
+ this.driftHandlers.add(handler);
114
+ return () => this.driftHandlers.delete(handler);
115
+ }
116
+
117
+ applyInbound(g: CohortGossipV1, now: number): void {
118
+ // Coord routing: a frame is fanned to every coord engine's bus, but it belongs only to the bus
119
+ // whose coord it names. Drop any gossip for another cohort so it cannot merge into this store/view
120
+ // (two coords on one node can even share a member set — and thus an epoch — so epoch alone never
121
+ // isolates them).
122
+ if (!this.isOurCoord(g)) {
123
+ return;
124
+ }
125
+ const localEpoch = this.deps.localEpoch();
126
+ const inboundEpoch = b64urlToBytes(g.cohortEpoch);
127
+ const epochMatches = bytesEqual(inboundEpoch, localEpoch);
128
+ if (!epochMatches) {
129
+ for (const handler of this.driftHandlers) {
130
+ handler(inboundEpoch, localEpoch, g.fromMember);
131
+ }
132
+ }
133
+ this.mergeView(g, inboundEpoch);
134
+ if (epochMatches) {
135
+ this.mergeRecords(g, now);
136
+ }
137
+ }
138
+
139
+ view(): CohortView {
140
+ return this.memberView;
141
+ }
142
+
143
+ close(): void {
144
+ this.detach();
145
+ }
146
+
147
+ private onInbound(msg: Uint8Array): void {
148
+ const g = decodeCohortGossipV1(msg, this.deps.maxMessageBytes);
149
+ // Cheap routing check first so a non-matching coord skips the (expensive) signature verify.
150
+ if (!this.isOurCoord(g)) {
151
+ return;
152
+ }
153
+ // Authenticity gate (db-p2p binds peer-sig + cohort membership); a forged/non-member gossip is
154
+ // dropped before any merge or handler fire. Absent in key-less/unit composition.
155
+ if (this.deps.verifyInbound !== undefined && !this.deps.verifyInbound(g)) {
156
+ return;
157
+ }
158
+ this.applyInbound(g, this.now());
159
+ for (const handler of this.gossipHandlers) {
160
+ handler(g);
161
+ }
162
+ }
163
+
164
+ /** True iff `g` names this bus's cohort coord (the inbound routing key). */
165
+ private isOurCoord(g: CohortGossipV1): boolean {
166
+ return bytesEqual(b64urlToBytes(g.coord), this.deps.coord);
167
+ }
168
+
169
+ private mergeView(g: CohortGossipV1, inboundEpoch: Uint8Array): void {
170
+ this.memberView.merge(g.fromMember, {
171
+ cohortEpoch: inboundEpoch,
172
+ willingness: parseInt(g.willingnessBits, 16),
173
+ loadBuckets: g.loadBuckets,
174
+ windowSeconds: g.windowSeconds,
175
+ topicSummaries: g.topicSummaries,
176
+ timestamp: g.timestamp,
177
+ });
178
+ }
179
+
180
+ private mergeRecords(g: CohortGossipV1, now: number): void {
181
+ for (const gr of g.records ?? []) {
182
+ const incoming = fromGossipRecord(gr);
183
+ // A record already past its TTL is dead; merging it would resurrect a registration the
184
+ // owner has (or soon will have) evicted. Drop it, matching `store.evictStale`'s predicate
185
+ // so replication can never reintroduce what local eviction removes.
186
+ if (now - incoming.lastPing > incoming.ttl) {
187
+ continue;
188
+ }
189
+ const held = this.deps.store.getByParticipant(incoming.topicId, incoming.participantId);
190
+ // Last-writer-wins by lastPing: a touch (newer lastPing) replaces an older replica.
191
+ if (held === undefined || incoming.lastPing >= held.lastPing) {
192
+ this.deps.store.put(incoming);
193
+ }
194
+ }
195
+ // Collect the distinct topics a gossiped eviction drained so the budget can be re-touched *after*
196
+ // the deletes (the count must be post-drain). This is the sibling-drain half of the topic-budget
197
+ // leak fix: a topic whose participants are sharded onto a sibling primary drains into this store as
198
+ // a gossip eviction, never via this member's own TTL sweep, so the `onRecordsEvicted` callback must
199
+ // re-`touch` the budget down here too — otherwise the slot leaks for any multi-member topic.
200
+ let evictedTopics: Map<string, Uint8Array> | undefined;
201
+ for (const ref of g.evicted ?? []) {
202
+ const topicId = b64urlToBytes(ref.topicId);
203
+ this.deps.store.delete(topicId, b64urlToBytes(ref.participantId));
204
+ if (this.deps.onRecordsEvicted !== undefined) {
205
+ (evictedTopics ??= new Map()).set(bytesKey(topicId), topicId);
206
+ }
207
+ }
208
+ if (evictedTopics !== undefined && evictedTopics.size > 0) {
209
+ this.deps.onRecordsEvicted!([...evictedTopics.values()]);
210
+ }
211
+ }
212
+ }
213
+
214
+ /** Build a {@link CohortGossipBus} over the injected transport + store. */
215
+ export function createCohortGossipBus(deps: CohortGossipBusDeps): CohortGossipBus {
216
+ return new TransportCohortGossipBus(deps);
217
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./records.js";
2
+ export * from "./view.js";
3
+ export * from "./bus.js";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Cohort-topic substrate — translation between the local {@link RegistrationRecord} (raw bytes)
3
+ * and the wire {@link GossipRecordV1} (base64url). The gossip bus carries record deltas so backups
4
+ * hold the record before the primary fails (per `docs/cohort-topic.md` §Registration record).
5
+ */
6
+
7
+ import { b64urlToBytes, bytesToB64url } from "../wire/codec.js";
8
+ import type { GossipRecordV1 } from "../wire/types.js";
9
+ import type { RegistrationRecord } from "../registration/types.js";
10
+
11
+ /** Encode a local registration record for cross-member gossip. */
12
+ export function toGossipRecord(rec: RegistrationRecord): GossipRecordV1 {
13
+ const out: GossipRecordV1 = {
14
+ topicId: bytesToB64url(rec.topicId),
15
+ participantId: bytesToB64url(rec.participantId),
16
+ tier: rec.tier,
17
+ primary: bytesToB64url(rec.primary),
18
+ backups: rec.backups.map(bytesToB64url),
19
+ attachedAt: rec.attachedAt,
20
+ lastPing: rec.lastPing,
21
+ ttl: rec.ttl,
22
+ };
23
+ if (rec.appState !== undefined) {
24
+ out.appState = bytesToB64url(rec.appState);
25
+ }
26
+ return out;
27
+ }
28
+
29
+ /** Decode a gossiped record back into the local store shape. */
30
+ export function fromGossipRecord(g: GossipRecordV1): RegistrationRecord {
31
+ const out: RegistrationRecord = {
32
+ topicId: b64urlToBytes(g.topicId),
33
+ participantId: b64urlToBytes(g.participantId),
34
+ tier: g.tier,
35
+ primary: b64urlToBytes(g.primary),
36
+ backups: g.backups.map(b64urlToBytes),
37
+ attachedAt: g.attachedAt,
38
+ lastPing: g.lastPing,
39
+ ttl: g.ttl,
40
+ };
41
+ if (g.appState !== undefined) {
42
+ out.appState = b64urlToBytes(g.appState);
43
+ }
44
+ return out;
45
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Cohort-topic substrate — the merged per-member gossip view.
3
+ *
4
+ * Each cohort member periodically gossips its willingness vector, load barometer buckets, and exact
5
+ * per-topic summaries (`CohortGossipV1`). The bus folds the latest such contribution per member into
6
+ * this view (last-writer-wins by gossip `timestamp`), which the willingness, barometer, and traffic
7
+ * tickets read. Records replicate into the registration store separately (see {@link createCohortGossipBus}).
8
+ */
9
+
10
+ import type { CohortTopicSummary } from "../wire/types.js";
11
+
12
+ /** Latest gossip contribution from a single cohort member. */
13
+ export interface MemberContribution {
14
+ /** Cohort epoch this member gossiped under (drift signal). */
15
+ readonly cohortEpoch: Uint8Array;
16
+ /** Willingness vector, 4 bits T0..T3 (0..15) decoded from the hex nibble. */
17
+ readonly willingness: number;
18
+ /** Load barometer buckets, 4 entries 0..7 per tier. */
19
+ readonly loadBuckets: readonly number[];
20
+ /** Observation window for the rate fields in `topicSummaries`. */
21
+ readonly windowSeconds: number;
22
+ /** Exact per-topic summaries this member last reported. */
23
+ readonly topicSummaries: readonly CohortTopicSummary[];
24
+ /** Gossip timestamp (unix ms) — the last-writer-wins key. */
25
+ readonly timestamp: number;
26
+ }
27
+
28
+ /** Read view over the merged per-member contributions, keyed by `fromMember`. */
29
+ export interface CohortView {
30
+ /** Latest contribution from `member` (the `fromMember` string), or `undefined`. */
31
+ get(member: string): MemberContribution | undefined;
32
+ /** All current contributions, keyed by `fromMember`. */
33
+ all(): ReadonlyMap<string, MemberContribution>;
34
+ }
35
+
36
+ /** Mutable view the bus writes into; exposes the read {@link CohortView} surface. */
37
+ export interface MutableCohortView extends CohortView {
38
+ /** Merge `c` for `member` iff it is at least as recent as the held contribution. Returns true if applied. */
39
+ merge(member: string, c: MemberContribution): boolean;
40
+ }
41
+
42
+ class MapCohortView implements MutableCohortView {
43
+ private readonly byMember = new Map<string, MemberContribution>();
44
+
45
+ get(member: string): MemberContribution | undefined {
46
+ return this.byMember.get(member);
47
+ }
48
+
49
+ all(): ReadonlyMap<string, MemberContribution> {
50
+ return this.byMember;
51
+ }
52
+
53
+ merge(member: string, c: MemberContribution): boolean {
54
+ const held = this.byMember.get(member);
55
+ if (held !== undefined && c.timestamp < held.timestamp) {
56
+ return false; // stale; keep the newer contribution
57
+ }
58
+ this.byMember.set(member, c);
59
+ return true;
60
+ }
61
+ }
62
+
63
+ /** Construct an empty {@link MutableCohortView}. */
64
+ export function createCohortView(): MutableCohortView {
65
+ return new MapCohortView();
66
+ }
@@ -0,0 +1,20 @@
1
+ export * from "./ports.js";
2
+ export * from "./ring-hash.js";
3
+ export * from "./addressing.js";
4
+ export * from "./tiers.js";
5
+ export * from "./dmax.js";
6
+ export * from "./wire/index.js";
7
+ export * from "./registration/index.js";
8
+ export * from "./gossip/index.js";
9
+ export * from "./sig/index.js";
10
+ export * from "./membership/index.js";
11
+ export * from "./load/index.js";
12
+ export * from "./willingness.js";
13
+ export * from "./traffic.js";
14
+ export * from "./walk.js";
15
+ export * from "./promotion.js";
16
+ export * from "./coldstart.js";
17
+ export * from "./antiflood/index.js";
18
+ export * from "./antidos/index.js";
19
+ export * from "./member-engine.js";
20
+ export * from "./service.js";