@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,81 @@
1
+ /**
2
+ * Cohort-topic substrate — in-memory registration store.
3
+ *
4
+ * Per `docs/cohort-topic.md` §Registration mechanics. Records are doubly indexed: an outer map
5
+ * keyed by topic, each holding an inner map keyed by participant. This gives O(1)
6
+ * {@link RegistrationStore.getByParticipant} / {@link RegistrationStore.delete} and O(participants)
7
+ * {@link RegistrationStore.listByTopic} without a secondary index to keep in sync. The store is
8
+ * local soft state; cross-member replication runs over cohort gossip in a later ticket.
9
+ */
10
+
11
+ import { bytesKey } from "./bytes.js";
12
+ import type { RegistrationRecord, RegistrationStore } from "./types.js";
13
+
14
+ class InMemoryRegistrationStore implements RegistrationStore {
15
+ /** topicKey → (participantKey → record). Inner maps are pruned when they empty. */
16
+ private readonly byTopic = new Map<string, Map<string, RegistrationRecord>>();
17
+
18
+ put(rec: RegistrationRecord): void {
19
+ const tk = bytesKey(rec.topicId);
20
+ let inner = this.byTopic.get(tk);
21
+ if (inner === undefined) {
22
+ inner = new Map<string, RegistrationRecord>();
23
+ this.byTopic.set(tk, inner);
24
+ }
25
+ inner.set(bytesKey(rec.participantId), rec);
26
+ }
27
+
28
+ getByParticipant(topicId: Uint8Array, participantId: Uint8Array): RegistrationRecord | undefined {
29
+ return this.byTopic.get(bytesKey(topicId))?.get(bytesKey(participantId));
30
+ }
31
+
32
+ listByTopic(topicId: Uint8Array): readonly RegistrationRecord[] {
33
+ const inner = this.byTopic.get(bytesKey(topicId));
34
+ return inner === undefined ? [] : [...inner.values()];
35
+ }
36
+
37
+ listAll(): readonly RegistrationRecord[] {
38
+ const out: RegistrationRecord[] = [];
39
+ for (const inner of this.byTopic.values()) {
40
+ for (const rec of inner.values()) {
41
+ out.push(rec);
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+
47
+ delete(topicId: Uint8Array, participantId: Uint8Array): void {
48
+ const tk = bytesKey(topicId);
49
+ const inner = this.byTopic.get(tk);
50
+ if (inner === undefined) return;
51
+ inner.delete(bytesKey(participantId));
52
+ if (inner.size === 0) {
53
+ this.byTopic.delete(tk);
54
+ }
55
+ }
56
+
57
+ directParticipants(topicId: Uint8Array): number {
58
+ return this.byTopic.get(bytesKey(topicId))?.size ?? 0;
59
+ }
60
+
61
+ evictStale(now: number): readonly RegistrationRecord[] {
62
+ const evicted: RegistrationRecord[] = [];
63
+ for (const [tk, inner] of this.byTopic) {
64
+ for (const [pk, rec] of inner) {
65
+ if (now - rec.lastPing > rec.ttl) {
66
+ evicted.push(rec);
67
+ inner.delete(pk);
68
+ }
69
+ }
70
+ if (inner.size === 0) {
71
+ this.byTopic.delete(tk);
72
+ }
73
+ }
74
+ return evicted;
75
+ }
76
+ }
77
+
78
+ /** Construct an empty {@link RegistrationStore}. */
79
+ export function createRegistrationStore(): RegistrationStore {
80
+ return new InMemoryRegistrationStore();
81
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Cohort-topic substrate — registration record store, types and constants.
3
+ *
4
+ * Transcribed from `docs/cohort-topic.md` §Registration mechanics and §Primary and backup
5
+ * sharding. This module owns the **local**, transport-agnostic shapes: the soft-state record a
6
+ * cohort member holds per participant, the in-memory store interface over those records, and the
7
+ * TTL constants. Cross-member replication (cohort gossip) is layered on top by db-p2p and the
8
+ * bridge ticket; db-core never imports FRET or libp2p here.
9
+ *
10
+ * Peer ids are the opaque byte-array references the cohort-topic substrate uses throughout — the
11
+ * same representation as {@link import("../ports.js").PeerRef}`.id` and
12
+ * {@link import("../ports.js").RingCoord} (raw `Uint8Array`, not the structural
13
+ * {@link import("../../network/types.js").PeerId}). The wire layer carries them as base64url
14
+ * strings; this store works in raw bytes and the renewal/handoff bridges translate at the wire
15
+ * boundary.
16
+ */
17
+
18
+ /**
19
+ * Soft-state registration a cohort member holds per participant. Replicated across the `~k`
20
+ * members by cohort gossip; only {@link RegistrationRecord.primary} serves, with
21
+ * {@link RegistrationRecord.backups} watching for warm failover.
22
+ */
23
+ export interface RegistrationRecord {
24
+ /** Topic id, 32 bytes. */
25
+ topicId: Uint8Array;
26
+ /** Registering participant. */
27
+ participantId: Uint8Array;
28
+ /** Tier this registration sits at (0..3). */
29
+ tier: number;
30
+ /** Cohort member assigned to serve this participant. */
31
+ primary: Uint8Array;
32
+ /** 1..2 warm-failover cohort members. */
33
+ backups: Uint8Array[];
34
+ /** Unix ms the registration first attached. */
35
+ attachedAt: number;
36
+ /** Unix ms of the most recent successful ping/touch. */
37
+ lastPing: number;
38
+ /** Lifetime in ms; record is stale once `now − lastPing > ttl`. */
39
+ ttl: number;
40
+ /** Opaque application-defined per-registration state; the layer never interprets it. */
41
+ appState?: Uint8Array;
42
+ }
43
+
44
+ /**
45
+ * In-memory registration store, indexed for both per-participant lookup and per-topic listing.
46
+ * This ticket owns the **local** store and its indexes; the gossip layer and TTL loop call the
47
+ * deterministic functions over it.
48
+ */
49
+ export interface RegistrationStore {
50
+ /** Insert or replace the record for `(topicId, participantId)`. */
51
+ put(rec: RegistrationRecord): void;
52
+ /** Record for `(topicId, participantId)`, or `undefined`. */
53
+ getByParticipant(topicId: Uint8Array, participantId: Uint8Array): RegistrationRecord | undefined;
54
+ /** All records held for `topicId` (empty if none). */
55
+ listByTopic(topicId: Uint8Array): readonly RegistrationRecord[];
56
+ /** Every record across all topics — used by the rotation handoff inventory pass. */
57
+ listAll(): readonly RegistrationRecord[];
58
+ /** Remove the record for `(topicId, participantId)`. */
59
+ delete(topicId: Uint8Array, participantId: Uint8Array): void;
60
+ /** Stock count of direct participants for `topicId` (drives promotion). */
61
+ directParticipants(topicId: Uint8Array): number;
62
+ /** Remove and return every record where `now − lastPing > ttl`. */
63
+ evictStale(now: number): readonly RegistrationRecord[];
64
+ }
65
+
66
+ /** Core-tier default registration TTL (ms). */
67
+ export const DEFAULT_TTL_MS = 90_000;
68
+ /** Edge-tier default registration TTL (ms). */
69
+ export const EDGE_TTL_MS = 60_000;
70
+ /** Consecutive ping failures before a participant promotes `backups[0]`. */
71
+ export const MAX_PING_FAILURES = 3;
72
+
73
+ /** `ping_interval = ttl / 3` (default 30s Core, 20s Edge), floored to whole ms. */
74
+ export function pingIntervalMs(ttl: number): number {
75
+ return Math.floor(ttl / 3);
76
+ }
@@ -0,0 +1,50 @@
1
+ import { sha256 } from '@noble/hashes/sha2.js';
2
+ import type { IRingHash, RingCoord } from './ports.js';
3
+
4
+ /**
5
+ * Default ring width in bits. 256 = the full SHA-256 digest, which lines up byte-for-byte with
6
+ * FRET's coordinate type (FRET hashes ring keys with SHA-256). Override only when targeting a
7
+ * DHT with a narrower ring.
8
+ */
9
+ export const RING_BITS = 256;
10
+
11
+ /**
12
+ * db-core's {@link IRingHash}, backed by its own SHA-256 (the same digest db-core already uses
13
+ * for logs and block ids) — **not** an import of any FRET hash module. This keeps db-core
14
+ * FRET-free while guaranteeing on-the-wire coord compatibility: at the default `ringBits = 256`,
15
+ * `H(bytes)` is the full SHA-256 digest, identical to what FRET produces for the same input.
16
+ *
17
+ * For ring widths that are not a whole number of bytes, the digest is truncated to
18
+ * `ceil(ringBits / 8)` bytes and the trailing partial byte's unused low bits are zeroed, so two
19
+ * inputs whose digests share the first `ringBits` bits yield byte-identical coords.
20
+ */
21
+ export class RingHash implements IRingHash {
22
+ public readonly ringBits: number;
23
+ private readonly nBytes: number;
24
+ private readonly tailMask: number;
25
+
26
+ constructor(ringBits: number = RING_BITS) {
27
+ if (!Number.isInteger(ringBits) || ringBits <= 0 || ringBits > 256) {
28
+ throw new RangeError(`ringBits must be an integer in [1, 256], got ${ringBits}`);
29
+ }
30
+ this.ringBits = ringBits;
31
+ this.nBytes = Math.ceil(ringBits / 8);
32
+ const remBits = ringBits - (this.nBytes - 1) * 8;
33
+ this.tailMask = remBits === 8 ? 0xff : (0xff << (8 - remBits)) & 0xff;
34
+ }
35
+
36
+ H(bytes: Uint8Array): RingCoord {
37
+ const digest = sha256(bytes);
38
+ if (this.nBytes === digest.length && this.tailMask === 0xff) {
39
+ return digest;
40
+ }
41
+ const coord = digest.slice(0, this.nBytes);
42
+ coord[this.nBytes - 1] = coord[this.nBytes - 1]! & this.tailMask;
43
+ return coord;
44
+ }
45
+ }
46
+
47
+ /** Convenience factory mirroring the db-p2p adapter construction style. */
48
+ export function createRingHash(ringBits: number = RING_BITS): IRingHash {
49
+ return new RingHash(ringBits);
50
+ }
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Cohort-topic substrate — participant-facing service composition.
3
+ *
4
+ * This is the substrate's public contract to applications (`docs/cohort-topic.md` §Application
5
+ * policies): given a `topicId` and a `tier`, reliably find a willing primary (or fail with a clear
6
+ * back-off), keep a registration alive within its TTL, and verify cohort identity/membership. It is
7
+ * the participant half of the substrate; the cohort half is {@link import("./member-engine.js").CohortMemberEngine}.
8
+ *
9
+ * The service is FRET-free: it drives the {@link ITopicRouter} (walk / register / direct-dial) and the
10
+ * other db-core ports by injection. db-p2p binds those ports to FRET + libp2p and constructs the
11
+ * service on the same node it runs the member engine on, so a node is simultaneously a participant and
12
+ * a cohort member (`docs/cohort-topic.md` §FRET integration — all four protocols on one node).
13
+ *
14
+ * Composition wired here: {@link WalkEngine} (lookup / register), the `d_max` computer + tier
15
+ * addressing, the participant-side {@link RenewalParticipant} (the `ttl/3` ping with crash-failover),
16
+ * and the application integration hooks ({@link CohortGossipBus}, {@link MembershipVerifier}). The
17
+ * cohort-side modules (willingness, promotion, traffic, store, membership publisher, anti-DoS) are
18
+ * assembled by the member engine the FRET host wires into the `RouteAndMaybeAct` activity callback.
19
+ */
20
+
21
+ import { randomBytes } from "@noble/hashes/utils.js";
22
+ import { createTierAddressing, type TierAddressing } from "./addressing.js";
23
+ import { makeDMaxComputer, type DMaxComputer } from "./dmax.js";
24
+ import { DEFAULT_FANOUT } from "./addressing.js";
25
+ import type { ISizeEstimator, ITopicRouter, IRingHash, RingCoord } from "./ports.js";
26
+ import { createWalkEngine, type RegisterMessageFactory, type WalkEngine, type WalkOutcome } from "./walk.js";
27
+ import { createRenewalParticipant, type RenewalParticipant, type RenewalParticipantTransport, type UnsignedRenew } from "./registration/renewal.js";
28
+ import type { RegistrationRecord } from "./registration/types.js";
29
+ import { DEFAULT_TTL_MS } from "./registration/types.js";
30
+ import { recordKey } from "./registration/bytes.js";
31
+ import type { CohortGossipBus } from "./gossip/bus.js";
32
+ import type { MembershipVerifier } from "./membership/verifier.js";
33
+ import type { Tier } from "./tiers.js";
34
+ import { bytesToB64url, b64urlToBytes, decodeRenewReplyV1, encodeCohortMessage } from "./wire/codec.js";
35
+ import type { RegisterReplyV1, RegisterV1, RenewReplyV1, TopicTrafficV1 } from "./wire/types.js";
36
+ import type { CollectionChangeEvent, CommitCert } from "../transactor/change-notifier.js";
37
+
38
+ /** A resolved cohort for a topic/tier — the return of {@link CohortTopicService.lookup}. */
39
+ export interface CohortHint {
40
+ readonly topicId: Uint8Array;
41
+ readonly tier: Tier;
42
+ /** Serving cohort member. */
43
+ readonly primary: Uint8Array;
44
+ /** Warm-failover cohort members (1..2). */
45
+ readonly backups: Uint8Array[];
46
+ readonly cohortEpoch: Uint8Array;
47
+ /** Full cohort member set, for client-side caching. */
48
+ readonly cohortMembers: Uint8Array[];
49
+ /** Coarse traffic barometer, when the cohort attached one. */
50
+ readonly topicTraffic?: TopicTrafficV1;
51
+ }
52
+
53
+ /** A live registration: a {@link CohortHint} plus the participant-side renewal handle behind it. */
54
+ export interface RegistrationHandle extends CohortHint {
55
+ /** Internal renewal driver (the `ttl/3` ping loop). Opaque to applications. */
56
+ readonly renewal: RenewalParticipant;
57
+ }
58
+
59
+ /**
60
+ * The substrate's **origination hook**: a commit that lands on a node which is a cohort member for the
61
+ * collection's reactivity topic is delivered here by the local change-notifier bridge
62
+ * (`local-change-notifier-bridge`), carrying the raw {@link CollectionChangeEvent} and the
63
+ * pass-through {@link CommitCert} (extracted from cluster consensus, forwarded UNCHANGED). Reactivity
64
+ * (and later matchmaking) set this hook to fan the event out — reusing `commitCert.thresholdSig`
65
+ * directly, never re-signing. The bridge swallows + logs any throw so origination can never break the
66
+ * commit.
67
+ */
68
+ export type LocalChangeHook = (event: CollectionChangeEvent, commitCert: CommitCert) => void;
69
+
70
+ /** Thrown when the substrate cannot place a registration right now; carries the back-off delay. */
71
+ export class CohortBackoffError extends Error {
72
+ constructor(readonly afterMs: number) {
73
+ super(`cohort-topic: no willing primary right now; retry after ${afterMs}ms`);
74
+ this.name = "CohortBackoffError";
75
+ }
76
+ }
77
+
78
+ /** A registration request (`docs/cohort-topic.md` §Application policies). */
79
+ export interface RegisterRequest {
80
+ readonly topicId: Uint8Array;
81
+ readonly tier: Tier;
82
+ /** Opaque application slot (reactivity / matchmaking define the contents). */
83
+ readonly appPayload?: Uint8Array;
84
+ /** Registration TTL (ms); defaults to the tier default. */
85
+ readonly ttl?: number;
86
+ /** Mark this a cold-root bootstrap request. */
87
+ readonly bootstrap?: boolean;
88
+ }
89
+
90
+ /** The substrate's participant-facing contract. */
91
+ export interface CohortTopicService {
92
+ /** Walk → register for `topicId` at `tier`; resolves a live {@link RegistrationHandle} or throws {@link CohortBackoffError}. */
93
+ register(req: RegisterRequest): Promise<RegistrationHandle>;
94
+ /** Run one `ttl/3` renewal cycle for `handle` (handles `primary_moved` + crash-failover). */
95
+ renew(handle: RegistrationHandle): Promise<void>;
96
+ /** Resolve the cohort for `topicId` at `tier` without keeping a live registration. */
97
+ lookup(topicId: Uint8Array, tier: Tier): Promise<CohortHint>;
98
+ /** Stop renewing `handle` and send a best-effort signed withdraw tombstone so the cohort frees the
99
+ * record immediately (TTL expiry remains the fallback if the primary is unreachable). */
100
+ withdraw(handle: RegistrationHandle): Promise<void>;
101
+ /** Origination hook the change-notifier bridge invokes per local member commit; see {@link LocalChangeHook}. */
102
+ onLocalCommit?: LocalChangeHook;
103
+ /** Cohort gossip bus — applications fold app state into the existing gossip. */
104
+ cohortGossip(): CohortGossipBus;
105
+ /** Membership verifier — applications verify threshold-signed app messages. */
106
+ verifier(): MembershipVerifier;
107
+ }
108
+
109
+ /**
110
+ * Signs the participant's outbound `RegisterV1` / `RenewV1` bodies (db-p2p supplies the peer key).
111
+ *
112
+ * Async because the underlying libp2p `PrivateKey.sign` is async; both call sites already `await`
113
+ * (`messageFactory.build` and the renewal `sign` hook). The signature is over the canonical body
114
+ * image ({@link import("./wire/payloads.js").registerSigningPayload} /
115
+ * {@link import("./wire/payloads.js").renewSigningPayload}) so a cohort member can recompute and
116
+ * verify it against the participant's peer key.
117
+ */
118
+ export interface ParticipantSigner {
119
+ /** Sign a `RegisterV1` (minus its signature); resolves the base64url signature. */
120
+ signRegister(body: Omit<RegisterV1, "signature">): Promise<string>;
121
+ /** Sign a `RenewV1` (minus its signature); resolves the base64url signature. */
122
+ signRenew(body: UnsignedRenew): Promise<string>;
123
+ }
124
+
125
+ /** Per-service tunables (cohort size, threshold, fan-out, TTL); all optional. */
126
+ export interface CohortServiceConfig {
127
+ /** Fan-out per tier `F`. Default {@link DEFAULT_FANOUT}. */
128
+ readonly fanout?: number;
129
+ /** Requested cohort size `wantK`. Default 16. */
130
+ readonly wantK?: number;
131
+ /** Threshold signers `minSigs = k − x`. Default 14. */
132
+ readonly minSigs?: number;
133
+ /** Default registration TTL (ms). Default {@link DEFAULT_TTL_MS}. */
134
+ readonly ttl?: number;
135
+ /** Frame ceiling for encode/decode. Defaults to the codec default. */
136
+ readonly maxMessageBytes?: number;
137
+ }
138
+
139
+ export interface CohortTopicServiceDeps {
140
+ /** This participant's peer id (the `P` in `coord_d(P, topicId)`). */
141
+ readonly self: Uint8Array;
142
+ /** Hash + ring math (db-core's own SHA-256). */
143
+ readonly hash: IRingHash;
144
+ /** FRET-backed router (walk / register / direct-dial). */
145
+ readonly router: ITopicRouter;
146
+ /** FRET-backed network-size estimator feeding `d_max`. */
147
+ readonly sizeEstimator: ISizeEstimator;
148
+ /** Participant body signer. */
149
+ readonly signer: ParticipantSigner;
150
+ /** Cohort gossip bus (the host constructs it for this node's cohort). */
151
+ readonly gossipBus: CohortGossipBus;
152
+ /** Participant-side membership verifier. */
153
+ readonly verifier: MembershipVerifier;
154
+ /** Monotonic-ish wall clock (unix ms); injectable for tests. Default `Date.now`. */
155
+ readonly clock?: () => number;
156
+ /**
157
+ * Optional cold-start bootstrap-evidence builder (db-p2p supplies it). Invoked **only** on the
158
+ * bootstrap re-issue (when the walk sets `bootstrap: true`), with the register's own canonical
159
+ * fields — the same `(topicId, tier, participantCoord, timestamp)` tuple a verifier binds via
160
+ * `bootstrapBoundImage`. It returns the **raw** envelope JSON bytes — `utf8(JSON.stringify(env))`,
161
+ * NOT the already-base64url string from `serializeBootstrapEvidenceEnvelope` — or `undefined` to
162
+ * attach none; the service base64url-encodes those bytes into `RegisterV1.bootstrapEvidence`
163
+ * **before** the body is signed, so the participant signature covers them and a MITM cannot strip or
164
+ * swap the proof. (Returning the `serialize()` string's bytes would double-encode the field.)
165
+ * Absent (default) → no evidence is attached, exactly today's behavior. The minting logic (PoW
166
+ * search, reputation/parent-ref signing) is the db-p2p follow-on.
167
+ */
168
+ readonly buildBootstrapEvidence?: (params: {
169
+ readonly topicId: string;
170
+ readonly tier: number;
171
+ readonly participantCoord: string;
172
+ readonly timestamp: number;
173
+ }) => Promise<Uint8Array | undefined>;
174
+ readonly config?: CohortServiceConfig;
175
+ }
176
+
177
+ class WalkRegisterService implements CohortTopicService {
178
+ public onLocalCommit?: LocalChangeHook;
179
+
180
+ private readonly addressing: TierAddressing;
181
+ private readonly dmax: DMaxComputer;
182
+ private readonly walk: WalkEngine;
183
+ private readonly clock: () => number;
184
+ private readonly ttl: number;
185
+ private readonly maxMessageBytes?: number;
186
+ /** Live renewal drivers, keyed by `(topicId, participantId)` so renew/withdraw find their handle. */
187
+ private readonly renewals = new Map<string, RenewalParticipant>();
188
+ private readonly participantId: Uint8Array;
189
+
190
+ constructor(private readonly deps: CohortTopicServiceDeps) {
191
+ const cfg = deps.config ?? {};
192
+ const fanout = cfg.fanout ?? DEFAULT_FANOUT;
193
+ this.clock = deps.clock ?? ((): number => Date.now());
194
+ this.ttl = cfg.ttl ?? DEFAULT_TTL_MS;
195
+ this.maxMessageBytes = cfg.maxMessageBytes;
196
+ // The participant identity carried on the wire (`participantCoord` on register, `participantId`
197
+ // on renew) IS `self` — the dialable peer id db-p2p supplies via its peer-codec. It is the
198
+ // record key the cohort stores AND the signer id its peer-key signature verifies against, so it
199
+ // must round-trip back to a peer id rather than being re-hashed here (§Tier addressing names the
200
+ // routing coord `P = self` directly; slot assignment hashes it internally for uniformity).
201
+ this.participantId = deps.self;
202
+ this.addressing = createTierAddressing(deps.hash, fanout);
203
+ this.dmax = makeDMaxComputer({ estimator: deps.sizeEstimator, F: fanout });
204
+ this.walk = createWalkEngine({
205
+ router: deps.router,
206
+ addressing: this.addressing,
207
+ dmax: this.dmax,
208
+ self: deps.self,
209
+ factory: this.messageFactory(),
210
+ config: { wantK: cfg.wantK, minSigs: cfg.minSigs, maxMessageBytes: cfg.maxMessageBytes },
211
+ });
212
+ }
213
+
214
+ async register(req: RegisterRequest): Promise<RegistrationHandle> {
215
+ const outcome = await this.walk.register(req.topicId, req.tier, req.appPayload);
216
+ return this.handleFromOutcome(req, outcome);
217
+ }
218
+
219
+ async lookup(topicId: Uint8Array, tier: Tier): Promise<CohortHint> {
220
+ // A read-only probe: it walks to the responsible cohort exactly as a register would and returns the
221
+ // same cohort snapshot, but admits NOTHING — no soft-state record, no arrival, no promotion trigger,
222
+ // no topic-budget touch, and never a cold-start instantiation. A topic served nowhere resolves to a
223
+ // `CohortBackoffError` (the probe never bootstraps a cold root), so a lookup leaves no throwaway
224
+ // registration behind to TTL-expire (§Application policies; the lookup-as-register interim is gone).
225
+ const outcome = await this.walk.register(topicId, tier, undefined, { probe: true });
226
+ if (outcome.kind !== "accepted") {
227
+ throw new CohortBackoffError(outcome.kind === "retry_later" ? outcome.afterMs : 0);
228
+ }
229
+ return this.hintFromReply(topicId, tier, outcome.reply);
230
+ }
231
+
232
+ async renew(handle: RegistrationHandle): Promise<void> {
233
+ // Withdrawn handles drop out of the live set; renewing one is a no-op so withdraw() actually
234
+ // stops the local ping loop (rather than the app silently continuing to refresh it).
235
+ if (!this.renewals.has(recordKey(handle.topicId, this.participantId))) {
236
+ return;
237
+ }
238
+ await handle.renewal.pingLoop();
239
+ this.syncHandle(handle);
240
+ }
241
+
242
+ async withdraw(handle: RegistrationHandle): Promise<void> {
243
+ // Two halves: (1) drop the handle from the live set so further renew() pings no-op (see renew()),
244
+ // stopping the local ping loop; (2) fire a best-effort signed withdraw tombstone to the current
245
+ // primary so the cohort frees the record immediately instead of holding it for up to a full TTL.
246
+ // The delete happens FIRST so a concurrent renew() already no-ops before the tombstone is sent. If
247
+ // the tombstone send fails (primary unreachable), the cohort soft-state TTL-expires as the
248
+ // fallback — withdraw never throws on a transport failure. Idempotent: a second withdraw finds
249
+ // `renewal === undefined` and no-ops.
250
+ const key = recordKey(handle.topicId, this.participantId);
251
+ const renewal = this.renewals.get(key);
252
+ this.renewals.delete(key);
253
+ await renewal?.withdraw();
254
+ }
255
+
256
+ cohortGossip(): CohortGossipBus {
257
+ return this.deps.gossipBus;
258
+ }
259
+
260
+ verifier(): MembershipVerifier {
261
+ return this.deps.verifier;
262
+ }
263
+
264
+ // --- internals ---
265
+
266
+ private handleFromOutcome(req: RegisterRequest, outcome: WalkOutcome): RegistrationHandle {
267
+ if (outcome.kind !== "accepted") {
268
+ throw new CohortBackoffError(outcome.kind === "retry_later" ? outcome.afterMs : 0);
269
+ }
270
+ const hint = this.hintFromReply(req.topicId, req.tier, outcome.reply);
271
+ const renewal = this.startRenewal(req, hint, outcome.reply);
272
+ return { ...hint, renewal };
273
+ }
274
+
275
+ private startRenewal(req: RegisterRequest, hint: CohortHint, reply: RegisterReplyV1): RenewalParticipant {
276
+ const ttl = req.ttl ?? this.ttl;
277
+ const initial: RegistrationRecord = {
278
+ topicId: req.topicId,
279
+ participantId: this.participantId,
280
+ tier: req.tier,
281
+ primary: hint.primary,
282
+ backups: hint.backups,
283
+ attachedAt: this.clock(),
284
+ lastPing: this.clock(),
285
+ ttl,
286
+ appState: req.appPayload,
287
+ };
288
+ const transport = this.renewalTransport(req);
289
+ const renewal = createRenewalParticipant(initial, {
290
+ transport,
291
+ clock: this.clock,
292
+ sign: (body: UnsignedRenew): Promise<string> => this.deps.signer.signRenew(body),
293
+ correlationId: reply.cohortEpoch ?? bytesToB64url(this.participantId),
294
+ initialCohortEpoch: hint.cohortEpoch,
295
+ });
296
+ this.renewals.set(recordKey(req.topicId, this.participantId), renewal);
297
+ return renewal;
298
+ }
299
+
300
+ /** Renewal transport: dial the cached primary directly; a full failure re-runs the register walk. */
301
+ private renewalTransport(req: RegisterRequest): RenewalParticipantTransport {
302
+ return {
303
+ send: async (target: Uint8Array, msg): Promise<RenewReplyV1> => {
304
+ const raw = await this.deps.router.dialMember({ id: target }, encodeCohortMessage(msg, this.maxMessageBytes));
305
+ return decodeRenewReplyV1(raw, this.maxMessageBytes);
306
+ },
307
+ relookup: async (): Promise<void> => {
308
+ await this.walk.register(req.topicId, req.tier, req.appPayload);
309
+ },
310
+ };
311
+ }
312
+
313
+ private hintFromReply(topicId: Uint8Array, tier: Tier, reply: RegisterReplyV1): CohortHint {
314
+ if (reply.primary === undefined || reply.cohortEpoch === undefined) {
315
+ throw new CohortBackoffError(0);
316
+ }
317
+ return {
318
+ topicId,
319
+ tier,
320
+ primary: b64urlToBytes(reply.primary),
321
+ backups: (reply.backups ?? []).map(b64urlToBytes),
322
+ cohortEpoch: b64urlToBytes(reply.cohortEpoch),
323
+ cohortMembers: (reply.cohortMembers ?? []).map(b64urlToBytes),
324
+ topicTraffic: reply.topicTraffic,
325
+ };
326
+ }
327
+
328
+ private syncHandle(handle: RegistrationHandle): void {
329
+ const rec = handle.renewal.record;
330
+ (handle as { primary: Uint8Array }).primary = rec.primary;
331
+ (handle as { backups: Uint8Array[] }).backups = [...rec.backups];
332
+ const epoch = handle.renewal.cohortEpochHint;
333
+ if (epoch !== undefined) {
334
+ (handle as { cohortEpoch: Uint8Array }).cohortEpoch = epoch;
335
+ }
336
+ }
337
+
338
+ /** The per-probe `RegisterV1` builder: stamps participant coord/ttl/correlation and signs. */
339
+ private messageFactory(): RegisterMessageFactory {
340
+ const participantCoord = bytesToB64url(this.participantId);
341
+ return {
342
+ build: async (params): Promise<RegisterV1> => {
343
+ const body: Omit<RegisterV1, "signature"> = {
344
+ v: 1,
345
+ topicId: bytesToB64url(params.topicId),
346
+ tier: params.tier,
347
+ treeTier: params.treeTier,
348
+ participantCoord,
349
+ ttl: this.ttl,
350
+ timestamp: this.clock(),
351
+ correlationId: this.freshCorrelationId(),
352
+ };
353
+ if (params.probe) {
354
+ // A read-only lookup probe. Mutually exclusive with bootstrap (the walk never sets
355
+ // `bootstrap` on a probe), so the cold-start-evidence branch below cannot run for a probe.
356
+ body.probe = true;
357
+ }
358
+ if (params.bootstrap) {
359
+ body.bootstrap = true;
360
+ // Attach cold-start evidence BEFORE signing (so the signature covers it). Bind the
361
+ // builder to the body's own canonical fields — the exact tuple a verifier reconstructs.
362
+ const evidence = await this.deps.buildBootstrapEvidence?.({
363
+ topicId: body.topicId,
364
+ tier: body.tier,
365
+ participantCoord: body.participantCoord,
366
+ timestamp: body.timestamp,
367
+ });
368
+ if (evidence !== undefined && evidence.length > 0) {
369
+ body.bootstrapEvidence = bytesToB64url(evidence);
370
+ }
371
+ }
372
+ if (params.appPayload !== undefined) {
373
+ body.appPayload = bytesToB64url(params.appPayload);
374
+ }
375
+ return { ...body, signature: await this.deps.signer.signRegister(body) };
376
+ },
377
+ };
378
+ }
379
+
380
+ /** 16 fresh CSPRNG bytes, base64url. A correlation id must be unique per probe — the replay guard
381
+ * keys on it — so it cannot be derived from the clock (two probes in the same ms would collide). */
382
+ private freshCorrelationId(): string {
383
+ return bytesToB64url(randomBytes(16));
384
+ }
385
+ }
386
+
387
+ /** Build the participant-facing {@link CohortTopicService} over the injected ports. */
388
+ export function createCohortTopicService(deps: CohortTopicServiceDeps): CohortTopicService {
389
+ return new WalkRegisterService(deps);
390
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./payloads.js";
2
+ export * from "./threshold.js";
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Cohort-topic substrate — canonical threshold-signature payloads.
3
+ *
4
+ * A threshold signature is only verifiable if signer and verifier agree on the exact bytes signed.
5
+ * These builders produce that deterministic byte image for each threshold-signed message
6
+ * (`MembershipCertV1`, `PromotionNoticeV1`, `DemotionNoticeV1`), covering exactly the semantic
7
+ * fields per `docs/cohort-topic.md` §Wire formats — never the `thresholdSig`/`signers` envelope.
8
+ *
9
+ * Determinism comes from encoding an explicitly-ordered JSON array (array order is stable, unlike
10
+ * object key order) as UTF-8. The publisher signs this image; the verifier recomputes and checks it.
11
+ */
12
+
13
+ import type { DemotionNoticeV1, MembershipCertV1, PromotionNoticeV1 } from "../wire/types.js";
14
+
15
+ const utf8 = new TextEncoder();
16
+
17
+ /** Fields of a `MembershipCertV1` covered by its threshold signature (doc: cohortCoord, cohortEpoch, members, stabilizedAt). */
18
+ export type MembershipCertSignable = Pick<MembershipCertV1, "cohortCoord" | "cohortEpoch" | "members" | "stabilizedAt">;
19
+
20
+ /** Canonical signed byte image of a membership certificate. */
21
+ export function membershipCertSigningPayload(cert: MembershipCertSignable): Uint8Array {
22
+ return utf8.encode(JSON.stringify(["MembershipCertV1", cert.cohortCoord, cert.cohortEpoch, cert.members, cert.stabilizedAt]));
23
+ }
24
+
25
+ /** Fields of a `PromotionNoticeV1` covered by its threshold signature. */
26
+ export type PromotionSignable = Pick<PromotionNoticeV1, "topicId" | "fromTier" | "toTier" | "effectiveAt" | "cohortEpoch">;
27
+
28
+ /** Canonical signed byte image of a promotion notice. */
29
+ export function promotionNoticeSigningPayload(n: PromotionSignable): Uint8Array {
30
+ return utf8.encode(JSON.stringify(["PromotionNoticeV1", n.topicId, n.fromTier, n.toTier, n.effectiveAt, n.cohortEpoch]));
31
+ }
32
+
33
+ /** Fields of a `DemotionNoticeV1` covered by its threshold signature. */
34
+ export type DemotionSignable = Pick<DemotionNoticeV1, "topicId" | "tier" | "parentCohortCoord" | "effectiveAt" | "cohortEpoch">;
35
+
36
+ /** Canonical signed byte image of a demotion notice. */
37
+ export function demotionNoticeSigningPayload(n: DemotionSignable): Uint8Array {
38
+ return utf8.encode(JSON.stringify(["DemotionNoticeV1", n.topicId, n.tier, n.parentCohortCoord, n.effectiveAt, n.cohortEpoch]));
39
+ }