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,327 @@
1
+ /**
2
+ * Runtime-owned operations for a local group's v2 IPEX messages (first user: a group holder's Admit).
3
+ *
4
+ * A group-authored IPEX message completes only when enough members have contributed a signature, and each member's
5
+ * signature is that member's consent. The runtime owns the rest. This module follows the workflow contract
6
+ * (`app/runtime/workflow-contract.ts`):
7
+ *
8
+ * - {@link proposeGroupAdmit} / {@link proposeGroupIpex} (the proposer) and {@link approveGroupIpex} (every other
9
+ * member) are the explicit consents. Each signs or reuses this member's contribution, accepts it locally, records
10
+ * a live operation and returns without waiting on anyone. {@link groupIpexProposals} lists the stored proposals a
11
+ * member may approve.
12
+ * - `processOnce`, driven by the runtime's owner loop, publishes this member's wrapper to the other members (retrying
13
+ * a member that is briefly offline), collects the other members' stored contributions, and, once the message is
14
+ * accepted, lets the elected lead send the completed message to its recipient. It signs nothing beyond the
15
+ * contribution that the consent recorded.
16
+ * - {@link groupIpexStatus} is a pure read and {@link waitForGroupIpex} only observes.
17
+ *
18
+ * Status is derived from the protocol stores: an accepted message reads `done`; a stored proposal without a live
19
+ * operation reads `awaitingConsent` (a member that reloaded consents again with the same call and reuses its
20
+ * recorded signature). Lost on reload, by design: whether the lead had already delivered the completed message,
21
+ * the `failed` and `abandoned` reasons, and the publication results. The runtime never writes wallet inventory; the
22
+ * embedder reacts to the pushed `{kind: "ipex", id: <message>}` change.
23
+ */
24
+ import { concatBytes, Counter, Vrsn_2_0 } from "cesr-ts";
25
+ import { ValidationError } from "../../core/errors.js";
26
+ import { registryEventStatus, settleRegistryEvent } from "../credentials/registry-settling.js";
27
+ import { isLocalGroupHab } from "../multisig/group-members.js";
28
+ import { MULTISIG_EXN_ROUTE } from "../multisig/grouping.js";
29
+ import { RetryBackoff } from "../runtime/retry-backoff.js";
30
+ import { observeUntil } from "../runtime/workflow-contract.js";
31
+ import { IpexAdmitter } from "./ipex-admitting.js";
32
+ import { approveMultisigIpexLocally, approveStoredMultisigIpexWrappers, findStoredMultisigIpex, groupIpexApproved, prepareAcdcV2GroupIpex, publishGroupIpexProposal, publishInitialGroupIpex, requireAcdcV2GroupUse, sendCompletedGroupIpex, storedMultisigIpexBody, } from "./ipex-grouping.js";
33
+ /** Milliseconds between collections of the other members' stored contributions. */
34
+ export const GROUP_IPEX_COLLECT_MS = 250;
35
+ /** How long delivery failures may last before an operation ends `failed`; a group IPEX message has no deadline. */
36
+ export const GROUP_IPEX_RETRY_BOUND_MS = 600_000;
37
+ function message(error) {
38
+ return error instanceof Error ? error.message : String(error);
39
+ }
40
+ /** The live table of group IPEX operations and its driver; one per host runtime. */
41
+ export class GroupIpexOperations {
42
+ constructor(deps) {
43
+ Object.defineProperty(this, "deps", {
44
+ enumerable: true,
45
+ configurable: true,
46
+ writable: true,
47
+ value: deps
48
+ });
49
+ Object.defineProperty(this, "live", {
50
+ enumerable: true,
51
+ configurable: true,
52
+ writable: true,
53
+ value: new Map()
54
+ });
55
+ Object.defineProperty(this, "ended", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: new Map()
60
+ });
61
+ /** What accepted operations published and delivered in this process; not durable. */
62
+ Object.defineProperty(this, "outcomes", {
63
+ enumerable: true,
64
+ configurable: true,
65
+ writable: true,
66
+ value: new Map()
67
+ });
68
+ }
69
+ /** Whether a pass has work. */
70
+ active() {
71
+ return this.live.size > 0;
72
+ }
73
+ /** Advance every live operation by one pass. */
74
+ *processOnce() {
75
+ for (const op of [...this.live.values()]) {
76
+ if (op.busy || !this.live.has(op.said))
77
+ continue;
78
+ op.busy = true;
79
+ try {
80
+ yield* this.step(op);
81
+ }
82
+ catch (error) {
83
+ this.end(op, "failed", message(error));
84
+ }
85
+ finally {
86
+ op.busy = false;
87
+ }
88
+ }
89
+ }
90
+ /** End a live operation and tell the sink. */
91
+ end(op, state, reason) {
92
+ this.live.delete(op.said);
93
+ this.ended.set(op.said, { state, reason });
94
+ this.deps.changes?.push({ kind: "ipex", id: op.said });
95
+ }
96
+ /** Record an operation the user consented to. */
97
+ begin(said, group, genus, outbound, deliver = true, prerequisite = null) {
98
+ this.ended.delete(said);
99
+ this.live.set(said, {
100
+ said,
101
+ group,
102
+ genus,
103
+ phase: prerequisite ? "awaitingRegistryUpdate" : outbound ? "publishing" : "awaitingMembers",
104
+ outbound,
105
+ prerequisite,
106
+ publication: null,
107
+ delivered: !deliver,
108
+ deliveries: [],
109
+ retry: new RetryBackoff(),
110
+ nextCollect: 0,
111
+ busy: false,
112
+ });
113
+ this.deps.changes?.push({ kind: "ipex", id: said });
114
+ return { said };
115
+ }
116
+ setPhase(op, phase) {
117
+ if (op.phase === phase)
118
+ return;
119
+ op.phase = phase;
120
+ this.deps.changes?.push({ kind: "ipex", id: op.said });
121
+ }
122
+ /** Run a delivery; a failure keeps the work for a later pass until failures have lasted the bound. */
123
+ *attempt(op, send) {
124
+ const now = this.deps.clock.now();
125
+ if (!op.retry.due(now))
126
+ return false;
127
+ try {
128
+ yield* send();
129
+ op.retry.succeeded();
130
+ return true;
131
+ }
132
+ catch (error) {
133
+ op.retry.failed(now, error);
134
+ if (op.retry.exhausted(now, GROUP_IPEX_RETRY_BOUND_MS)) {
135
+ throw new ValidationError(`Delivery kept failing: ${message(error)}`);
136
+ }
137
+ return false;
138
+ }
139
+ }
140
+ *step(op) {
141
+ const { hby, protocol, postman, exc, rgy, clock } = this.deps;
142
+ if (op.prerequisite) {
143
+ const read = { db: hby.db, store: rgy.store, protocol };
144
+ const { anchor, proceed } = op.prerequisite;
145
+ if (registryEventStatus(read, anchor).state !== "accepted") {
146
+ if ((yield* settleRegistryEvent(read, anchor)).state !== "accepted")
147
+ return;
148
+ }
149
+ op.outbound = { kind: "initial", wire: yield* proceed() };
150
+ op.prerequisite = null;
151
+ this.setPhase(op, "publishing");
152
+ }
153
+ if (op.outbound) {
154
+ const { kind, wire } = op.outbound;
155
+ const sent = yield* this.attempt(op, function* () {
156
+ op.publication = kind === "initial"
157
+ ? yield* publishInitialGroupIpex(hby, protocol, postman, op.group, wire, op.genus)
158
+ : yield* publishGroupIpexProposal(hby, protocol, postman, op.group, wire, op.genus);
159
+ });
160
+ if (!sent)
161
+ return;
162
+ op.outbound = null;
163
+ this.setPhase(op, "awaitingMembers");
164
+ }
165
+ if (!exc.complete(op.said) && clock.now() >= op.nextCollect) {
166
+ op.nextCollect = clock.now() + GROUP_IPEX_COLLECT_MS;
167
+ // This operation exists only because this member consented, so collecting reuses its recorded contribution.
168
+ yield* approveStoredMultisigIpexWrappers(hby, protocol, op.said, null);
169
+ }
170
+ if (!exc.complete(op.said))
171
+ return;
172
+ const embedded = hby.db.exns.get([op.said]);
173
+ requireAcdcV2GroupUse(hby, protocol, embedded);
174
+ if (!op.delivered) {
175
+ if (exc.lead(op.group, op.said)) {
176
+ this.setPhase(op, "delivering");
177
+ const sent = yield* this.attempt(op, function* () {
178
+ op.deliveries = yield* sendCompletedGroupIpex(hby, postman, null, op.group, embedded, op.genus, rgy.store, exc);
179
+ });
180
+ if (!sent)
181
+ return;
182
+ }
183
+ op.delivered = true;
184
+ }
185
+ this.live.delete(op.said);
186
+ this.outcomes.set(op.said, {
187
+ messages: op.publication?.messages ?? [],
188
+ deliveries: op.publication?.deliveries ?? [],
189
+ sent: op.deliveries,
190
+ });
191
+ this.deps.changes?.push({ kind: "ipex", id: op.said });
192
+ }
193
+ }
194
+ /**
195
+ * The proposer's consent to a group-authored v2 IPEX message: signs this member's contribution to the exact
196
+ * `exn` body, accepts it locally and records the operation. Publishing the wrapper to the other members is
197
+ * `processOnce`.
198
+ *
199
+ * @throws ValidationError for a body the group did not author, a non-group identifier, a message awaiting
200
+ * authenticating dependencies, or one already begun.
201
+ */
202
+ export function* proposeGroupIpex(ops, { hab, exn, nests = new Uint8Array(), gvrsn = Vrsn_2_0, deliver = true }) {
203
+ const { hby, protocol } = ops.deps;
204
+ if (!isLocalGroupHab(hby, hab))
205
+ throw new ValidationError("Group IPEX requires a local group identifier");
206
+ if (exn.pre !== hab.pre || !exn.said)
207
+ throw new ValidationError("The IPEX message must be authored by the group");
208
+ if (ops.live.has(exn.said))
209
+ throw new ValidationError("A group IPEX message was already begun for this SAID");
210
+ const candidate = concatBytes(Counter.makeGVC(gvrsn), exn.raw, Counter.enclose({ qb64: nests, version: gvrsn }));
211
+ const own = yield* prepareAcdcV2GroupIpex(hby, protocol, hab, candidate);
212
+ if (!own)
213
+ throw new ValidationError("ACDC v2 group disclosure awaits authenticating dependencies.");
214
+ return ops.begin(exn.said, hab, gvrsn, { kind: "initial", wire: own }, deliver);
215
+ }
216
+ /**
217
+ * The proposer's consent to a group-authored message that waits for a registry update, as a Grant carrying a
218
+ * presentation proof does: the caller has proposed the update (this member's contribution is its approval) and
219
+ * passes the anchor to wait for. The owner signs the message's contribution, through `prerequisite.proceed`, once
220
+ * the group has accepted the update, under this same consent. Returns after local commit.
221
+ */
222
+ export function proposeGroupIpexAfter(ops, { hab, exn, gvrsn = Vrsn_2_0, deliver = true, prerequisite }) {
223
+ if (!isLocalGroupHab(ops.deps.hby, hab))
224
+ throw new ValidationError("Group IPEX requires a local group identifier");
225
+ if (exn.pre !== hab.pre || !exn.said)
226
+ throw new ValidationError("The IPEX message must be authored by the group");
227
+ if (ops.live.has(exn.said))
228
+ throw new ValidationError("A group IPEX message was already begun for this SAID");
229
+ return ops.begin(exn.said, hab, gvrsn, null, deliver, prerequisite);
230
+ }
231
+ /**
232
+ * A group holder's consent to admit an accepted Grant: freezes the Admit body (explicit consent, exact prior
233
+ * authentication and current root truth), then proposes it as {@link proposeGroupIpex} does. The other members list
234
+ * the proposal and approve it; nothing here records inventory.
235
+ */
236
+ export function* proposeGroupAdmit(ops, { hab, grant, message = "", gvrsn = Vrsn_2_0, deliver }) {
237
+ const { hby, rgy, exc } = ops.deps;
238
+ if (!isLocalGroupHab(hby, hab))
239
+ throw new ValidationError("Group IPEX requires a local group identifier");
240
+ const admit = yield* new IpexAdmitter(hby, rgy.store, exc).prepareAdmitBody(grant, hab.pre, {
241
+ consent: true,
242
+ message,
243
+ gvrsn,
244
+ });
245
+ return { ...(yield* proposeGroupIpex(ops, { hab, exn: admit, gvrsn, deliver })), grant };
246
+ }
247
+ /** Stored v2 IPEX proposals of `hab` (or of every local group) that were proposed by a member, newest unordered. */
248
+ export function groupIpexProposals(ops, hab) {
249
+ const { hby, exc } = ops.deps, found = [];
250
+ for (const [, wrapper] of hby.db.exns.getTopItemIter()) {
251
+ if (wrapper.route !== MULTISIG_EXN_ROUTE || wrapper.pvrsn.major !== 2)
252
+ continue;
253
+ const embedded = storedMultisigIpexBody(hby.db, wrapper);
254
+ const group = embedded?.pre;
255
+ if (!embedded?.route?.startsWith("/ipex/") || !group || (hab && group !== hab.pre)
256
+ || !hby.habs.get(group) || !isLocalGroupHab(hby, hby.habs.get(group)))
257
+ continue;
258
+ if (found.some((item) => item.said === embedded.said))
259
+ continue;
260
+ found.push({
261
+ said: embedded.said,
262
+ route: embedded.route,
263
+ group,
264
+ wrapper: wrapper.said,
265
+ proposer: wrapper.pre,
266
+ ...(typeof embedded.sad?.p === "string" && embedded.sad.p ? { prior: embedded.sad.p } : {}),
267
+ approved: groupIpexApproved(hby, group, embedded),
268
+ complete: exc.complete(embedded.said),
269
+ });
270
+ }
271
+ return found;
272
+ }
273
+ /**
274
+ * A member's consent to a stored group IPEX proposal: verifies the exact stored wrapper, signs or reuses this
275
+ * member's contribution and accepts it locally, and records the operation. Publishing it, collecting the other
276
+ * members' contributions and, for the lead, delivering the completed message are `processOnce`.
277
+ *
278
+ * An accepted message may be approved again: the recorded contribution is reused, and the elected lead delivers the
279
+ * completed message, which a lead that learned of the acceptance from another member's wrapper has not yet done.
280
+ *
281
+ * @throws ValidationError when no v2 proposal is stored for `said`, an operation is already live for it, or this
282
+ * member still lacks the evidence to approve it.
283
+ */
284
+ export function* approveGroupIpex(ops, said, { deliver = true } = {}) {
285
+ const { hby, protocol } = ops.deps;
286
+ if (ops.live.has(said))
287
+ throw new ValidationError("A group IPEX message was already begun for this SAID");
288
+ const wrapper = findStoredMultisigIpex(hby.db, said);
289
+ if (!wrapper || wrapper.pvrsn.major !== 2)
290
+ throw new ValidationError("No stored group IPEX proposal for this SAID");
291
+ const { groupHab, genus, approved } = yield* approveMultisigIpexLocally(hby, protocol, null, wrapper);
292
+ if (!approved)
293
+ throw new ValidationError("This member lacks the evidence to approve the proposal yet");
294
+ return ops.begin(said, groupHab, genus, { kind: "proposal", wire: approved }, deliver);
295
+ }
296
+ /** Local abandon: stop publishing and collecting. Does not undo a recorded contribution or message the peers. */
297
+ export function abandonGroupIpex(ops, said, reason = "Abandoned by the user") {
298
+ const live = ops.live.get(said);
299
+ if (live)
300
+ ops.end(live, "abandoned", reason);
301
+ }
302
+ /**
303
+ * Pure read of one group IPEX message: what ended in this process, the live table, and otherwise what the stores
304
+ * prove (see the module doc).
305
+ *
306
+ * @throws ValidationError when no stored proposal or accepted message of a local group has this SAID.
307
+ */
308
+ export function groupIpexStatus(ops, said) {
309
+ const ended = ops.ended.get(said), live = ops.live.get(said);
310
+ if (ended)
311
+ return { state: ended.state, reason: ended.reason };
312
+ if (live)
313
+ return { state: "active", phase: live.phase };
314
+ const { hby, exc } = ops.deps;
315
+ if (exc.complete(said))
316
+ return { state: "done", phase: "accepted" };
317
+ if (!findStoredMultisigIpex(hby.db, said)) {
318
+ throw new ValidationError("No group IPEX proposal is stored for this SAID");
319
+ }
320
+ return { state: "active", phase: "awaitingConsent", needs: "approve" };
321
+ }
322
+ /** Observe until the message is accepted and delivered, or ended, or the budget expires. Starts no worker. */
323
+ export function* waitForGroupIpex(ops, said, { timeoutMs, clock = ops.deps.clock, until = (status) => status.state !== "active" }) {
324
+ const settled = yield* observeUntil(() => until(groupIpexStatus(ops, said)), { timeoutMs, clock });
325
+ const status = groupIpexStatus(ops, said);
326
+ return settled ? { outcome: "settled", status } : { outcome: "timed-out", status, timeoutMs };
327
+ }