keri-ts 2.1.0 → 2.2.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 (294) hide show
  1. package/README.md +30 -2
  2. package/esm/keri/npm/src/acdc/authoring.js +195 -0
  3. package/esm/keri/npm/src/acdc/custody.js +164 -0
  4. package/esm/keri/npm/src/acdc/dhking.js +244 -32
  5. package/esm/keri/npm/src/acdc/regbasing.js +10 -0
  6. package/esm/keri/npm/src/acdc/salting.js +16 -1
  7. package/esm/keri/npm/src/acdc/scheming.js +93 -54
  8. package/esm/keri/npm/src/acdc/unblinding.js +118 -95
  9. package/esm/keri/npm/src/app/bootstrap/authenticating.js +4 -6
  10. package/esm/keri/npm/src/app/bootstrap/oobi-resolving.js +41 -0
  11. package/esm/keri/npm/src/app/bootstrap/oobiery.js +4 -6
  12. package/esm/keri/npm/src/app/credentials/credential-revoking.js +1 -0
  13. package/esm/keri/npm/src/app/credentials/credential-workflows.js +2 -0
  14. package/esm/keri/npm/src/app/credentials/registry-settling.js +44 -0
  15. package/esm/keri/npm/src/app/credentials/root-replicating.js +68 -60
  16. package/esm/keri/npm/src/app/credentials/root-replicator.js +198 -0
  17. package/esm/keri/npm/src/app/credentials/tel-completion.js +36 -0
  18. package/esm/keri/npm/src/app/delivery/forwarding.js +16 -3
  19. package/esm/keri/npm/src/app/exchanges/exchanging.js +14 -4
  20. package/esm/keri/npm/src/app/identity/challenging.js +50 -0
  21. package/esm/keri/npm/src/app/identity/delegating.js +77 -77
  22. package/esm/keri/npm/src/app/identity/delegation-approval.js +333 -0
  23. package/esm/keri/npm/src/app/identity/event-completion.js +154 -0
  24. package/esm/keri/npm/src/app/identity/habbing.js +129 -6
  25. package/esm/keri/npm/src/app/identity/keeping.js +77 -4
  26. package/esm/keri/npm/src/app/ipex/group-ipex.js +327 -0
  27. package/esm/keri/npm/src/app/ipex/group-issuance.js +902 -0
  28. package/esm/keri/npm/src/app/ipex/ipex-admitting.js +120 -0
  29. package/esm/keri/npm/src/app/ipex/ipex-conversation.js +197 -0
  30. package/esm/keri/npm/src/app/ipex/ipex-discloser.js +591 -0
  31. package/esm/keri/npm/src/app/ipex/ipex-granting.js +93 -0
  32. package/esm/keri/npm/src/app/ipex/ipex-grouping.js +34 -11
  33. package/esm/keri/npm/src/app/ipex/issuance.js +888 -0
  34. package/esm/keri/npm/src/app/ipex/pending-presentations.js +46 -0
  35. package/esm/keri/npm/src/app/ipex/presentation-files.js +200 -0
  36. package/esm/keri/npm/src/app/ipex/presenting.js +401 -0
  37. package/esm/keri/npm/src/app/ipex/receive-issuance.js +86 -0
  38. package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +660 -0
  39. package/esm/keri/npm/src/app/mailbox/ipex-arrival.js +103 -0
  40. package/esm/keri/npm/src/app/mailbox/kel-publisher.js +322 -0
  41. package/esm/keri/npm/src/app/mailbox/kel-publishing.js +65 -0
  42. package/esm/keri/npm/src/app/mailbox/kel-submission.js +24 -0
  43. package/esm/keri/npm/src/app/mailbox/mailbox-admin.js +208 -0
  44. package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +212 -47
  45. package/esm/keri/npm/src/app/mailbox/mailboxing.js +10 -5
  46. package/esm/keri/npm/src/app/multisig/counseling.js +482 -0
  47. package/esm/keri/npm/src/app/multisig/group-members.js +18 -0
  48. package/esm/keri/npm/src/app/multisig/multisig-workflows.js +94 -139
  49. package/esm/keri/npm/src/app/multisig/proposals.js +281 -0
  50. package/esm/keri/npm/src/app/multisig/proposing.js +122 -0
  51. package/esm/keri/npm/src/app/notifications/signaling.js +5 -5
  52. package/esm/keri/npm/src/app/protocol/parsering.js +26 -1
  53. package/esm/keri/npm/src/app/protocol/protocol-processing.js +9 -3
  54. package/esm/keri/npm/src/app/queries/query-workflows.js +98 -0
  55. package/esm/keri/npm/src/app/runtime/agent-runtime.js +106 -55
  56. package/esm/keri/npm/src/app/runtime/cue-runtime.js +3 -6
  57. package/esm/keri/npm/src/app/runtime/delivery-outbox.js +87 -0
  58. package/esm/keri/npm/src/app/runtime/doing.js +17 -0
  59. package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +207 -0
  60. package/esm/keri/npm/src/app/runtime/operation-owners.js +151 -0
  61. package/esm/keri/npm/src/app/runtime/retry-backoff.js +93 -0
  62. package/esm/keri/npm/src/app/runtime/workflow-contract.js +80 -0
  63. package/esm/keri/npm/src/app/transport/http-client.js +25 -6
  64. package/esm/keri/npm/src/app/witness/http-endpoints.js +4 -4
  65. package/esm/keri/npm/src/cli/common/event-completion.js +49 -0
  66. package/esm/keri/npm/src/cli/common/runtime-workers.js +23 -0
  67. package/esm/keri/npm/src/cli/credentials/ipex.js +157 -190
  68. package/esm/keri/npm/src/cli/credentials/vc.js +53 -72
  69. package/esm/keri/npm/src/cli/endpoints/did.js +12 -3
  70. package/esm/keri/npm/src/cli/endpoints/oobi.js +10 -13
  71. package/esm/keri/npm/src/cli/identity/challenge.js +8 -34
  72. package/esm/keri/npm/src/cli/identity/delegate.js +81 -258
  73. package/esm/keri/npm/src/cli/identity/incept.js +15 -32
  74. package/esm/keri/npm/src/cli/identity/interact.js +5 -17
  75. package/esm/keri/npm/src/cli/identity/rotate.js +8 -36
  76. package/esm/keri/npm/src/cli/index.js +3 -1
  77. package/esm/keri/npm/src/cli/messaging/group-join.js +79 -0
  78. package/esm/keri/npm/src/cli/messaging/group-wait.js +88 -0
  79. package/esm/keri/npm/src/cli/messaging/multisig.js +163 -284
  80. package/esm/keri/npm/src/cli/messaging/root-copy-lines.js +31 -0
  81. package/esm/keri/npm/src/core/kel/eventing.js +66 -10
  82. package/esm/keri/npm/src/core/mailbox/kram-policy.js +39 -6
  83. package/esm/keri/npm/src/core/mailbox/kraming.js +2 -0
  84. package/esm/keri/npm/src/core/records.js +23 -0
  85. package/esm/keri/npm/src/core/routing.js +13 -0
  86. package/esm/keri/npm/src/db/basing.js +34 -2
  87. package/esm/keri/npm/src/db/core/indexeddb-persistence.js +221 -0
  88. package/esm/keri/npm/src/db/indexeddb.js +98 -0
  89. package/esm/keri/npm/src/db/keeping.js +11 -2
  90. package/esm/keri/npm/src/db/store-lock.js +150 -0
  91. package/esm/keri/npm/src/npm/db-indexeddb.browser.js +2 -0
  92. package/esm/keri/npm/src/npm/db-indexeddb.js +3 -0
  93. package/esm/keri/npm/src/npm/db.browser.js +2 -0
  94. package/esm/keri/npm/src/npm/index.browser.js +9 -0
  95. package/esm/keri/npm/src/npm/runtime-web.browser.js +2 -0
  96. package/esm/keri/npm/src/npm/runtime-web.js +3 -0
  97. package/esm/keri/npm/src/npm/runtime.browser.js +2 -0
  98. package/esm/keri/npm/src/runtime/index.js +51 -4
  99. package/esm/keri/npm/src/runtime/web/output-barrier.js +47 -0
  100. package/esm/keri/npm/src/runtime/web/persistent-storage.js +37 -0
  101. package/esm/keri/npm/src/runtime/web.js +10 -0
  102. package/esm/keri/npm/src/vdr/tel-publication.js +16 -0
  103. package/esm/keri/npm/src/version.js +2 -2
  104. package/package.json +15 -2
  105. package/types/acdc/authoring.d.ts +54 -0
  106. package/types/acdc/authoring.d.ts.map +1 -0
  107. package/types/acdc/custody.d.ts +77 -0
  108. package/types/acdc/custody.d.ts.map +1 -0
  109. package/types/acdc/dhking.d.ts +94 -3
  110. package/types/acdc/dhking.d.ts.map +1 -1
  111. package/types/acdc/regbasing.d.ts +4 -0
  112. package/types/acdc/regbasing.d.ts.map +1 -1
  113. package/types/acdc/salting.d.ts +6 -1
  114. package/types/acdc/salting.d.ts.map +1 -1
  115. package/types/acdc/scheming.d.ts +2 -0
  116. package/types/acdc/scheming.d.ts.map +1 -1
  117. package/types/acdc/unblinding.d.ts +58 -19
  118. package/types/acdc/unblinding.d.ts.map +1 -1
  119. package/types/app/bootstrap/authenticating.d.ts +3 -1
  120. package/types/app/bootstrap/authenticating.d.ts.map +1 -1
  121. package/types/app/bootstrap/oobi-resolving.d.ts +92 -0
  122. package/types/app/bootstrap/oobi-resolving.d.ts.map +1 -0
  123. package/types/app/bootstrap/oobiery.d.ts +3 -1
  124. package/types/app/bootstrap/oobiery.d.ts.map +1 -1
  125. package/types/app/credentials/credential-revoking.d.ts +3 -0
  126. package/types/app/credentials/credential-revoking.d.ts.map +1 -1
  127. package/types/app/credentials/credential-workflows.d.ts +5 -0
  128. package/types/app/credentials/credential-workflows.d.ts.map +1 -1
  129. package/types/app/credentials/registry-settling.d.ts +85 -0
  130. package/types/app/credentials/registry-settling.d.ts.map +1 -0
  131. package/types/app/credentials/root-replicating.d.ts +56 -44
  132. package/types/app/credentials/root-replicating.d.ts.map +1 -1
  133. package/types/app/credentials/root-replicator.d.ts +105 -0
  134. package/types/app/credentials/root-replicator.d.ts.map +1 -0
  135. package/types/app/credentials/tel-completion.d.ts +55 -0
  136. package/types/app/credentials/tel-completion.d.ts.map +1 -0
  137. package/types/app/delivery/forwarding.d.ts +6 -0
  138. package/types/app/delivery/forwarding.d.ts.map +1 -1
  139. package/types/app/exchanges/exchanging.d.ts.map +1 -1
  140. package/types/app/identity/challenging.d.ts +54 -0
  141. package/types/app/identity/challenging.d.ts.map +1 -1
  142. package/types/app/identity/delegating.d.ts +21 -4
  143. package/types/app/identity/delegating.d.ts.map +1 -1
  144. package/types/app/identity/delegation-approval.d.ts +228 -0
  145. package/types/app/identity/delegation-approval.d.ts.map +1 -0
  146. package/types/app/identity/event-completion.d.ts +151 -0
  147. package/types/app/identity/event-completion.d.ts.map +1 -0
  148. package/types/app/identity/habbing.d.ts +67 -2
  149. package/types/app/identity/habbing.d.ts.map +1 -1
  150. package/types/app/identity/keeping.d.ts +49 -3
  151. package/types/app/identity/keeping.d.ts.map +1 -1
  152. package/types/app/ipex/group-ipex.d.ts +247 -0
  153. package/types/app/ipex/group-ipex.d.ts.map +1 -0
  154. package/types/app/ipex/group-issuance.d.ts +281 -0
  155. package/types/app/ipex/group-issuance.d.ts.map +1 -0
  156. package/types/app/ipex/ipex-admitting.d.ts +47 -0
  157. package/types/app/ipex/ipex-admitting.d.ts.map +1 -0
  158. package/types/app/ipex/ipex-conversation.d.ts +195 -0
  159. package/types/app/ipex/ipex-conversation.d.ts.map +1 -0
  160. package/types/app/ipex/ipex-discloser.d.ts +156 -0
  161. package/types/app/ipex/ipex-discloser.d.ts.map +1 -0
  162. package/types/app/ipex/ipex-granting.d.ts +145 -0
  163. package/types/app/ipex/ipex-granting.d.ts.map +1 -0
  164. package/types/app/ipex/ipex-grouping.d.ts +18 -0
  165. package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
  166. package/types/app/ipex/issuance.d.ts +443 -0
  167. package/types/app/ipex/issuance.d.ts.map +1 -0
  168. package/types/app/ipex/pending-presentations.d.ts +25 -0
  169. package/types/app/ipex/pending-presentations.d.ts.map +1 -0
  170. package/types/app/ipex/presentation-files.d.ts +47 -0
  171. package/types/app/ipex/presentation-files.d.ts.map +1 -0
  172. package/types/app/ipex/presenting.d.ts +298 -0
  173. package/types/app/ipex/presenting.d.ts.map +1 -0
  174. package/types/app/ipex/receive-issuance.d.ts +99 -0
  175. package/types/app/ipex/receive-issuance.d.ts.map +1 -0
  176. package/types/app/ipex/unblinding-workflow.d.ts +255 -0
  177. package/types/app/ipex/unblinding-workflow.d.ts.map +1 -0
  178. package/types/app/mailbox/ipex-arrival.d.ts +104 -0
  179. package/types/app/mailbox/ipex-arrival.d.ts.map +1 -0
  180. package/types/app/mailbox/kel-publisher.d.ts +141 -0
  181. package/types/app/mailbox/kel-publisher.d.ts.map +1 -0
  182. package/types/app/mailbox/kel-publishing.d.ts +70 -0
  183. package/types/app/mailbox/kel-publishing.d.ts.map +1 -0
  184. package/types/app/mailbox/kel-submission.d.ts +18 -0
  185. package/types/app/mailbox/kel-submission.d.ts.map +1 -0
  186. package/types/app/mailbox/mailbox-admin.d.ts +113 -0
  187. package/types/app/mailbox/mailbox-admin.d.ts.map +1 -0
  188. package/types/app/mailbox/mailbox-poller.d.ts +92 -3
  189. package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
  190. package/types/app/mailbox/mailboxing.d.ts +3 -0
  191. package/types/app/mailbox/mailboxing.d.ts.map +1 -1
  192. package/types/app/multisig/counseling.d.ts +196 -0
  193. package/types/app/multisig/counseling.d.ts.map +1 -0
  194. package/types/app/multisig/group-members.d.ts +11 -0
  195. package/types/app/multisig/group-members.d.ts.map +1 -1
  196. package/types/app/multisig/grouping.d.ts +39 -20
  197. package/types/app/multisig/grouping.d.ts.map +1 -1
  198. package/types/app/multisig/multisig-workflows.d.ts +91 -22
  199. package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
  200. package/types/app/multisig/proposals.d.ts +129 -0
  201. package/types/app/multisig/proposals.d.ts.map +1 -0
  202. package/types/app/multisig/proposing.d.ts +125 -0
  203. package/types/app/multisig/proposing.d.ts.map +1 -0
  204. package/types/app/notifications/signaling.d.ts +3 -1
  205. package/types/app/notifications/signaling.d.ts.map +1 -1
  206. package/types/app/protocol/parsering.d.ts +13 -0
  207. package/types/app/protocol/parsering.d.ts.map +1 -1
  208. package/types/app/protocol/protocol-processing.d.ts +7 -1
  209. package/types/app/protocol/protocol-processing.d.ts.map +1 -1
  210. package/types/app/queries/query-workflows.d.ts +98 -0
  211. package/types/app/queries/query-workflows.d.ts.map +1 -0
  212. package/types/app/runtime/agent-runtime.d.ts +72 -25
  213. package/types/app/runtime/agent-runtime.d.ts.map +1 -1
  214. package/types/app/runtime/cue-runtime.d.ts +2 -1
  215. package/types/app/runtime/cue-runtime.d.ts.map +1 -1
  216. package/types/app/runtime/delivery-outbox.d.ts +68 -0
  217. package/types/app/runtime/delivery-outbox.d.ts.map +1 -0
  218. package/types/app/runtime/doing.d.ts +27 -0
  219. package/types/app/runtime/doing.d.ts.map +1 -0
  220. package/types/app/runtime/ipex-auxiliaries.d.ts +74 -0
  221. package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -0
  222. package/types/app/runtime/operation-owners.d.ts +137 -0
  223. package/types/app/runtime/operation-owners.d.ts.map +1 -0
  224. package/types/app/runtime/retry-backoff.d.ts +49 -0
  225. package/types/app/runtime/retry-backoff.d.ts.map +1 -0
  226. package/types/app/runtime/workflow-contract.d.ts +79 -0
  227. package/types/app/runtime/workflow-contract.d.ts.map +1 -0
  228. package/types/app/transport/http-client.d.ts.map +1 -1
  229. package/types/cli/common/event-completion.d.ts +26 -0
  230. package/types/cli/common/event-completion.d.ts.map +1 -0
  231. package/types/cli/common/runtime-workers.d.ts +23 -0
  232. package/types/cli/common/runtime-workers.d.ts.map +1 -0
  233. package/types/cli/credentials/ipex.d.ts.map +1 -1
  234. package/types/cli/credentials/vc.d.ts +1 -1
  235. package/types/cli/credentials/vc.d.ts.map +1 -1
  236. package/types/cli/endpoints/did.d.ts.map +1 -1
  237. package/types/cli/endpoints/oobi.d.ts +3 -1
  238. package/types/cli/endpoints/oobi.d.ts.map +1 -1
  239. package/types/cli/identity/challenge.d.ts +1 -1
  240. package/types/cli/identity/challenge.d.ts.map +1 -1
  241. package/types/cli/identity/delegate.d.ts +14 -1
  242. package/types/cli/identity/delegate.d.ts.map +1 -1
  243. package/types/cli/identity/incept.d.ts.map +1 -1
  244. package/types/cli/identity/interact.d.ts.map +1 -1
  245. package/types/cli/identity/rotate.d.ts.map +1 -1
  246. package/types/cli/index.d.ts +3 -1
  247. package/types/cli/index.d.ts.map +1 -1
  248. package/types/cli/messaging/group-join.d.ts +53 -0
  249. package/types/cli/messaging/group-join.d.ts.map +1 -0
  250. package/types/cli/messaging/group-wait.d.ts +34 -0
  251. package/types/cli/messaging/group-wait.d.ts.map +1 -0
  252. package/types/cli/messaging/multisig.d.ts +28 -5
  253. package/types/cli/messaging/multisig.d.ts.map +1 -1
  254. package/types/cli/messaging/root-copy-lines.d.ts +39 -0
  255. package/types/cli/messaging/root-copy-lines.d.ts.map +1 -0
  256. package/types/core/cues.d.ts +11 -0
  257. package/types/core/cues.d.ts.map +1 -1
  258. package/types/core/kel/eventing.d.ts.map +1 -1
  259. package/types/core/mailbox/kram-policy.d.ts +14 -0
  260. package/types/core/mailbox/kram-policy.d.ts.map +1 -1
  261. package/types/core/mailbox/kraming.d.ts +1 -1
  262. package/types/core/mailbox/kraming.d.ts.map +1 -1
  263. package/types/core/records.d.ts +61 -0
  264. package/types/core/records.d.ts.map +1 -1
  265. package/types/core/routing.d.ts +6 -0
  266. package/types/core/routing.d.ts.map +1 -1
  267. package/types/db/basing.d.ts +8 -2
  268. package/types/db/basing.d.ts.map +1 -1
  269. package/types/db/core/indexeddb-persistence.d.ts +59 -0
  270. package/types/db/core/indexeddb-persistence.d.ts.map +1 -0
  271. package/types/db/indexeddb.d.ts +37 -0
  272. package/types/db/indexeddb.d.ts.map +1 -0
  273. package/types/db/keeping.d.ts +5 -2
  274. package/types/db/keeping.d.ts.map +1 -1
  275. package/types/db/store-lock.d.ts +72 -0
  276. package/types/db/store-lock.d.ts.map +1 -0
  277. package/types/npm/db-indexeddb.d.ts +4 -0
  278. package/types/npm/db-indexeddb.d.ts.map +1 -0
  279. package/types/npm/runtime-web.d.ts +4 -0
  280. package/types/npm/runtime-web.d.ts.map +1 -0
  281. package/types/runtime/index.d.ts +41 -6
  282. package/types/runtime/index.d.ts.map +1 -1
  283. package/types/runtime/web/output-barrier.d.ts +30 -0
  284. package/types/runtime/web/output-barrier.d.ts.map +1 -0
  285. package/types/runtime/web/persistent-storage.d.ts +36 -0
  286. package/types/runtime/web/persistent-storage.d.ts.map +1 -0
  287. package/types/runtime/web.d.ts +11 -0
  288. package/types/runtime/web.d.ts.map +1 -0
  289. package/types/vdr/tel-publication.d.ts +6 -0
  290. package/types/vdr/tel-publication.d.ts.map +1 -1
  291. package/types/version.d.ts +2 -2
  292. package/esm/keri/npm/src/cli/common/tel-completion.js +0 -24
  293. package/types/cli/common/tel-completion.d.ts +0 -8
  294. package/types/cli/common/tel-completion.d.ts.map +0 -1
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Group-event Counselor: drives an accepted-or-pending group KEL event through
3
+ * member signatures and witness receipts, as KERIpy `app.grouping.Counselor`.
4
+ *
5
+ * Durable state is KERIpy's, row for row: `gpse` and `gpwe` hold `(pre) ->
6
+ * IoSet[(Number, Diger)]`, `gdee` holds delegated events awaiting the delegator
7
+ * and `cgms` holds `(pre, number.qb64) -> Diger` for completed events. The
8
+ * Counselor never signs; it only observes accepted state and, for witness
9
+ * receipts, asks the injected receipting collaborator to submit or fetch.
10
+ *
11
+ * Divergences from KERIpy, each deliberate:
12
+ * - An escrow row is removed by exact `(Number, Diger)` tuple. KERIpy removes
13
+ * every row of the prefix and never compares digests; here a different SAID
14
+ * accepted at the same sn drops only the stale tuple and reads `superseded`.
15
+ * - Receipts are judged against the event's own governing establishment, not
16
+ * the prefix's current key state.
17
+ * - "Must submit" derives from stored receipts and the election, not from an
18
+ * in-memory cue, so a restarted process resumes. Non-elected members fetch
19
+ * receipts and, after `fallbackAfterMs`, submit as the elected one would;
20
+ * KERIpy stalls when the elected member is offline.
21
+ * - A newer completion at the same `(pre, sn)` replaces the `cgms` digest.
22
+ * - A delegated `dip`/`drt` is accepted by its own delegate before the delegator's
23
+ * anchor and before witnesses receipt it (KERIpy `validateDelegation`: the
24
+ * delegatee accepts its own event to trigger receipting and approval). It
25
+ * waits in `gdee`. The publishing member (elected, or any member once the
26
+ * fallback grace passes) hands it to the Anchorer, which requests approval,
27
+ * awaits the anchor and publishes; the Counselor gates and observes, and
28
+ * writes `cgms` when the Anchorer completes. Any other member queries the
29
+ * delegator's witness for the anchor, pins `aess`, and continues in `gpwe`.
30
+ * Without an Anchorer such events stay in `gdee`, reported `notSupported`.
31
+ *
32
+ * Witness authorization codes, the delegation proxy and retry timers are process
33
+ * memory only, as in KERIpy and the Anchorer. Without a proxy the local group
34
+ * member sends delegation traffic, as KERIpy does for every group event, so a
35
+ * restart never leaves a delegated event waiting for one. Collaborators are
36
+ * narrow (ADR-0013); nothing here reaches a runtime.
37
+ */
38
+ import { Diger, Ilks, NumberPrimitive, NumDex, Verfer } from "cesr-ts";
39
+ import { ValidationError } from "../../core/errors.js";
40
+ import { Kever } from "../../core/kel/kever.js";
41
+ import { dgKey } from "../../db/core/keys.js";
42
+ import { pinAuthorizingSeal, retryDelegatorWitnessQuery, } from "../identity/delegating.js";
43
+ import { observeUntil } from "../runtime/workflow-contract.js";
44
+ import { groupWitnesser, isLocalGroupHab, localGroupMember } from "./group-members.js";
45
+ /** Grace before a non-elected member submits receipts itself. */
46
+ export const DEFAULT_FALLBACK_AFTER_MS = 60_000;
47
+ /** First and longest wait between receipt attempts for one event. */
48
+ export const RECEIPT_BACKOFF_MS = { first: 1_000, max: 30_000 };
49
+ /** The production receipt retry delays as a policy; only `baseMs` and `maxMs` apply, the Counselor never gives up. */
50
+ export const DEFAULT_RECEIPT_RETRY = Object.freeze({
51
+ baseMs: RECEIPT_BACKOFF_MS.first,
52
+ maxMs: RECEIPT_BACKOFF_MS.max,
53
+ boundMs: 0,
54
+ });
55
+ /** Escrow tables in stage order. */
56
+ const STAGES = [["signatures", "gpse"], ["delegation", "gdee"], ["witnesses", "gpwe"]];
57
+ /** Receipt progress of one event against its own governing establishment. */
58
+ function receiptProgress(db, serder) {
59
+ let est = serder;
60
+ while (est && !est.estive)
61
+ est = est.prior ? db.getEvtSerder(est.pre, est.prior) : null;
62
+ if (!est)
63
+ return { have: 0, need: 0, missing: [] };
64
+ const witnesses = db.wits.get(dgKey(est.pre, est.said)).map(wit => wit.qb64);
65
+ const { indices } = Kever.verifyIndexedSignatures(serder.raw, db.wigs.get(dgKey(serder.pre, serder.said)), witnesses.map(qb64 => new Verfer({ qb64 })));
66
+ return {
67
+ have: indices.length,
68
+ need: Number(est.bner.num),
69
+ missing: witnesses.filter((_, index) => !indices.includes(index)),
70
+ };
71
+ }
72
+ /** Counsels group events through signature and witness stages. */
73
+ export class Counselor {
74
+ /**
75
+ * @param hby Borrowed identities and stores; the Counselor opens nothing.
76
+ * @param receipting Submits or fetches witness receipts for accepted events.
77
+ * @param clock Time source for fallback and retry; only `now` is used.
78
+ * @param changes Optional sink told `{kind:"group", id:"<pre>:<sn>"}` after commits.
79
+ * @param delegation Anchorer and query port for delegated events; omit for non-delegated hosts.
80
+ * @param mailboxes Told when one of the group's rotations completes; the member runtime's publisher then tells
81
+ * the group's mailboxes the new key state, and several members doing so is harmless.
82
+ */
83
+ constructor(hby, receipting, clock, changes, delegation, mailboxes, receiptRetry = DEFAULT_RECEIPT_RETRY) {
84
+ Object.defineProperty(this, "hby", {
85
+ enumerable: true,
86
+ configurable: true,
87
+ writable: true,
88
+ value: hby
89
+ });
90
+ Object.defineProperty(this, "receipting", {
91
+ enumerable: true,
92
+ configurable: true,
93
+ writable: true,
94
+ value: receipting
95
+ });
96
+ Object.defineProperty(this, "clock", {
97
+ enumerable: true,
98
+ configurable: true,
99
+ writable: true,
100
+ value: clock
101
+ });
102
+ Object.defineProperty(this, "changes", {
103
+ enumerable: true,
104
+ configurable: true,
105
+ writable: true,
106
+ value: changes
107
+ });
108
+ Object.defineProperty(this, "delegation", {
109
+ enumerable: true,
110
+ configurable: true,
111
+ writable: true,
112
+ value: delegation
113
+ });
114
+ Object.defineProperty(this, "mailboxes", {
115
+ enumerable: true,
116
+ configurable: true,
117
+ writable: true,
118
+ value: mailboxes
119
+ });
120
+ Object.defineProperty(this, "receiptRetry", {
121
+ enumerable: true,
122
+ configurable: true,
123
+ writable: true,
124
+ value: receiptRetry
125
+ });
126
+ Object.defineProperty(this, "runs", {
127
+ enumerable: true,
128
+ configurable: true,
129
+ writable: true,
130
+ value: new Map()
131
+ });
132
+ Object.defineProperty(this, "anchorQuery", {
133
+ enumerable: true,
134
+ configurable: true,
135
+ writable: true,
136
+ value: void 0
137
+ });
138
+ this.anchorQuery = delegation && { hby, querying: delegation.querying, retryPasses: new Map() };
139
+ }
140
+ /**
141
+ * Begin counseling an event. Idempotent and safe before local acceptance.
142
+ *
143
+ * @returns True when a new `gpse` row was added; false when the event is
144
+ * already counseled or complete, or another SAID holds that sn.
145
+ * @throws ValidationError when `pre` is not a local group or the ref is malformed.
146
+ */
147
+ start(event, inputs = {}) {
148
+ const { pre, sn, said } = event;
149
+ if (!Number.isSafeInteger(sn) || sn < 0 || !said)
150
+ throw new ValidationError("Invalid group event reference.");
151
+ const hab = this.hby.habs.get(pre);
152
+ if (!hab || !isLocalGroupHab(this.hby, hab))
153
+ throw new ValidationError(`${pre} is not a local group.`);
154
+ const db = this.hby.db;
155
+ if (this.settled(event))
156
+ return false; // nothing to counsel, so nothing to retain
157
+ this.retain(event, inputs);
158
+ if (this.stageOf(event))
159
+ return false;
160
+ return db.gpse.add([pre], [NumberPrimitive.fromNum(BigInt(sn)), new Diger({ qb64: said })]);
161
+ }
162
+ /**
163
+ * Replace in-memory inputs of a counseled event, for example witness auths or
164
+ * a proxy after a restart, and re-arm any attempt waiting out its backoff.
165
+ * Ignored for an event that is already complete or superseded.
166
+ */
167
+ supply(event, inputs) {
168
+ if (!this.settled(event))
169
+ this.retain(event, inputs);
170
+ }
171
+ /** Pure read of one event's state. */
172
+ status(event) {
173
+ const { pre, sn, said } = event;
174
+ const done = this.completed(pre, sn);
175
+ if (done !== null)
176
+ return done === said ? { state: "complete" } : { state: "superseded", accepted: done };
177
+ const accepted = this.hby.db.kels.getLast(pre, sn);
178
+ if (accepted && accepted !== said)
179
+ return { state: "superseded", accepted };
180
+ const stage = this.stageOf(event);
181
+ if (!stage)
182
+ return { state: "unknown" };
183
+ // A counseled event may not be stored locally yet; it is still pending, not unknown.
184
+ const serder = this.hby.db.getEvtSerder(pre, said);
185
+ if (!serder)
186
+ return { state: "pending", stage, elected: null, receipts: { have: 0, need: 0 } };
187
+ const { have, need, missing } = receiptProgress(this.hby.db, serder);
188
+ const elected = accepted ? groupWitnesser(this.hby, pre, { sn, said }) : null;
189
+ const status = { state: "pending", stage, elected, receipts: { have, need } };
190
+ const run = this.runs.get(this.runKey(event));
191
+ const failure = run?.failure ? this.receiptBlock(run, missing) : undefined;
192
+ if (stage === "delegation")
193
+ status.blocked = this.delegationBlock(event) ?? failure;
194
+ else if (stage === "witnesses")
195
+ status.blocked = failure;
196
+ if (!status.blocked)
197
+ delete status.blocked;
198
+ return status;
199
+ }
200
+ /** Why a receipt attempt failed: missing auths for unreceipted witnesses, else the witness's own reason. */
201
+ receiptBlock(run, missing) {
202
+ const unauthorized = missing.filter(witness => !(witness in run.auths));
203
+ return unauthorized.length
204
+ ? { kind: "witnessAuth", witnesses: unauthorized }
205
+ : { kind: "witness", reason: run.failure };
206
+ }
207
+ /** What keeps a delegated event in `gdee`, if anything the caller can fix. */
208
+ delegationBlock(event) {
209
+ if (!this.delegation)
210
+ return { kind: "notSupported", feature: "delegation" };
211
+ const delpre = this.hby.db.getKever(event.pre)?.delpre;
212
+ if (delpre && !this.hby.db.getKever(delpre))
213
+ return { kind: "delegatorUnknown", delpre };
214
+ return undefined;
215
+ }
216
+ fallbackElapsed(run) {
217
+ return !!run && run.since !== null && this.clock.now() - run.since >= run.fallbackAfterMs;
218
+ }
219
+ /** True when the event is complete, or another SAID holds its sn. */
220
+ settled({ pre, sn, said }) {
221
+ const accepted = this.hby.db.kels.getLast(pre, sn);
222
+ return (!!accepted && accepted !== said) || this.completed(pre, sn) !== null;
223
+ }
224
+ /** Events still counseled for `pre`, or for every prefix, in stage order. */
225
+ pending(pre) {
226
+ const found = [];
227
+ for (const [stage, table] of STAGES) {
228
+ for (const [[key], [number, diger]] of this.hby.db[table].getTopItemIter(pre ?? "", { topive: !!pre })) {
229
+ found.push({ pre: key, sn: Number(number.num), said: diger.qb64, stage });
230
+ }
231
+ }
232
+ return found;
233
+ }
234
+ /** True once the exact event completed; drops its in-memory inputs. */
235
+ complete(event) {
236
+ const done = this.completed(event.pre, event.sn) === event.said;
237
+ if (done)
238
+ this.runs.delete(this.runKey(event));
239
+ return done;
240
+ }
241
+ /**
242
+ * Stop counseling `pre`: remove its escrow rows, completion markers and
243
+ * in-memory inputs. Accepted KEL state is untouched.
244
+ */
245
+ abandon(pre) {
246
+ const db = this.hby.db;
247
+ const removed = new Set(this.pending(pre).map(event => event.sn));
248
+ for (const [[, number]] of db.cgms.getTopItemIter([pre], { topive: true })) {
249
+ removed.add(Number(new NumberPrimitive({ qb64: number }).num));
250
+ }
251
+ db.transaction(() => {
252
+ for (const [, table] of STAGES)
253
+ db[table].rem([pre]);
254
+ for (const [keys] of [...db.cgms.getTopItemIter([pre], { topive: true })])
255
+ db.cgms.rem(keys);
256
+ });
257
+ for (const key of [...this.runs.keys()])
258
+ if (key.startsWith(`${pre}:`))
259
+ this.runs.delete(key);
260
+ for (const sn of removed)
261
+ this.changes?.push({ kind: "group", id: `${pre}:${sn}` });
262
+ }
263
+ /** One pass over all stages, as KERIpy `processEscrows`. */
264
+ *processOnce() {
265
+ // A runtime calls this every turn; with nothing counseled, three row counts are the whole cost.
266
+ const db = this.hby.db;
267
+ if (db.gpse.cnt() === 0 && db.gdee.cnt() === 0 && db.gpwe.cnt() === 0)
268
+ return;
269
+ this.processSignatures();
270
+ yield* this.processDelegation();
271
+ yield* this.processWitnesses();
272
+ }
273
+ /** Move events accepted with their escrowed SAID on to delegation or witnesses. */
274
+ processSignatures() {
275
+ for (const [[pre], [number, diger]] of [...this.hby.db.gpse.getTopItemIter()]) {
276
+ const event = this.refOf(pre, number, diger);
277
+ if (this.dropIfSuperseded(this.hby.db.gpse, event, number, diger))
278
+ continue;
279
+ if (this.hby.db.kels.getLast(pre, event.sn) !== event.said)
280
+ continue;
281
+ const ilk = this.hby.db.getEvtSerder(pre, event.said)?.ilk;
282
+ const next = ilk === Ilks.dip || ilk === Ilks.drt ? this.hby.db.gdee : this.hby.db.gpwe;
283
+ this.hby.db.transaction(() => {
284
+ this.hby.db.gpse.rem([pre], [number, diger]);
285
+ next.add([pre], [number, diger]);
286
+ });
287
+ this.changes?.push({ kind: "group", id: `${pre}:${event.sn}` });
288
+ }
289
+ }
290
+ /**
291
+ * Delegated events: the publishing member drives the Anchorer, any other member
292
+ * watches for the delegator's anchor. Both wait for the delegator's KEL.
293
+ */
294
+ *processDelegation() {
295
+ const db = this.hby.db;
296
+ for (const [[pre], [number, diger]] of [...db.gdee.getTopItemIter()]) {
297
+ const event = this.refOf(pre, number, diger);
298
+ if (this.dropIfSuperseded(db.gdee, event, number, diger))
299
+ continue;
300
+ const serder = db.getEvtSerder(pre, event.said);
301
+ const delpre = db.getKever(pre)?.delpre;
302
+ if (!this.delegation || !serder || !delpre || !db.getKever(delpre))
303
+ continue;
304
+ const run = this.retain(event, {});
305
+ run.since ??= this.clock.now();
306
+ const member = localGroupMember(this.hby, pre);
307
+ if (groupWitnesser(this.hby, pre, event) || this.fallbackElapsed(run)) {
308
+ yield* this.publishDelegation(event, serder, run, member, [number, diger]);
309
+ }
310
+ else {
311
+ this.watchAnchor(event, serder, run, member, delpre, [number, diger]);
312
+ }
313
+ }
314
+ }
315
+ /** Publishing duty: begin the Anchorer, keep receipts coming, and record completion. */
316
+ *publishDelegation(event, serder, run, member, row) {
317
+ const { anchorer } = this.delegation;
318
+ if (anchorer.complete(event.pre, event.sn)) {
319
+ this.finish(this.hby.db.gdee, event, row);
320
+ return;
321
+ }
322
+ // The Anchorer asks the delegator only once the event's witnesses have receipted it.
323
+ if (!this.hby.db.fullyWitnessed(serder))
324
+ yield* this.pursueReceipts(event, serder, true);
325
+ if (!run.begun) {
326
+ anchorer.begin(serder, { communicationHab: run.proxy ?? member });
327
+ run.begun = true;
328
+ this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
329
+ }
330
+ }
331
+ /** Non-publishing duty: find the delegator's sealing event, pin it, and move on to receipts. */
332
+ watchAnchor(event, serder, run, member, delpre, row) {
333
+ const db = this.hby.db;
334
+ retryDelegatorWitnessQuery(this.anchorQuery, serder, run.proxy ?? member);
335
+ const delegating = db.fetchLastSealingEventByEventSeal(delpre, { i: event.pre, s: serder.snh, d: event.said });
336
+ if (!delegating)
337
+ return;
338
+ pinAuthorizingSeal(this.hby, serder, delegating);
339
+ db.transaction(() => {
340
+ db.gdee.rem([event.pre], row);
341
+ db.gpwe.add([event.pre], row);
342
+ });
343
+ this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
344
+ }
345
+ /** Record completion of a counseled event and drop its row from `table`. */
346
+ finish(table, event, row) {
347
+ const db = this.hby.db;
348
+ db.transaction(() => {
349
+ table.rem([event.pre], row);
350
+ db.cgms.pin([event.pre, row[0].qb64], row[1]);
351
+ });
352
+ this.runs.delete(this.runKey(event));
353
+ this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
354
+ const ilk = db.getEvtSerder(event.pre, event.said)?.ilk;
355
+ if (ilk === Ilks.rot || ilk === Ilks.drt) {
356
+ this.mailboxes?.rotated({ kin: "rotated", pre: event.pre, sn: event.sn, said: event.said });
357
+ }
358
+ }
359
+ /** Complete events whose own witness threshold is met; otherwise submit or fetch receipts. */
360
+ *processWitnesses() {
361
+ const db = this.hby.db;
362
+ for (const [[pre], [number, diger]] of [...db.gpwe.getTopItemIter()]) {
363
+ const event = this.refOf(pre, number, diger);
364
+ if (this.dropIfSuperseded(db.gpwe, event, number, diger))
365
+ continue;
366
+ const serder = db.getEvtSerder(pre, event.said);
367
+ if (!serder)
368
+ continue;
369
+ if (!db.fullyWitnessed(serder)) {
370
+ yield* this.pursueReceipts(event, serder);
371
+ if (!db.fullyWitnessed(serder))
372
+ continue;
373
+ }
374
+ this.finish(db.gpwe, event, [number, diger]);
375
+ }
376
+ }
377
+ /** One backed-off receipt attempt: submit when forced, elected or past the grace, else fetch. */
378
+ *pursueReceipts(event, serder, forceSubmit = false) {
379
+ const run = this.retain(event, {});
380
+ const now = this.clock.now();
381
+ run.since ??= now;
382
+ if (now < run.nextAt)
383
+ return;
384
+ const submit = forceSubmit || groupWitnesser(this.hby, event.pre, event) || now - run.since >= run.fallbackAfterMs;
385
+ try {
386
+ if (submit)
387
+ yield* this.receipting.receipt(event.pre, { sn: event.sn, auths: run.auths });
388
+ else
389
+ yield* this.receipting.get(event.pre, { sn: event.sn, said: event.said });
390
+ run.failure = null;
391
+ run.delay = this.receiptRetry.baseMs;
392
+ }
393
+ catch (error) {
394
+ // A witness refusing or being unreachable is a remote outcome: report it in status and retry later.
395
+ run.failure = error instanceof Error ? error.message : String(error);
396
+ run.delay = Math.min(run.delay * 2, this.receiptRetry.maxMs);
397
+ }
398
+ run.nextAt = now + run.delay;
399
+ if (!this.hby.db.fullyWitnessed(serder))
400
+ this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
401
+ }
402
+ /** Remove a stale tuple when another SAID holds its sn; true when removed. */
403
+ dropIfSuperseded(table, event, number, diger) {
404
+ const accepted = this.hby.db.kels.getLast(event.pre, event.sn);
405
+ if (!accepted || accepted === event.said)
406
+ return false;
407
+ table.rem([event.pre], [number, diger]);
408
+ this.runs.delete(this.runKey(event));
409
+ this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
410
+ return true;
411
+ }
412
+ /** Digest of the completed event at `(pre, sn)`, under either number width KERIpy writes. */
413
+ completed(pre, sn) {
414
+ const cgms = this.hby.db.cgms;
415
+ const auto = NumberPrimitive.fromNum(BigInt(sn));
416
+ const huge = new NumberPrimitive({ code: NumDex.Huge, raw: hugeRaw(sn) });
417
+ return (cgms.get([pre, auto.qb64]) ?? cgms.get([pre, huge.qb64]))?.qb64 ?? null;
418
+ }
419
+ /** Stage whose escrow holds exactly this event, if any. */
420
+ stageOf({ pre, sn, said }) {
421
+ for (const [stage, table] of STAGES) {
422
+ for (const [number, diger] of this.hby.db[table].get([pre])) {
423
+ if (Number(number.num) === sn && diger.qb64 === said)
424
+ return stage;
425
+ }
426
+ }
427
+ return null;
428
+ }
429
+ refOf(pre, number, diger) {
430
+ return { pre, sn: Number(number.num), said: diger.qb64 };
431
+ }
432
+ runKey({ pre, sn, said }) {
433
+ return `${pre}:${sn}:${said}`;
434
+ }
435
+ /** Get or create the in-memory run for an event, applying supplied inputs. */
436
+ retain(event, { auths, fallbackAfterMs, proxy }) {
437
+ if (fallbackAfterMs !== undefined && !(fallbackAfterMs >= 0 && Number.isFinite(fallbackAfterMs))) {
438
+ throw new ValidationError("fallbackAfterMs must be a finite nonnegative duration.");
439
+ }
440
+ const key = this.runKey(event);
441
+ const run = this.runs.get(key)
442
+ ?? {
443
+ auths: {},
444
+ fallbackAfterMs: DEFAULT_FALLBACK_AFTER_MS,
445
+ since: null,
446
+ nextAt: 0,
447
+ delay: this.receiptRetry.baseMs,
448
+ failure: null,
449
+ proxy: null,
450
+ begun: false,
451
+ };
452
+ if (proxy) {
453
+ run.proxy = proxy;
454
+ run.begun = false; // the Anchorer pins its sender per begin, so re-pin
455
+ }
456
+ if (auths) {
457
+ run.auths = { ...auths };
458
+ run.nextAt = 0; // New credentials re-arm an attempt that was waiting out its backoff.
459
+ }
460
+ if (fallbackAfterMs !== undefined)
461
+ run.fallbackAfterMs = fallbackAfterMs;
462
+ this.runs.set(key, run);
463
+ return run;
464
+ }
465
+ }
466
+ /** Big-endian 16-byte raw for the `0A` (Huge) number code. */
467
+ function hugeRaw(sn) {
468
+ const raw = new Uint8Array(16);
469
+ for (let at = 15, value = BigInt(sn); at >= 0; at--, value >>= 8n)
470
+ raw[at] = Number(value & 0xffn);
471
+ return raw;
472
+ }
473
+ /**
474
+ * Observe `event` until it is no longer pending or the deadline passes. Starts
475
+ * no worker and makes no receipt call; progress comes from `processOnce`
476
+ * running elsewhere.
477
+ */
478
+ export function* waitForGroupEvent(counselor, event, { timeoutMs, clock }) {
479
+ const done = yield* observeUntil(() => counselor.status(event).state !== "pending", { timeoutMs, clock });
480
+ const status = counselor.status(event);
481
+ return done ? { outcome: "settled", status } : { outcome: "timed-out", status, timeoutMs };
482
+ }
@@ -78,6 +78,24 @@ export function groupElectedMember(hby, group, coordinator) {
78
78
  return null;
79
79
  return groupSigningMembers(hby, group).find(member => latestGroupContribution(hby.db, member, selection.establishment.verfers)?.index === selection.index) ?? null;
80
80
  }
81
+ /**
82
+ * True when the local member is the witnesser for the exact accepted group event
83
+ * `coordinator`, as KERIpy `_groupWitnesser`: the member's latest contribution to
84
+ * the governing establishment's keys holds the lowest verified signing index.
85
+ *
86
+ * Unlike `groupElectedMember` this does not require that member to be a current
87
+ * signing member, so a historical event keeps its witnesser after a later
88
+ * rotation. Like it, the answer depends on locally stored signatures and suits
89
+ * idempotent duties only.
90
+ */
91
+ export function groupWitnesser(hby, group, coordinator) {
92
+ const selection = groupWitnesserSelection(hby.db, group, coordinator);
93
+ if (!selection)
94
+ return false;
95
+ const member = localGroupMember(hby, group).pre;
96
+ const contribution = member ? latestGroupContribution(hby.db, member, selection.establishment.verfers) : null;
97
+ return contribution?.index === selection.index;
98
+ }
81
99
  /** Return current group signing member AIDs in signing-index order. */
82
100
  export function groupSigningMembers(hby, groupPre) {
83
101
  const stored = hby.ks.getSmids(groupPre).map((tuple) => tuple[0].qb64);