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,103 @@
1
+ import { sleep } from "effection";
2
+ import { credentialSaidFromGrant, processCredentialPresentationArtifacts, storedGrantArtifacts, } from "../ipex/ipex-credentialing.js";
3
+ import { IPEX_GRANT_ROUTE } from "../ipex/ipexing.js";
4
+ import { systemClock } from "../runtime/runtime-clock.js";
5
+ /**
6
+ * Counts mailbox batches a host has consumed and whether one is still settling.
7
+ * Process-local: it describes this host's intake, so it is not part of any durable
8
+ * status. A wallet that does not track batches simply omits it.
9
+ */
10
+ export class MailboxIntakeTally {
11
+ constructor() {
12
+ Object.defineProperty(this, "receiving", {
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true,
16
+ value: 0
17
+ });
18
+ Object.defineProperty(this, "batches", {
19
+ enumerable: true,
20
+ configurable: true,
21
+ writable: true,
22
+ value: 0
23
+ });
24
+ Object.defineProperty(this, "messages", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: 0
29
+ });
30
+ }
31
+ /** Run the host's settle step for one batch, counting it when settled. */
32
+ *track(batch, settle) {
33
+ this.receiving++;
34
+ try {
35
+ yield* settle();
36
+ this.batches++;
37
+ this.messages += batch.messages.length;
38
+ }
39
+ finally {
40
+ this.receiving--;
41
+ }
42
+ }
43
+ }
44
+ function accepted(deps) {
45
+ return [...deps.hby.db.exns.getTopItemIter()].map(([, exn]) => exn);
46
+ }
47
+ function savedSaids(deps) {
48
+ return [...deps.reger.saved.getTopItemIter()].map(([keys]) => keys[0]).filter((key) => !!key);
49
+ }
50
+ /** Snapshot the said sets that later arrivals are measured against. */
51
+ export function ipexBaseline(deps) {
52
+ return {
53
+ exns: new Set(accepted(deps).map((exn) => exn.said).filter((said) => typeof said === "string")),
54
+ saved: new Set(savedSaids(deps)),
55
+ };
56
+ }
57
+ /** Pure read of what arrived since `baseline`. Starts nothing and writes nothing. */
58
+ export function ipexArrivalStatus(deps, baseline, tally) {
59
+ const ipex = accepted(deps).filter((exn) => !!exn.said && !baseline.exns.has(exn.said) && (exn.route ?? "").startsWith("/ipex/"));
60
+ const saved = [...new Set(savedSaids(deps))].filter((said) => !baseline.saved.has(said));
61
+ const receiving = (tally?.receiving ?? 0) > 0;
62
+ return {
63
+ state: !receiving && (ipex.length > 0 || saved.length > 0) ? "arrived" : "waiting",
64
+ ipex,
65
+ saved,
66
+ receiving,
67
+ };
68
+ }
69
+ /**
70
+ * Replay accepted grant artifacts whose credential is not yet saved, so a grant
71
+ * stored before the credential arrived settles once its evidence is complete.
72
+ */
73
+ export function* settleStoredGrants(deps, gvrsn) {
74
+ for (const [, grant] of deps.hby.db.exns.getTopItemIter()) {
75
+ if (grant.route !== IPEX_GRANT_ROUTE)
76
+ continue;
77
+ const credentialSaid = credentialSaidFromGrant(grant);
78
+ if (!credentialSaid || deps.reger.saved.get([credentialSaid]))
79
+ continue;
80
+ yield* processCredentialPresentationArtifacts(deps.protocol, storedGrantArtifacts(deps.hby, grant, gvrsn));
81
+ }
82
+ }
83
+ /**
84
+ * Observe until new IPEX state has arrived and settled, or the budget expires.
85
+ * Runs `deps.settle` before each read; starts no worker.
86
+ *
87
+ * @returns `settled` with the arrival, or `timed-out` with the last read (which
88
+ * may already hold arrivals that were still mid-settle).
89
+ */
90
+ export function* waitForIpexArrival(deps, baseline, { timeoutMs, tally, pollMs = 250, clock = systemClock }) {
91
+ const deadline = clock.now() + timeoutMs;
92
+ while (true) {
93
+ if (deps.settle)
94
+ yield* deps.settle();
95
+ const status = ipexArrivalStatus(deps, baseline, tally);
96
+ if (status.state === "arrived")
97
+ return { outcome: "settled", status };
98
+ const remaining = deadline - clock.now();
99
+ if (remaining <= 0)
100
+ return { outcome: "timed-out", status, timeoutMs };
101
+ yield* sleep(Math.min(pollMs, remaining));
102
+ }
103
+ }
@@ -0,0 +1,322 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _KelPublisher_instances, _KelPublisher_deps, _KelPublisher_budgets, _KelPublisher_inflight, _KelPublisher_dirty, _KelPublisher_owed, _KelPublisher_statusOf, _KelPublisher_budget, _KelPublisher_attempt, _KelPublisher_derive, _KelPublisher_deriveFor, _KelPublisher_prune;
13
+ import { Deck } from "../../core/deck.js";
14
+ import { dgKey } from "../../db/core/keys.js";
15
+ import { doEvery } from "../runtime/doing.js";
16
+ import { observeUntil } from "../runtime/workflow-contract.js";
17
+ import { holdsHeads, KEL_PUBLISH_HEADER_TIMEOUT_MS, kelHeads, pinKelPublished, postKelToMailbox, } from "./kel-publishing.js";
18
+ import { authorizedMailboxes, fetchEndpointUrls, preferredUrl } from "./mailboxing.js";
19
+ /** Most posts one `processOnce` attempts, so a first pass over a large store cannot stall the fiber. */
20
+ export const KelSendsPerPass = 4;
21
+ /** Delay after a post's first failure; doubles per attempt up to {@link KEL_PUBLISH_MAX_DELAY_MS}. */
22
+ export const KEL_PUBLISH_BASE_MS = 1_000;
23
+ export const KEL_PUBLISH_MAX_DELAY_MS = 60_000;
24
+ /** Give up when the next attempt would start more than this long after the first. */
25
+ export const KEL_PUBLISH_BUDGET_MS = 300_000;
26
+ /** The production retry policy of KEL posts: `boundMs` is the budget. */
27
+ export const DEFAULT_KEL_PUBLISH_RETRY = Object.freeze({
28
+ baseMs: KEL_PUBLISH_BASE_MS,
29
+ maxMs: KEL_PUBLISH_MAX_DELAY_MS,
30
+ boundMs: KEL_PUBLISH_BUDGET_MS,
31
+ });
32
+ /** Milliseconds between publisher passes; retries start at one second, so a finer tock buys nothing. */
33
+ export const KEL_PUBLISHER_TOCK_MS = 250;
34
+ /** Tocks between full sweeps of every identifier: 240 tocks of 250 ms is a minute. */
35
+ export const KEL_PUBLISHER_SWEEP_TOCKS = 240;
36
+ /** The production pacing of the publisher fiber. */
37
+ export const KEL_PUBLISHER_PACING = Object.freeze({
38
+ tockMs: KEL_PUBLISHER_TOCK_MS,
39
+ sweepTocks: KEL_PUBLISHER_SWEEP_TOCKS,
40
+ });
41
+ const idOf = (pre, mailbox) => `${pre}/${mailbox}`;
42
+ /** Derives, sends and records the KEL posts a wallet's identifiers owe their mailboxes. */
43
+ export class KelPublisher {
44
+ constructor(deps) {
45
+ _KelPublisher_instances.add(this);
46
+ /** Completion points push here; the next tock gives that identifier's entries a fresh budget. */
47
+ Object.defineProperty(this, "rotated", {
48
+ enumerable: true,
49
+ configurable: true,
50
+ writable: true,
51
+ value: new Deck()
52
+ });
53
+ /** A failure that escapes a pass is counted and kept here; the fiber keeps running. */
54
+ Object.defineProperty(this, "health", {
55
+ enumerable: true,
56
+ configurable: true,
57
+ writable: true,
58
+ value: { faults: 0, last: null }
59
+ });
60
+ _KelPublisher_deps.set(this, void 0);
61
+ _KelPublisher_budgets.set(this, new Map());
62
+ _KelPublisher_inflight.set(this, new Set());
63
+ /** Identifiers to re-derive on the next tock; the sweep adds every identifier. */
64
+ _KelPublisher_dirty.set(this, new Set());
65
+ /** Identifiers with something owed, re-derived each tock so a cleared hold is seen. */
66
+ _KelPublisher_owed.set(this, new Set());
67
+ __classPrivateFieldSet(this, _KelPublisher_deps, deps, "f");
68
+ }
69
+ /**
70
+ * Give `pre`'s entries a fresh budget and derive them on the next tock; with `mailbox`, re-arm only that mailbox's
71
+ * entry, and only if it timed out (the poller calls this after a successful poll).
72
+ */
73
+ prompt(pre, mailbox) {
74
+ if (mailbox !== undefined) {
75
+ const id = idOf(pre, mailbox);
76
+ if (!__classPrivateFieldGet(this, _KelPublisher_budgets, "f").get(id)?.expired)
77
+ return;
78
+ __classPrivateFieldGet(this, _KelPublisher_budgets, "f").delete(id);
79
+ }
80
+ else {
81
+ for (const id of [...__classPrivateFieldGet(this, _KelPublisher_budgets, "f").keys()])
82
+ if (id.startsWith(`${pre}/`))
83
+ __classPrivateFieldGet(this, _KelPublisher_budgets, "f").delete(id);
84
+ }
85
+ __classPrivateFieldGet(this, _KelPublisher_dirty, "f").add(pre);
86
+ }
87
+ /**
88
+ * A mailbox refused the owner's poll with 403: whatever the record says it holds, it cannot authenticate
89
+ * the owner (it lost its state, or accepted a post it then escrowed). Forget what it was given, so the next
90
+ * pass posts the KEL again, and give the entry a fresh budget. The poller calls this at most once per backoff
91
+ * period, which is also the most this posts; a mailbox that never recovers is given up on like any other.
92
+ */
93
+ repair(pre, mailbox) {
94
+ const { hby } = __classPrivateFieldGet(this, _KelPublisher_deps, "f"), record = hby.db.kpub.get([pre, mailbox]);
95
+ if (!hby.readonly && record?.d)
96
+ hby.db.kpub.pin([pre, mailbox], { d: null, dt: record.dt });
97
+ __classPrivateFieldGet(this, _KelPublisher_budgets, "f").delete(idOf(pre, mailbox));
98
+ __classPrivateFieldGet(this, _KelPublisher_dirty, "f").add(pre);
99
+ }
100
+ /**
101
+ * The full check: forget records of mailboxes no longer authorized, and mark every identifier for derivation.
102
+ * A walk of all identifiers, so a host runs it at open and every many tocks.
103
+ */
104
+ sweep() {
105
+ const { hby } = __classPrivateFieldGet(this, _KelPublisher_deps, "f");
106
+ if (hby.readonly)
107
+ return;
108
+ __classPrivateFieldGet(this, _KelPublisher_instances, "m", _KelPublisher_prune).call(this);
109
+ for (const hab of hby.habs.values())
110
+ if (hab.pre)
111
+ __classPrivateFieldGet(this, _KelPublisher_dirty, "f").add(hab.pre);
112
+ }
113
+ /**
114
+ * The fixed-tock pass: drain the cues, derive the marked identifiers and those still owed, then send up to
115
+ * {@link KelSendsPerPass} due posts, each within `timeoutMs` for the mailbox's answer. An idle pass reads no
116
+ * store. A post is recorded when the mailbox answers 2xx; a failure is retried on the schedule until the budget
117
+ * is used up. Never throws on a remote failure; every attempt is returned.
118
+ */
119
+ *processDue({ timeoutMs = KEL_PUBLISH_HEADER_TIMEOUT_MS } = {}) {
120
+ const { clock, hby } = __classPrivateFieldGet(this, _KelPublisher_deps, "f");
121
+ for (let cue = this.rotated.pull(); cue; cue = this.rotated.pull())
122
+ this.prompt(cue.pre);
123
+ if (hby.readonly)
124
+ return [];
125
+ const owed = [];
126
+ for (const pre of new Set([...__classPrivateFieldGet(this, _KelPublisher_dirty, "f"), ...__classPrivateFieldGet(this, _KelPublisher_owed, "f")])) {
127
+ const entries = __classPrivateFieldGet(this, _KelPublisher_instances, "m", _KelPublisher_deriveFor).call(this, pre).filter((entry) => !entry.published);
128
+ if (entries.length)
129
+ __classPrivateFieldGet(this, _KelPublisher_owed, "f").add(pre);
130
+ else
131
+ __classPrivateFieldGet(this, _KelPublisher_owed, "f").delete(pre);
132
+ owed.push(...entries);
133
+ }
134
+ __classPrivateFieldGet(this, _KelPublisher_dirty, "f").clear();
135
+ const live = new Set(owed.map((entry) => idOf(entry.pre, entry.mailbox)));
136
+ for (const id of [...__classPrivateFieldGet(this, _KelPublisher_budgets, "f").keys()]) {
137
+ const pre = id.slice(0, id.indexOf("/"));
138
+ if (!live.has(id) && !__classPrivateFieldGet(this, _KelPublisher_owed, "f").has(pre))
139
+ __classPrivateFieldGet(this, _KelPublisher_budgets, "f").delete(id);
140
+ }
141
+ const results = [];
142
+ for (const entry of owed) {
143
+ if (results.length >= KelSendsPerPass)
144
+ break;
145
+ const id = idOf(entry.pre, entry.mailbox), budget = __classPrivateFieldGet(this, _KelPublisher_instances, "m", _KelPublisher_budget).call(this, id, entry.said);
146
+ if (entry.hold || !entry.url || budget.expired || budget.retryAt > clock.now() || __classPrivateFieldGet(this, _KelPublisher_inflight, "f").has(id)) {
147
+ continue;
148
+ }
149
+ __classPrivateFieldGet(this, _KelPublisher_inflight, "f").add(id);
150
+ try {
151
+ results.push(yield* __classPrivateFieldGet(this, _KelPublisher_instances, "m", _KelPublisher_attempt).call(this, entry, entry.url, budget, timeoutMs));
152
+ }
153
+ finally {
154
+ __classPrivateFieldGet(this, _KelPublisher_inflight, "f").delete(id);
155
+ }
156
+ }
157
+ return results;
158
+ }
159
+ /** A sweep and a pass: what a one-shot host, or a test, runs once. */
160
+ *processOnce(options = {}) {
161
+ this.sweep();
162
+ return yield* this.processDue(options);
163
+ }
164
+ /** Pure read of every (identifier, mailbox) pair and where it stands. */
165
+ status() {
166
+ if (__classPrivateFieldGet(this, _KelPublisher_deps, "f").hby.readonly)
167
+ return { entries: [] };
168
+ return { entries: __classPrivateFieldGet(this, _KelPublisher_instances, "m", _KelPublisher_derive).call(this).map((entry) => __classPrivateFieldGet(this, _KelPublisher_instances, "m", _KelPublisher_statusOf).call(this, entry)) };
169
+ }
170
+ /** Pure read of one identifier's mailboxes; the cheap read for a wait on a single identifier. */
171
+ statusOf(pre) {
172
+ if (__classPrivateFieldGet(this, _KelPublisher_deps, "f").hby.readonly)
173
+ return [];
174
+ return __classPrivateFieldGet(this, _KelPublisher_instances, "m", _KelPublisher_deriveFor).call(this, pre).map((entry) => __classPrivateFieldGet(this, _KelPublisher_instances, "m", _KelPublisher_statusOf).call(this, entry));
175
+ }
176
+ /**
177
+ * Observe until every mailbox of `pre` is `published` or `timed-out`, or the deadline passes. Starts no
178
+ * worker: the runtime's publisher fiber (or a host's own `processOnce`) makes the progress. A waiting entry
179
+ * (unwitnessed, unanchored, no URL) keeps the wait open.
180
+ */
181
+ *waitForPublication(pre, { timeoutMs, pollMs = 50 }) {
182
+ __classPrivateFieldGet(this, _KelPublisher_dirty, "f").add(pre);
183
+ const settled = () => this.statusOf(pre).every((entry) => entry.state === "published" || entry.state === "timed-out");
184
+ const done = yield* observeUntil(settled, { timeoutMs, pollMs, clock: __classPrivateFieldGet(this, _KelPublisher_deps, "f").clock });
185
+ const status = this.status();
186
+ return done ? { outcome: "settled", status } : { outcome: "timed-out", status, timeoutMs };
187
+ }
188
+ }
189
+ _KelPublisher_deps = new WeakMap(), _KelPublisher_budgets = new WeakMap(), _KelPublisher_inflight = new WeakMap(), _KelPublisher_dirty = new WeakMap(), _KelPublisher_owed = new WeakMap(), _KelPublisher_instances = new WeakSet(), _KelPublisher_statusOf = function _KelPublisher_statusOf(entry) {
190
+ const base = { pre: entry.pre, mailbox: entry.mailbox, said: entry.said };
191
+ if (entry.published)
192
+ return { ...base, state: "published", attempts: 0 };
193
+ const budget = __classPrivateFieldGet(this, _KelPublisher_budgets, "f").get(idOf(entry.pre, entry.mailbox));
194
+ const failed = __classPrivateFieldGet(this, _KelPublisher_deps, "f").hby.db.kpub.get([entry.pre, entry.mailbox])?.failed;
195
+ if (entry.hold)
196
+ return { ...base, state: "waiting", attempts: budget?.attempts ?? 0, lastFailure: entry.hold };
197
+ if (budget?.expired) {
198
+ return {
199
+ ...base,
200
+ state: "timed-out",
201
+ attempts: failed?.attempts ?? budget.attempts,
202
+ lastFailure: failed?.reason ?? budget.lastFailure ?? undefined,
203
+ };
204
+ }
205
+ return {
206
+ ...base,
207
+ state: "pending",
208
+ attempts: budget?.attempts ?? 0,
209
+ ...(budget?.lastFailure ? { lastFailure: budget.lastFailure } : {}),
210
+ ...(budget?.attempts ? { retryAt: new Date(budget.retryAt).toISOString() } : {}),
211
+ };
212
+ }, _KelPublisher_budget = function _KelPublisher_budget(id, said) {
213
+ let budget = __classPrivateFieldGet(this, _KelPublisher_budgets, "f").get(id);
214
+ if (!budget || budget.said !== said) {
215
+ budget = { said, firstAt: null, attempts: 0, lastFailure: null, retryAt: 0, expired: false };
216
+ __classPrivateFieldGet(this, _KelPublisher_budgets, "f").set(id, budget);
217
+ }
218
+ return budget;
219
+ }, _KelPublisher_attempt = function
220
+ /** Post once, then record: the record on 2xx (or a host that cannot take the stream), a delay or give-up otherwise. */
221
+ * _KelPublisher_attempt(entry, url, budget, timeoutMs) {
222
+ const { clock, changes, hby } = __classPrivateFieldGet(this, _KelPublisher_deps, "f"), id = idOf(entry.pre, entry.mailbox);
223
+ budget.firstAt ??= clock.now();
224
+ const result = yield* postKelToMailbox(__classPrivateFieldGet(this, _KelPublisher_deps, "f"), entry.pre, { eid: entry.mailbox, url }, { timeoutMs });
225
+ const unsupported = result.outcome === "failed" && (result.status === 400 || result.status === 406);
226
+ if (result.outcome === "told" || unsupported) {
227
+ pinKelPublished(hby.db, entry.pre, entry.mailbox, { d: entry.said, m: entry.member }, clock);
228
+ __classPrivateFieldGet(this, _KelPublisher_budgets, "f").delete(id);
229
+ }
230
+ else {
231
+ budget.attempts += 1;
232
+ budget.lastFailure = result.reason;
233
+ const retry = __classPrivateFieldGet(this, _KelPublisher_deps, "f").retry ?? DEFAULT_KEL_PUBLISH_RETRY;
234
+ const delay = Math.min(retry.baseMs * 2 ** (budget.attempts - 1), retry.maxMs);
235
+ budget.retryAt = clock.now() + delay;
236
+ if (budget.retryAt - budget.firstAt > retry.boundMs) {
237
+ budget.expired = true;
238
+ const record = hby.db.kpub.get([entry.pre, entry.mailbox]);
239
+ hby.db.kpub.pin([entry.pre, entry.mailbox], {
240
+ d: record?.d ?? null,
241
+ ...(record?.m ? { m: record.m } : {}),
242
+ dt: record?.dt ?? new Date(clock.now()).toISOString(),
243
+ failed: {
244
+ d: entry.said,
245
+ dt: new Date(clock.now()).toISOString(),
246
+ attempts: budget.attempts,
247
+ reason: result.reason,
248
+ },
249
+ });
250
+ }
251
+ }
252
+ changes?.push({ kind: "event", id: `${entry.pre}:${entry.sn}` });
253
+ return result;
254
+ }, _KelPublisher_derive = function _KelPublisher_derive() {
255
+ const derived = [];
256
+ for (const hab of __classPrivateFieldGet(this, _KelPublisher_deps, "f").hby.habs.values())
257
+ if (hab.pre)
258
+ derived.push(...__classPrivateFieldGet(this, _KelPublisher_instances, "m", _KelPublisher_deriveFor).call(this, hab.pre));
259
+ return derived;
260
+ }, _KelPublisher_deriveFor = function _KelPublisher_deriveFor(pre) {
261
+ const { hby } = __classPrivateFieldGet(this, _KelPublisher_deps, "f"), db = hby.db, derived = [];
262
+ const hab = hby.habs.get(pre), kever = hab?.kever;
263
+ if (!hab || !kever)
264
+ return derived;
265
+ const heads = kelHeads(hby, pre);
266
+ if (!heads)
267
+ return derived;
268
+ const said = heads.d;
269
+ for (const mailbox of authorizedMailboxes(db, pre)) {
270
+ const url = preferredUrl(fetchEndpointUrls(hby, mailbox)) ?? null;
271
+ const established = db.getEvtSerder(pre, said);
272
+ let hold = null;
273
+ if (!url)
274
+ hold = "no-url";
275
+ else if (!established || !db.fullyWitnessed(established))
276
+ hold = "unwitnessed";
277
+ else if (kever.delegated && !db.aess.get(dgKey(pre, said)))
278
+ hold = "unanchored";
279
+ derived.push({
280
+ pre,
281
+ mailbox,
282
+ url,
283
+ said,
284
+ member: heads.m,
285
+ sn: kever.sn,
286
+ published: holdsHeads(db.kpub.get([pre, mailbox]), heads),
287
+ hold,
288
+ });
289
+ }
290
+ return derived;
291
+ }, _KelPublisher_prune = function _KelPublisher_prune() {
292
+ const { hby } = __classPrivateFieldGet(this, _KelPublisher_deps, "f"), db = hby.db;
293
+ const wanted = new Set();
294
+ for (const hab of hby.habs.values()) {
295
+ if (hab.pre)
296
+ for (const mailbox of authorizedMailboxes(db, hab.pre))
297
+ wanted.add(idOf(hab.pre, mailbox));
298
+ }
299
+ for (const [[pre, mailbox]] of [...db.kpub.getTopItemIter()]) {
300
+ if (!wanted.has(idOf(pre, mailbox)))
301
+ db.kpub.rem([pre, mailbox]);
302
+ }
303
+ };
304
+ /**
305
+ * Run the publisher on its own fiber until halted, as a fixed-tock doer: a sweep on tock 0 and every
306
+ * `pacing.sweepTocks`, a pass on every tock, then a wait of one tock. A mailbox that hangs holds up only this
307
+ * fiber, never the operation owners. A failure that escapes a pass is counted in `publisher.health` and the
308
+ * loop keeps running.
309
+ */
310
+ export function* kelPublisherDo(publisher, clock, pacing = KEL_PUBLISHER_PACING) {
311
+ return yield* doEvery(clock, pacing.tockMs, function* (tock) {
312
+ try {
313
+ if (tock % pacing.sweepTocks === 0)
314
+ publisher.sweep();
315
+ yield* publisher.processDue();
316
+ }
317
+ catch (error) {
318
+ publisher.health.faults++;
319
+ publisher.health.last = { error, at: clock.now() };
320
+ }
321
+ });
322
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Telling a mailbox host an identifier's KEL. A host authenticates a mailbox poll against the key state it
3
+ * holds, which is only as new as the last KEL it was given, so a rotation it has not been told about makes it
4
+ * refuse the owner's polls. The publisher (`KelPublisher`) and the poller's repair of a refusing mailbox use
5
+ * the one post below and record it in the same marker, `Baser.kpub`.
6
+ */
7
+ import { scoped } from "effection";
8
+ import { localGroupMember } from "../multisig/group-members.js";
9
+ import { buildCesrStreamRequest } from "../transport/cesr-http.js";
10
+ import { requestWithHeaderTimeout } from "../transport/httping.js";
11
+ import { collectKelSubmission } from "./kel-submission.js";
12
+ /** Longest a mailbox may take to answer the post, in milliseconds. */
13
+ export const KEL_PUBLISH_HEADER_TIMEOUT_MS = 10_000;
14
+ /**
15
+ * Post the KEL replay of `pre` (delegation chain, KEL and the receipts stored for it) to one mailbox's CESR
16
+ * ingress, with `CESR-DESTINATION` selecting the hosted mailbox. A separate request, never prefixed to a poll:
17
+ * a host answers with the response to the first message of a body.
18
+ */
19
+ export function* postKelToMailbox({ hby, http, clock }, pre, endpoint, { timeoutMs = KEL_PUBLISH_HEADER_TIMEOUT_MS } = {}) {
20
+ const { replay } = collectKelSubmission(hby, pre);
21
+ const request = buildCesrStreamRequest(replay, { destination: endpoint.eid });
22
+ try {
23
+ return yield* scoped(function* () {
24
+ const response = yield* requestWithHeaderTimeout(http, clock, endpoint.url, { method: "POST", headers: request.headers, body: request.body }, timeoutMs);
25
+ if (!response)
26
+ return { mailbox: endpoint.eid, outcome: "failed", reason: `no answer within ${timeoutMs} ms` };
27
+ return response.ok
28
+ ? { mailbox: endpoint.eid, outcome: "told" }
29
+ : { mailbox: endpoint.eid, outcome: "failed", reason: `HTTP ${response.status}`, status: response.status };
30
+ });
31
+ }
32
+ catch (error) {
33
+ return { mailbox: endpoint.eid, outcome: "failed", reason: error instanceof Error ? error.message : String(error) };
34
+ }
35
+ }
36
+ /**
37
+ * The establishment events a mailbox must hold to authenticate `pre`: the identifier's own latest one and, for
38
+ * a local group, the local signing member's, because the member signs the group's polls and the host needs its
39
+ * key state to verify them. `collectKelSubmission` is the one place that decides what the post carries; these
40
+ * heads are what the record is compared with.
41
+ */
42
+ export function kelHeads(hby, pre) {
43
+ const kever = hby.db.getKever(pre);
44
+ if (!kever)
45
+ return null;
46
+ if (!hby.db.getHab(pre)?.mid)
47
+ return { d: kever.lastEst.d };
48
+ const member = localGroupMember(hby, pre).kever;
49
+ return member ? { d: kever.lastEst.d, m: member.lastEst.d } : { d: kever.lastEst.d };
50
+ }
51
+ /**
52
+ * Record that `mailbox` was given the KEL of `pre` up to `heads`, and clear any earlier give-up. The record holds
53
+ * coordinates only; the publisher derives what is owed by comparing it with the identifier's current heads.
54
+ */
55
+ export function pinKelPublished(db, pre, mailbox, heads, clock) {
56
+ db.kpub.pin([pre, mailbox], {
57
+ d: heads.d,
58
+ ...(heads.m ? { m: heads.m } : {}),
59
+ dt: new Date(clock.now()).toISOString(),
60
+ });
61
+ }
62
+ /** Whether `record` shows the mailbox holds `heads`. */
63
+ export function holdsHeads(record, heads) {
64
+ return record?.d === heads.d && (heads.m === undefined || record.m === heads.m);
65
+ }
@@ -0,0 +1,24 @@
1
+ /** The KEL of a local identifier as bytes to post to a mailbox host that must learn its key state. */
2
+ import { concatBytes } from "cesr-ts";
3
+ import { localGroupMember } from "../multisig/group-members.js";
4
+ /**
5
+ * Collect the accepted KEL of `pre`, and its delegation chain, from the local stores. When `pre` is a local
6
+ * group the KEL of the local member that signs for it comes first, so a host that authenticates the member's
7
+ * mailbox polls learns the member's key state, including a rotation of its own, along with the group's.
8
+ *
9
+ * `replay` is the verifier-ready stream used by raw CESR ingress. `kel`/`delkel` preserve the legacy
10
+ * multipart wrapper contract used by existing KERIpy mailbox admin hosts.
11
+ */
12
+ export function collectKelSubmission(hby, pre) {
13
+ const parts = [...(hby.db.getHab(pre)?.mid ? [localGroupMember(hby, pre).pre] : []), pre].map((each) => {
14
+ const kever = hby.db.getKever(each);
15
+ return {
16
+ delkel: kever ? concatBytes(...hby.db.cloneDelegation(kever)) : new Uint8Array(),
17
+ kel: concatBytes(...hby.db.clonePreIter(each)),
18
+ };
19
+ });
20
+ const delkel = concatBytes(...parts.map((part) => part.delkel));
21
+ const kel = concatBytes(...parts.map((part) => part.kel));
22
+ const replay = concatBytes(...parts.flatMap((part) => [part.delkel, part.kel]));
23
+ return { replay, kel, delkel };
24
+ }