keri-ts 2.2.3 → 2.2.5

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 (247) hide show
  1. package/esm/keri/npm/src/_dnt.polyfills.js +14 -14
  2. package/esm/keri/npm/src/acdc/registering.js +19 -0
  3. package/esm/keri/npm/src/acdc/salting.js +4 -0
  4. package/esm/keri/npm/src/app/bootstrap/authenticating.js +34 -12
  5. package/esm/keri/npm/src/app/bootstrap/oobiery.js +365 -32
  6. package/esm/keri/npm/src/app/credentials/credential-importing.js +33 -4
  7. package/esm/keri/npm/src/app/credentials/root-replicator.js +27 -15
  8. package/esm/keri/npm/src/app/credentials/schema-resolving.js +5 -1
  9. package/esm/keri/npm/src/app/credentials/tel-publishing.js +16 -7
  10. package/esm/keri/npm/src/app/delivery/forwarding.js +188 -47
  11. package/esm/keri/npm/src/app/delivery/relaying.js +232 -0
  12. package/esm/keri/npm/src/app/delivery/respondant.js +8 -5
  13. package/esm/keri/npm/src/app/exchanges/exchange-messaging.js +1 -1
  14. package/esm/keri/npm/src/app/exchanges/exchanging.js +65 -9
  15. package/esm/keri/npm/src/app/identity/delegating.js +66 -9
  16. package/esm/keri/npm/src/app/identity/habbing.js +190 -71
  17. package/esm/keri/npm/src/app/identity/keeping.js +111 -39
  18. package/esm/keri/npm/src/app/identity/retire-identifier.js +98 -0
  19. package/esm/keri/npm/src/app/ipex/disclosure-replaying.js +116 -0
  20. package/esm/keri/npm/src/app/ipex/group-ipex.js +122 -25
  21. package/esm/keri/npm/src/app/ipex/group-issuance.js +109 -23
  22. package/esm/keri/npm/src/app/ipex/ipex-discloser.js +36 -22
  23. package/esm/keri/npm/src/app/ipex/ipex-grouping.js +53 -3
  24. package/esm/keri/npm/src/app/ipex/ipex-v2.js +7 -2
  25. package/esm/keri/npm/src/app/ipex/issuance.js +131 -35
  26. package/esm/keri/npm/src/app/ipex/pending-presentations.js +4 -2
  27. package/esm/keri/npm/src/app/ipex/presenting.js +3 -18
  28. package/esm/keri/npm/src/app/ipex/receive-issuance.js +2 -1
  29. package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +16 -3
  30. package/esm/keri/npm/src/app/mailbox/agent-inbox.js +292 -0
  31. package/esm/keri/npm/src/app/mailbox/forward-handler.js +298 -52
  32. package/esm/keri/npm/src/app/mailbox/kel-publisher.js +26 -31
  33. package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +70 -62
  34. package/esm/keri/npm/src/app/mailbox/mailboxing.js +20 -4
  35. package/esm/keri/npm/src/app/multisig/counseling.js +75 -27
  36. package/esm/keri/npm/src/app/multisig/group-members.js +41 -5
  37. package/esm/keri/npm/src/app/multisig/grouping.js +50 -2
  38. package/esm/keri/npm/src/app/multisig/multisig-workflows.js +3 -18
  39. package/esm/keri/npm/src/app/multisig/proposing.js +1 -16
  40. package/esm/keri/npm/src/app/multisig/rejoin-group.js +99 -0
  41. package/esm/keri/npm/src/app/multisig/replace-member.js +29 -8
  42. package/esm/keri/npm/src/app/protocol/parsering.js +45 -17
  43. package/esm/keri/npm/src/app/protocol/protocol-processing.js +62 -27
  44. package/esm/keri/npm/src/app/queries/query-replies.js +0 -22
  45. package/esm/keri/npm/src/app/queries/relay-refresh.js +81 -0
  46. package/esm/keri/npm/src/app/queries/witness-inquisitor.js +190 -40
  47. package/esm/keri/npm/src/app/runtime/agent-runtime.js +46 -73
  48. package/esm/keri/npm/src/app/runtime/cue-runtime.js +45 -5
  49. package/esm/keri/npm/src/app/runtime/doing.js +25 -0
  50. package/esm/keri/npm/src/app/runtime/operation-owners.js +26 -56
  51. package/esm/keri/npm/src/app/runtime/workflow-contract.js +7 -4
  52. package/esm/keri/npm/src/app/transport/http-client.js +20 -2
  53. package/esm/keri/npm/src/app/transport/message-source.js +2 -1
  54. package/esm/keri/npm/src/app/transport/messengering.js +39 -8
  55. package/esm/keri/npm/src/app/witness/ingress.js +7 -0
  56. package/esm/keri/npm/src/app/witness/receipt-client.js +43 -16
  57. package/esm/keri/npm/src/app/witness/receipting.js +62 -20
  58. package/esm/keri/npm/src/cli/messaging/multisig.js +4 -3
  59. package/esm/keri/npm/src/core/errors.js +73 -0
  60. package/esm/keri/npm/src/core/kel/delegation-ancestry.js +11 -2
  61. package/esm/keri/npm/src/core/kel/eventing.js +25 -28
  62. package/esm/keri/npm/src/core/kel/kever.js +15 -9
  63. package/esm/keri/npm/src/core/mailbox/kram-policy.js +8 -0
  64. package/esm/keri/npm/src/core/mailbox/kram-pruning.js +25 -6
  65. package/esm/keri/npm/src/core/mailbox/kram-time.js +34 -6
  66. package/esm/keri/npm/src/core/mailbox/kraming.js +58 -2
  67. package/esm/keri/npm/src/core/routing.js +18 -25
  68. package/esm/keri/npm/src/core/scheming.js +12 -5
  69. package/esm/keri/npm/src/core/wire-fields.js +126 -0
  70. package/esm/keri/npm/src/db/basing.js +4 -13
  71. package/esm/keri/npm/src/db/core/indexeddb-persistence.js +3 -2
  72. package/esm/keri/npm/src/db/core/lmdber.js +25 -6
  73. package/esm/keri/npm/src/db/core/memstore.js +4 -0
  74. package/esm/keri/npm/src/db/core/store-owner.js +4 -0
  75. package/esm/keri/npm/src/db/core/webdber.js +85 -27
  76. package/esm/keri/npm/src/db/mailbox/mailboxing.js +13 -0
  77. package/esm/keri/npm/src/db/subing.js +4 -0
  78. package/esm/keri/npm/src/runtime/index.js +11 -5
  79. package/esm/keri/npm/src/time/mod.js +5 -0
  80. package/esm/keri/npm/src/vdr/credential-tsn-record.js +2 -7
  81. package/esm/keri/npm/src/vdr/eventing.js +28 -19
  82. package/esm/keri/npm/src/vdr/registry-tsn-record.js +3 -13
  83. package/esm/keri/npm/src/version.js +2 -2
  84. package/package.json +1 -1
  85. package/types/_dnt.polyfills.d.ts +9 -9
  86. package/types/_dnt.polyfills.d.ts.map +1 -1
  87. package/types/acdc/registering.d.ts +6 -0
  88. package/types/acdc/registering.d.ts.map +1 -1
  89. package/types/acdc/salting.d.ts.map +1 -1
  90. package/types/app/bootstrap/authenticating.d.ts +12 -2
  91. package/types/app/bootstrap/authenticating.d.ts.map +1 -1
  92. package/types/app/bootstrap/oobiery.d.ts +144 -7
  93. package/types/app/bootstrap/oobiery.d.ts.map +1 -1
  94. package/types/app/credentials/credential-importing.d.ts +8 -0
  95. package/types/app/credentials/credential-importing.d.ts.map +1 -1
  96. package/types/app/credentials/root-replicator.d.ts +19 -8
  97. package/types/app/credentials/root-replicator.d.ts.map +1 -1
  98. package/types/app/credentials/schema-resolving.d.ts.map +1 -1
  99. package/types/app/credentials/tel-publishing.d.ts +4 -2
  100. package/types/app/credentials/tel-publishing.d.ts.map +1 -1
  101. package/types/app/delivery/forwarding.d.ts +75 -7
  102. package/types/app/delivery/forwarding.d.ts.map +1 -1
  103. package/types/app/delivery/relaying.d.ts +144 -0
  104. package/types/app/delivery/relaying.d.ts.map +1 -0
  105. package/types/app/delivery/respondant.d.ts +9 -2
  106. package/types/app/delivery/respondant.d.ts.map +1 -1
  107. package/types/app/exchanges/exchanging.d.ts +36 -0
  108. package/types/app/exchanges/exchanging.d.ts.map +1 -1
  109. package/types/app/identity/delegating.d.ts +6 -0
  110. package/types/app/identity/delegating.d.ts.map +1 -1
  111. package/types/app/identity/habbing.d.ts +106 -19
  112. package/types/app/identity/habbing.d.ts.map +1 -1
  113. package/types/app/identity/keeping.d.ts +54 -19
  114. package/types/app/identity/keeping.d.ts.map +1 -1
  115. package/types/app/identity/retire-identifier.d.ts +24 -0
  116. package/types/app/identity/retire-identifier.d.ts.map +1 -0
  117. package/types/app/ipex/disclosure-replaying.d.ts +61 -0
  118. package/types/app/ipex/disclosure-replaying.d.ts.map +1 -0
  119. package/types/app/ipex/group-ipex.d.ts +60 -14
  120. package/types/app/ipex/group-ipex.d.ts.map +1 -1
  121. package/types/app/ipex/group-issuance.d.ts +48 -6
  122. package/types/app/ipex/group-issuance.d.ts.map +1 -1
  123. package/types/app/ipex/ipex-discloser.d.ts +14 -2
  124. package/types/app/ipex/ipex-discloser.d.ts.map +1 -1
  125. package/types/app/ipex/ipex-grouping.d.ts +23 -2
  126. package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
  127. package/types/app/ipex/ipex-v2.d.ts +5 -2
  128. package/types/app/ipex/ipex-v2.d.ts.map +1 -1
  129. package/types/app/ipex/issuance.d.ts +35 -11
  130. package/types/app/ipex/issuance.d.ts.map +1 -1
  131. package/types/app/ipex/pending-presentations.d.ts +4 -2
  132. package/types/app/ipex/pending-presentations.d.ts.map +1 -1
  133. package/types/app/ipex/presenting.d.ts.map +1 -1
  134. package/types/app/ipex/receive-issuance.d.ts +2 -1
  135. package/types/app/ipex/receive-issuance.d.ts.map +1 -1
  136. package/types/app/ipex/unblinding-workflow.d.ts +4 -1
  137. package/types/app/ipex/unblinding-workflow.d.ts.map +1 -1
  138. package/types/app/mailbox/agent-inbox.d.ts +126 -0
  139. package/types/app/mailbox/agent-inbox.d.ts.map +1 -0
  140. package/types/app/mailbox/forward-handler.d.ts +114 -14
  141. package/types/app/mailbox/forward-handler.d.ts.map +1 -1
  142. package/types/app/mailbox/kel-publisher.d.ts +12 -22
  143. package/types/app/mailbox/kel-publisher.d.ts.map +1 -1
  144. package/types/app/mailbox/mailbox-poller.d.ts +40 -24
  145. package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
  146. package/types/app/mailbox/mailboxing.d.ts +19 -5
  147. package/types/app/mailbox/mailboxing.d.ts.map +1 -1
  148. package/types/app/multisig/counseling.d.ts +34 -11
  149. package/types/app/multisig/counseling.d.ts.map +1 -1
  150. package/types/app/multisig/group-members.d.ts +17 -1
  151. package/types/app/multisig/group-members.d.ts.map +1 -1
  152. package/types/app/multisig/grouping.d.ts +17 -1
  153. package/types/app/multisig/grouping.d.ts.map +1 -1
  154. package/types/app/multisig/multisig-workflows.d.ts +0 -8
  155. package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
  156. package/types/app/multisig/proposing.d.ts +4 -19
  157. package/types/app/multisig/proposing.d.ts.map +1 -1
  158. package/types/app/multisig/rejoin-group.d.ts +119 -0
  159. package/types/app/multisig/rejoin-group.d.ts.map +1 -0
  160. package/types/app/multisig/replace-member.d.ts +15 -1
  161. package/types/app/multisig/replace-member.d.ts.map +1 -1
  162. package/types/app/protocol/parsering.d.ts +21 -2
  163. package/types/app/protocol/parsering.d.ts.map +1 -1
  164. package/types/app/protocol/protocol-processing.d.ts +31 -6
  165. package/types/app/protocol/protocol-processing.d.ts.map +1 -1
  166. package/types/app/queries/query-replies.d.ts +4 -8
  167. package/types/app/queries/query-replies.d.ts.map +1 -1
  168. package/types/app/queries/relay-refresh.d.ts +26 -0
  169. package/types/app/queries/relay-refresh.d.ts.map +1 -0
  170. package/types/app/queries/witness-inquisitor.d.ts +57 -6
  171. package/types/app/queries/witness-inquisitor.d.ts.map +1 -1
  172. package/types/app/runtime/agent-runtime.d.ts +34 -3
  173. package/types/app/runtime/agent-runtime.d.ts.map +1 -1
  174. package/types/app/runtime/cue-runtime.d.ts +15 -3
  175. package/types/app/runtime/cue-runtime.d.ts.map +1 -1
  176. package/types/app/runtime/doing.d.ts +17 -0
  177. package/types/app/runtime/doing.d.ts.map +1 -1
  178. package/types/app/runtime/operation-owners.d.ts +16 -26
  179. package/types/app/runtime/operation-owners.d.ts.map +1 -1
  180. package/types/app/runtime/workflow-contract.d.ts +21 -6
  181. package/types/app/runtime/workflow-contract.d.ts.map +1 -1
  182. package/types/app/transport/http-client.d.ts +9 -0
  183. package/types/app/transport/http-client.d.ts.map +1 -1
  184. package/types/app/transport/message-source.d.ts +6 -3
  185. package/types/app/transport/message-source.d.ts.map +1 -1
  186. package/types/app/transport/messengering.d.ts +10 -1
  187. package/types/app/transport/messengering.d.ts.map +1 -1
  188. package/types/app/witness/ingress.d.ts +1 -0
  189. package/types/app/witness/ingress.d.ts.map +1 -1
  190. package/types/app/witness/receipt-client.d.ts +19 -1
  191. package/types/app/witness/receipt-client.d.ts.map +1 -1
  192. package/types/app/witness/receipting.d.ts +24 -1
  193. package/types/app/witness/receipting.d.ts.map +1 -1
  194. package/types/cli/messaging/multisig.d.ts.map +1 -1
  195. package/types/core/cues.d.ts +53 -1
  196. package/types/core/cues.d.ts.map +1 -1
  197. package/types/core/errors.d.ts +32 -0
  198. package/types/core/errors.d.ts.map +1 -1
  199. package/types/core/kel/delegation-ancestry.d.ts.map +1 -1
  200. package/types/core/kel/eventing.d.ts.map +1 -1
  201. package/types/core/kel/kever-decisions.d.ts +3 -1
  202. package/types/core/kel/kever-decisions.d.ts.map +1 -1
  203. package/types/core/kel/kever.d.ts +7 -1
  204. package/types/core/kel/kever.d.ts.map +1 -1
  205. package/types/core/mailbox/kram-policy.d.ts +6 -0
  206. package/types/core/mailbox/kram-policy.d.ts.map +1 -1
  207. package/types/core/mailbox/kram-pruning.d.ts +5 -1
  208. package/types/core/mailbox/kram-pruning.d.ts.map +1 -1
  209. package/types/core/mailbox/kram-time.d.ts +26 -2
  210. package/types/core/mailbox/kram-time.d.ts.map +1 -1
  211. package/types/core/mailbox/kraming.d.ts +14 -1
  212. package/types/core/mailbox/kraming.d.ts.map +1 -1
  213. package/types/core/routing.d.ts +3 -7
  214. package/types/core/routing.d.ts.map +1 -1
  215. package/types/core/scheming.d.ts.map +1 -1
  216. package/types/core/wire-fields.d.ts +56 -0
  217. package/types/core/wire-fields.d.ts.map +1 -0
  218. package/types/db/basing.d.ts.map +1 -1
  219. package/types/db/core/dber.d.ts +5 -0
  220. package/types/db/core/dber.d.ts.map +1 -1
  221. package/types/db/core/indexeddb-persistence.d.ts +3 -2
  222. package/types/db/core/indexeddb-persistence.d.ts.map +1 -1
  223. package/types/db/core/lmdber.d.ts +6 -0
  224. package/types/db/core/lmdber.d.ts.map +1 -1
  225. package/types/db/core/memstore.d.ts +2 -0
  226. package/types/db/core/memstore.d.ts.map +1 -1
  227. package/types/db/core/store-owner.d.ts +2 -0
  228. package/types/db/core/store-owner.d.ts.map +1 -1
  229. package/types/db/core/webdber.d.ts +32 -6
  230. package/types/db/core/webdber.d.ts.map +1 -1
  231. package/types/db/exchange/exchange-records.d.ts +4 -0
  232. package/types/db/exchange/exchange-records.d.ts.map +1 -1
  233. package/types/db/mailbox/mailboxing.d.ts +10 -1
  234. package/types/db/mailbox/mailboxing.d.ts.map +1 -1
  235. package/types/db/subing.d.ts +2 -0
  236. package/types/db/subing.d.ts.map +1 -1
  237. package/types/runtime/index.d.ts +20 -14
  238. package/types/runtime/index.d.ts.map +1 -1
  239. package/types/time/mod.d.ts +2 -0
  240. package/types/time/mod.d.ts.map +1 -1
  241. package/types/vdr/credential-tsn-record.d.ts.map +1 -1
  242. package/types/vdr/eventing.d.ts.map +1 -1
  243. package/types/vdr/registry-tsn-record.d.ts.map +1 -1
  244. package/types/version.d.ts +2 -2
  245. package/esm/keri/npm/src/app/runtime/signer-conditions.js +0 -51
  246. package/types/app/runtime/signer-conditions.d.ts +0 -32
  247. package/types/app/runtime/signer-conditions.d.ts.map +0 -1
@@ -7,25 +7,56 @@
7
7
  * do not select a stored framing preference or acquire workers, stores or queues.
8
8
  */
9
9
  import { ValidationError } from "../../core/errors.js";
10
+ import { readUrl } from "../../core/wire-fields.js";
11
+ import { isRequestableHttpUrl } from "./http-client.js";
10
12
  import { HTTPMessenger } from "./http-messenger.js";
11
13
  import { HTTPStreamMessenger } from "./http-stream-messenger.js";
12
14
  import { requireTcpDialer } from "./tcp-dialer.js";
13
- /** Choose HTTPS, HTTP, then TCP and reject a mismatched advertised scheme before I/O. */
14
- function endpointFrom(urls) {
15
+ /**
16
+ * Choose HTTPS, HTTP, then TCP, refusing before I/O what no messenger can use: an unparseable URL, a scheme other than
17
+ * the advertised one, an HTTP(S) URL the HTTP client refuses ({@link isRequestableHttpUrl}: embedded credentials), or
18
+ * a TCP URL without a valid port. A refused higher-priority URL does not fall through to another transport.
19
+ *
20
+ * @param urls Resolved endpoints, keyed by their advertised scheme.
21
+ * @returns The selection, never a throw.
22
+ */
23
+ function selectEndpoint(urls) {
15
24
  for (const scheme of ["https", "http", "tcp"]) {
16
25
  const value = urls[scheme];
17
26
  if (value === undefined)
18
27
  continue;
19
- const endpoint = new URL(value);
20
- if (endpoint.protocol !== `${scheme}:` || endpoint.username || endpoint.password) {
21
- throw new ValidationError(`Invalid ${scheme} messenger endpoint: ${value}`);
28
+ const endpoint = readUrl(value);
29
+ if (!endpoint)
30
+ return { kind: "refused", reason: `Invalid ${scheme} messenger endpoint: ${value}` };
31
+ const usable = scheme === "tcp" ? !endpoint.username && !endpoint.password : isRequestableHttpUrl(value);
32
+ if (endpoint.protocol !== `${scheme}:` || !usable) {
33
+ return { kind: "refused", reason: `Invalid ${scheme} messenger endpoint: ${value}` };
22
34
  }
23
35
  if (scheme === "tcp" && (!endpoint.port || Number(endpoint.port) < 1 || Number(endpoint.port) > 65535)) {
24
- throw new ValidationError(`TCP messenger endpoint requires a valid port: ${value}`);
36
+ return { kind: "refused", reason: `TCP messenger endpoint requires a valid port: ${value}` };
25
37
  }
26
- return endpoint;
38
+ return { kind: "usable", value, endpoint };
27
39
  }
28
- throw new ValidationError("No supported messenger endpoint is available.");
40
+ return { kind: "refused", reason: "No supported messenger endpoint is available." };
41
+ }
42
+ /** Select the factories' endpoint, refusing an unusable one as a caller error. */
43
+ function endpointFrom(urls) {
44
+ const selection = selectEndpoint(urls);
45
+ if (selection.kind === "refused")
46
+ throw new ValidationError(selection.reason);
47
+ return selection.endpoint;
48
+ }
49
+ /**
50
+ * The URL the messenger factories would use for these endpoints, or null when they would refuse it. A caller holding
51
+ * a location another party advertised reads a refused one as no location, so it fails one delivery as unroutable
52
+ * instead of throwing out of the factory.
53
+ *
54
+ * @param urls Resolved endpoints, keyed by their advertised scheme.
55
+ * @returns The selected URL exactly as advertised, or null.
56
+ */
57
+ export function messengerUrl(urls) {
58
+ const selection = selectEndpoint(urls);
59
+ return selection.kind === "usable" ? selection.value : null;
29
60
  }
30
61
  /**
31
62
  * Bind ordinary signed-message delivery to the first present HTTPS/HTTP/TCP URL.
@@ -2,6 +2,7 @@
2
2
  import { parse } from "cesr-ts";
3
3
  import { call, ensure } from "effection";
4
4
  import { rethrowCleanupFailure } from "../runtime/abortable.js";
5
+ import { promptForEndRole } from "../runtime/cue-runtime.js";
5
6
  import { chunkBytes } from "../transport/byte-source.js";
6
7
  /** Return the active wire-message habitat or `null` when selection is ambiguous. */
7
8
  function cueHab(runtime, serviceHab) {
@@ -15,6 +16,7 @@ function cueHab(runtime, serviceHab) {
15
16
  /**
16
17
  * Route one initial witness cue batch, leaving saved key-state facts for the
17
18
  * application cue worker and its optional command-owned noticer destination.
19
+ * An accepted mailbox end role prompts the KEL publisher and poller as the cue worker does.
18
20
  * Respondant publishes Poster work; this ingress helper never drains Poster
19
21
  * network delivery. Query network I/O stays with the independent sender.
20
22
  *
@@ -39,6 +41,11 @@ export function* drainWitnessCues(runtime, serviceHab, cues, { deliver = true }
39
41
  cues.append(cue);
40
42
  continue;
41
43
  }
44
+ if (cue.kin === "endRoleSaved") {
45
+ cues.pull();
46
+ promptForEndRole(runtime, cue);
47
+ continue;
48
+ }
42
49
  if (deliver) {
43
50
  if (cue.kin === "query") {
44
51
  runtime.querying.enqueueCue(cue, hab);
@@ -8,6 +8,7 @@ import { collectBytes } from "../transport/byte-source.js";
8
8
  import { buildCesrRequest, CESR_CONTENT_TYPE, KERIPY_CESR_JSON_CONTENT_TYPE } from "../transport/cesr-http.js";
9
9
  import { HTTPMessenger } from "../transport/http-messenger.js";
10
10
  import { requireTcpDialer } from "../transport/tcp-dialer.js";
11
+ import { TransportError } from "../transport/transport-error.js";
11
12
  import { inspectReceiptMessage } from "./receipt-messages.js";
12
13
  const WitnessReceiptPollAttempts = 20;
13
14
  const WitnessReceiptPollDelayMs = 100;
@@ -51,6 +52,8 @@ function preferredWitnessTransportUrl(hab, witness) {
51
52
  return witnessHttpUrl(hab, witness) ?? witnessTcpUrl(hab, witness);
52
53
  }
53
54
  /**
55
+ * Deliver one message to a witness, reporting remote failure as data.
56
+ *
54
57
  * Send via HTTP first; an explicit protocol processor processes same-socket TCP replies.
55
58
  * Without a protocol processor TCP reports local transmission only. Actual socket/parser
56
59
  * cleanup finishes before this borrowed protocol processor can be released.
@@ -62,27 +65,51 @@ function preferredWitnessTransportUrl(hab, witness) {
62
65
  * @param clock Borrowed clock with epoch-millisecond now and millisecond timer delays.
63
66
  * @param protocol Optional same-socket reply processor; null selects transmit-only TCP.
64
67
  * @param auth Optional HTTP Authorization header value.
65
- * @throws Missing endpoint, peer refusal, transport/parser or cleanup failures propagate.
68
+ * @returns `unreachable` when the witness advertises no endpoint, refuses over HTTP, or the connection fails
69
+ * (`TransportError`); the caller decides whether to retry later. Parser, storage, programmer and cleanup
70
+ * failures still throw.
66
71
  */
67
- export function* sendWitnessMessage(hab, witness, bytes, http, clock, protocol, auth) {
72
+ export function* deliverWitnessMessage(hab, witness, bytes, http, clock, protocol, auth) {
68
73
  const url = preferredWitnessTransportUrl(hab, witness);
69
74
  if (!url) {
70
- throw new ValidationError(`Unable to find a valid endpoint for witness ${witness}.`);
75
+ return {
76
+ kind: "unreachable",
77
+ witness,
78
+ error: new ValidationError(`Unable to find a valid endpoint for witness ${witness}.`),
79
+ };
71
80
  }
72
- const parsed = new URL(url);
73
- if (parsed.protocol === "tcp:") {
74
- yield* requireTcpDialer(http).messenger(url, clock, protocol).send(bytes, {
75
- replies: Boolean(protocol),
76
- local: true,
77
- });
78
- return;
81
+ try {
82
+ const parsed = new URL(url);
83
+ if (parsed.protocol === "tcp:") {
84
+ yield* requireTcpDialer(http).messenger(url, clock, protocol).send(bytes, {
85
+ replies: Boolean(protocol),
86
+ local: true,
87
+ });
88
+ return { kind: "delivered" };
89
+ }
90
+ const outcome = yield* new HTTPMessenger(url, http, {
91
+ destination: witness,
92
+ headers: auth ? { Authorization: auth } : undefined,
93
+ }).send(bytes);
94
+ if (outcome.kind === "refuse")
95
+ return { kind: "unreachable", witness, error: outcome.error };
96
+ return { kind: "delivered" };
97
+ }
98
+ catch (error) {
99
+ if (!(error instanceof TransportError))
100
+ throw error;
101
+ return { kind: "unreachable", witness, error };
79
102
  }
80
- const outcome = yield* new HTTPMessenger(url, http, {
81
- destination: witness,
82
- headers: auth ? { Authorization: auth } : undefined,
83
- }).send(bytes);
84
- if (outcome.kind === "refuse")
85
- throw outcome.error;
103
+ }
104
+ /**
105
+ * Send one message to a witness for callers that require delivery.
106
+ *
107
+ * @throws The delivery's remote failure (missing endpoint, peer refusal, transport) and any local failure.
108
+ */
109
+ export function* sendWitnessMessage(hab, witness, bytes, http, clock, protocol, auth) {
110
+ const delivery = yield* deliverWitnessMessage(hab, witness, bytes, http, clock, protocol, auth);
111
+ if (delivery.kind === "unreachable")
112
+ throw delivery.error;
86
113
  }
87
114
  function isKeripyContentTypeRejection(status, contentType, responseText) {
88
115
  return status === 406
@@ -8,8 +8,12 @@ import { dgKey } from "../../db/core/keys.js";
8
8
  import { acceptedEventReplayMessage } from "../identity/habbing.js";
9
9
  import { chunkBytes } from "../transport/byte-source.js";
10
10
  import { processMessages } from "../transport/message-source.js";
11
- import { getWitnessReceiptEndpoint, pollWitnessReceipt, postWitnessReceiptEndpoint, sendWitnessMessage, witnessHttpUrl, witnessTcpUrl, } from "./receipt-client.js";
11
+ import { TransportError } from "../transport/transport-error.js";
12
+ import { deliverWitnessMessage, getWitnessReceiptEndpoint, pollWitnessReceipt, postWitnessReceiptEndpoint, witnessHttpUrl, witnessTcpUrl, } from "./receipt-client.js";
12
13
  import { buildDetachedReceiptMessage } from "./receipt-messages.js";
14
+ function incomplete(witness, error) {
15
+ return { kind: "incomplete", witness, error };
16
+ }
13
17
  /** Build one `loc/scheme` replay bundle for witness introductions. */
14
18
  function* witnessSchemeReplies(hab, eids, gvrsn) {
15
19
  const replies = eids
@@ -94,7 +98,17 @@ export class Receiptor {
94
98
  this.gvrsn = gvrsn;
95
99
  }
96
100
  /** Submit the latest or specified event to all witnesses and gather receipts. */
97
- *receipt(pre, { sn, auths = {}, allowTcp = false } = {}) {
101
+ *receipt(pre, options = {}) {
102
+ const outcome = yield* this.attempt(pre, options);
103
+ if (outcome.kind === "incomplete")
104
+ throw outcome.error;
105
+ return outcome.result;
106
+ }
107
+ /**
108
+ * Submit like `receipt`, but report a witness that is unreachable, refuses, or does not receipt as
109
+ * `incomplete` data. Local faults (unknown local AID, missing key state, storage) still throw.
110
+ */
111
+ *attempt(pre, { sn, auths = {}, allowTcp = false } = {}) {
98
112
  return yield* call(() => this.receiptOnce(pre, { sn, auths }, allowTcp));
99
113
  }
100
114
  /**
@@ -118,11 +132,13 @@ export class Receiptor {
118
132
  const { serder, message } = acceptedEventReplayMessage(this.hby, pre, eventSn, this.gvrsn);
119
133
  const witnesses = this.hby.db.acceptedEventWitnesses(serder);
120
134
  if (witnesses.length === 0) {
121
- return { witnesses: [], statuses: {} };
135
+ return { kind: "receipted", result: { witnesses: [], statuses: {} } };
122
136
  }
123
137
  if (serder.ilk === Ilks.rot || serder.ilk === Ilks.drt) {
124
138
  for (const witness of serder.adds) {
125
- yield* this.catchup(pre, witness);
139
+ const caughtUp = yield* this.replayHistory(hab, witness);
140
+ if (caughtUp.kind === "unreachable")
141
+ return incomplete(witness, caughtUp.error);
126
142
  }
127
143
  }
128
144
  const receiptGroups = new Map();
@@ -134,23 +150,35 @@ export class Receiptor {
134
150
  for (const witness of witnesses) {
135
151
  if (kever.delegated) {
136
152
  for (const dmsg of this.hby.db.cloneDelegation(kever, this.gvrsn)) {
137
- yield* sendWitnessMessage(hab, witness, this.selected(dmsg), this.http, this.clock, this.protocol);
153
+ const delivery = yield* deliverWitnessMessage(hab, witness, this.selected(dmsg), this.http, this.clock, this.protocol);
154
+ if (delivery.kind === "unreachable")
155
+ return incomplete(witness, delivery.error);
138
156
  }
139
157
  }
140
158
  if (allowTcp && !witnessHttpUrl(hab, witness) && witnessTcpUrl(hab, witness)) {
141
- yield* sendWitnessMessage(hab, witness, this.selected(message), this.http, this.clock, this.protocol);
159
+ const delivery = yield* deliverWitnessMessage(hab, witness, this.selected(message), this.http, this.clock, this.protocol);
160
+ if (delivery.kind === "unreachable")
161
+ return incomplete(witness, delivery.error);
142
162
  statuses[witness] = 200;
143
163
  }
144
164
  else {
145
165
  // A single receipt attempt owns its response until parsing and collection finish.
146
- let response = yield* call(() => postWitnessReceiptEndpoint(hab, witness, message, this.http, auths[witness], this.gvrsn));
147
- if (response.kind === "escrow") {
148
- response = yield* pollWitnessReceipt(hab, witness, pre, eventSn, said, this.http, this.gvrsn);
166
+ let response;
167
+ try {
168
+ response = yield* call(() => postWitnessReceiptEndpoint(hab, witness, message, this.http, auths[witness], this.gvrsn));
169
+ if (response.kind === "escrow") {
170
+ response = yield* pollWitnessReceipt(hab, witness, pre, eventSn, said, this.http, this.gvrsn);
171
+ }
172
+ }
173
+ catch (error) {
174
+ if (!(error instanceof TransportError))
175
+ throw error;
176
+ return incomplete(witness, error);
149
177
  }
150
178
  statuses[witness] = response.status;
151
179
  if (response.kind !== "accepted") {
152
180
  const detail = response.kind === "reject" ? response.message : "receipt remained escrowed";
153
- throw new ValidationError(`Witness ${witness} failed to receipt ${pre}:${eventSn.toString(16)}: ${detail}`);
181
+ return incomplete(witness, new ValidationError(`Witness ${witness} failed to receipt ${pre}:${eventSn.toString(16)}: ${detail}`));
154
182
  }
155
183
  yield* processMessages((signal) => chunkBytes(response.body, { signal }), this.protocol, this.kvy, {
156
184
  local: false,
@@ -161,7 +189,7 @@ export class Receiptor {
161
189
  const index = witnesses.indexOf(witness);
162
190
  const wigers = this.hby.db.wigs.get(dgKey(pre, said)).filter((siger) => siger.index === index);
163
191
  if (wigers.length === 0) {
164
- throw new ValidationError(`Witness ${witness} did not receipt ${pre}:${eventSn.toString(16)}.`);
192
+ return incomplete(witness, new ValidationError(`Witness ${witness} did not receipt ${pre}:${eventSn.toString(16)}.`));
165
193
  }
166
194
  receiptGroups.set(witness, wigers);
167
195
  }
@@ -190,9 +218,11 @@ export class Receiptor {
190
218
  pipelined: true,
191
219
  genusify: true,
192
220
  }));
193
- yield* sendWitnessMessage(hab, witness, concatBytes(...parts), this.http, this.clock, this.protocol);
221
+ const delivery = yield* deliverWitnessMessage(hab, witness, concatBytes(...parts), this.http, this.clock, this.protocol);
222
+ if (delivery.kind === "unreachable")
223
+ return incomplete(witness, delivery.error);
194
224
  }
195
- return { witnesses, statuses };
225
+ return { kind: "receipted", result: { witnesses, statuses } };
196
226
  }
197
227
  /**
198
228
  * Query a stored witness receipt and apply it to local state when found.
@@ -243,15 +273,27 @@ export class Receiptor {
243
273
  if (!hab) {
244
274
  throw new ValidationError(`${pre} not a valid local AID.`);
245
275
  }
276
+ const delivery = yield* this.replayHistory(hab, witness);
277
+ if (delivery.kind === "unreachable")
278
+ throw delivery.error;
279
+ }
280
+ /** Replay a delegated inception's delegation chain and the KEL to one witness, stopping at the first failure. */
281
+ *replayHistory(hab, witness) {
246
282
  const kever = hab.kever;
247
- if (kever?.delegated) {
248
- for (const msg of this.hby.db.cloneDelegation(kever, this.gvrsn)) {
249
- yield* sendWitnessMessage(hab, witness, this.selected(msg), this.http, this.clock, this.protocol);
250
- }
251
- }
252
- for (const msg of this.hby.db.clonePreIter(pre, 0, this.gvrsn)) {
253
- yield* sendWitnessMessage(hab, witness, this.selected(msg), this.http, this.clock, this.protocol);
283
+ const db = this.hby.db;
284
+ const gvrsn = this.gvrsn;
285
+ const pre = hab.pre;
286
+ const messages = (function* () {
287
+ if (kever?.delegated)
288
+ yield* db.cloneDelegation(kever, gvrsn);
289
+ yield* db.clonePreIter(pre, 0, gvrsn);
290
+ })();
291
+ for (const msg of messages) {
292
+ const delivery = yield* deliverWitnessMessage(hab, witness, this.selected(msg), this.http, this.clock, this.protocol);
293
+ if (delivery.kind === "unreachable")
294
+ return delivery;
254
295
  }
296
+ return { kind: "delivered" };
255
297
  }
256
298
  /** Complete streams select their framing before the body; detached receipt headers retain their existing enclosure. */
257
299
  selected(message) {
@@ -16,7 +16,7 @@ import { telEventStatus, waitForIssuedCredential, waitForTelEvent, } from "../..
16
16
  import { endpointRoleAccepted, isLocalGroupHab, localGroupMember, proposeGroupEndpointRole, } from "../../app/identity/endpoint-roleing.js";
17
17
  import { MULTISIG_REV_ROUTE, MULTISIG_ROT_ROUTE, MULTISIG_VCP_ROUTE } from "../../app/multisig/grouping.js";
18
18
  import { routeForKelEvent } from "../../app/multisig/multisig-workflows.js";
19
- import { proposeGroupInception, proposeGroupInteraction, proposeGroupRotation, settleProposedRotation, } from "../../app/multisig/proposing.js";
19
+ import { proposeGroupInception, proposeGroupInteraction, proposeGroupRotation, } from "../../app/multisig/proposing.js";
20
20
  import { observeUntil } from "../../app/runtime/workflow-contract.js";
21
21
  import { ValidationError } from "../../core/errors.js";
22
22
  import { isV2RegistryRoute } from "../../core/exchange/protocol-exchanging.js";
@@ -272,8 +272,9 @@ export function* multisigRotateCommand(args) {
272
272
  let delegationPhase = null;
273
273
  let rootCopies = [];
274
274
  if (accepted) {
275
- // The proposer's own acceptance must commit the rotated membership as a joiner's does.
276
- settleProposedRotation({ hby }, sent);
275
+ // Acceptance was read straight from the KEL, possibly before the Counselor's pass: follow it now, since the
276
+ // root copies below go to the rotated members.
277
+ hby.alignGroupMembers(sent.group);
277
278
  delegationPhase = yield* finishAcceptedEvent(hby, runtime, sent.event, commandArgs);
278
279
  rootCopies = yield* resealAfterRotation(hby, runtime, sent.group, sent.event.said, since);
279
280
  }
@@ -32,6 +32,34 @@ export class ValidationError extends AppError {
32
32
  });
33
33
  }
34
34
  }
35
+ /**
36
+ * A caller asked to treat a KRAM refusal as a failure. Carries the refused message SAID and the refusal decision;
37
+ * the message names the reason and, when the refusal has one, the last instant the message was acceptable.
38
+ */
39
+ export class KramRefusedError extends ValidationError {
40
+ constructor(said, refusal) {
41
+ super(`KRAM refused message ${said}: ${refusal.reason}`
42
+ + (refusal.deadline ? ` (acceptable until ${refusal.deadline})` : ""));
43
+ Object.defineProperty(this, "said", {
44
+ enumerable: true,
45
+ configurable: true,
46
+ writable: true,
47
+ value: said
48
+ });
49
+ Object.defineProperty(this, "refusal", {
50
+ enumerable: true,
51
+ configurable: true,
52
+ writable: true,
53
+ value: refusal
54
+ });
55
+ Object.defineProperty(this, "name", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: "KramRefusedError"
60
+ });
61
+ }
62
+ }
35
63
  /** Filesystem or path-resolution failure from path-managed resources. */
36
64
  export class PathError extends AppError {
37
65
  constructor() {
@@ -120,3 +148,48 @@ export class DatabaseOperationError extends DatabaseError {
120
148
  });
121
149
  }
122
150
  }
151
+ /**
152
+ * Local durable state that the store's own records say must exist is missing or contradicts itself: a local group
153
+ * names a member habitat the store does not hold, for example. Never a remote outcome: no peer message removes a
154
+ * local record, so a long-running loop does not survive it and lets it end the runtime. Recovery is repairing or
155
+ * replacing the store, not retrying.
156
+ */
157
+ export class MissingDurableStateError extends DatabaseError {
158
+ constructor() {
159
+ super(...arguments);
160
+ Object.defineProperty(this, "name", {
161
+ enumerable: true,
162
+ configurable: true,
163
+ writable: true,
164
+ value: "MissingDurableStateError"
165
+ });
166
+ }
167
+ }
168
+ /**
169
+ * A browser composition's persistence queue gave up on a write after its retries (ADR-0015 rule 3), so it accepts
170
+ * no more commits and reports this from every later commit, flush and close. The cause is the write's first failure.
171
+ * The latch is terminal for that composition; recovery is closing it and opening a new one.
172
+ */
173
+ export class PersistenceLatchedError extends DatabaseOperationError {
174
+ constructor() {
175
+ super(...arguments);
176
+ Object.defineProperty(this, "name", {
177
+ enumerable: true,
178
+ configurable: true,
179
+ writable: true,
180
+ value: "PersistenceLatchedError"
181
+ });
182
+ }
183
+ }
184
+ /** A task passed to `PersistenceQueue.read` failed. Only its caller sees it; the queue does not latch. */
185
+ export class PersistenceReadError extends DatabaseOperationError {
186
+ constructor() {
187
+ super(...arguments);
188
+ Object.defineProperty(this, "name", {
189
+ enumerable: true,
190
+ configurable: true,
191
+ writable: true,
192
+ value: "PersistenceReadError"
193
+ });
194
+ }
195
+ }
@@ -1,8 +1,17 @@
1
1
  import { dgKey } from "../../db/core/keys.js";
2
2
  import { ValidationError } from "../errors.js";
3
- /** Whether one accepted delegator event carries the exact `{i,s,d}` seal of a delegated inception. */
3
+ import { readEventSeal } from "../wire-fields.js";
4
+ /**
5
+ * Whether one accepted delegator event carries the exact `{i,s,d}` seal of a delegated inception.
6
+ *
7
+ * The delegator's seals are remote-authored and unchecked at acceptance; other shapes and undecodable values are
8
+ * skipped (`readEventSeal`), never thrown.
9
+ */
4
10
  function approves(event, dip) {
5
- return event.eventSeals.some((seal) => seal.i.qb64 === dip.pre && seal.s.numh === dip.snh && seal.d.qb64 === dip.said);
11
+ return event.seals.some((raw) => {
12
+ const seal = readEventSeal(raw);
13
+ return seal !== null && seal.i.qb64 === dip.pre && seal.s.numh === dip.snh && seal.d.qb64 === dip.said;
14
+ });
6
15
  }
7
16
  /**
8
17
  * Find the delegator's accepted approval of `dip`.
@@ -8,6 +8,7 @@ import { normalizeMbxTopicCursor } from "../mailbox/mailbox-topics.js";
8
8
  import { reply as replyEvent } from "../protocol-eventing.js";
9
9
  import { KeyStateRecord, ObservedRecord } from "../records.js";
10
10
  import { acceptReplyDecision, unverifiedReplyDecision } from "../routing.js";
11
+ import { kelEventFieldDefect, readDater, readDiger, readOrdinal, readPrefixer, readSequenceNumber, } from "../wire-fields.js";
11
12
  import { acceptEscrow, acceptQuery, acceptReceipt, dropEscrow, dropQuery, dropReceipt, escrowQuery, escrowReceipt, ignoreReceipt, keepEscrow, logEscrowDecision, } from "./kever-decisions.js";
12
13
  import { Kever } from "./kever.js";
13
14
  import { deriveRotatedWitnessSet, hasUniqueWitnesses } from "./witnesses.js";
@@ -365,14 +366,15 @@ export class Kevery {
365
366
  processReceipt(envelope) {
366
367
  const serder = envelope.serder;
367
368
  const pre = serder.pre;
368
- const sn = serder.sn;
369
+ // Decided before any escrow: a remote receipt's sn and prefix are read through the guarded readers.
370
+ const sn = readSequenceNumber(serder);
369
371
  const said = serder.said;
370
372
  const local = envelope.local ?? this.local;
371
- if (!pre || sn === null || !said) {
373
+ if (!pre || !readPrefixer(pre) || sn === null || !said) {
372
374
  this.cues.push({
373
375
  kin: "invalid",
374
376
  serder,
375
- reason: "Receipt message is missing receipted pre, sn, or said.",
377
+ reason: "Receipt message is missing a valid receipted pre, hex sn, or said.",
376
378
  });
377
379
  return;
378
380
  }
@@ -611,23 +613,22 @@ export class Kevery {
611
613
  const pre = serder.pre;
612
614
  const ilk = serder.ilk;
613
615
  const said = serder.said;
614
- const sn = serder.sn;
616
+ // Every remote field value is decided here, before any escrow or store write: escrow replay re-enters through
617
+ // this method, so a row stored with a malformed value is rejected and removed instead of failing every pass.
618
+ const sn = readSequenceNumber(serder);
615
619
  if (!pre || !ilk || !said || sn === null) {
616
620
  return {
617
621
  kind: "reject",
618
622
  code: !pre ? "invalidPre" : !ilk ? "invalidIlk" : "invalidSn",
619
- message: "KEL event must include pre, ilk, said, and sn.",
623
+ message: `KEL event must include pre, ilk, said, and a hex sn of at most ${Number.MAX_SAFE_INTEGER}.`,
620
624
  };
621
625
  }
622
- try {
623
- new Prefixer({ qb64: pre });
626
+ if (!readPrefixer(pre)) {
627
+ return { kind: "reject", code: "invalidPre", message: `Invalid pre=${pre} for event ${said}.` };
624
628
  }
625
- catch (error) {
626
- return {
627
- kind: "reject",
628
- code: "invalidPre",
629
- message: `Invalid pre=${pre} for event ${said}: ${error instanceof Error ? error.message : String(error)}`,
630
- };
629
+ const defect = kelEventFieldDefect(serder);
630
+ if (defect) {
631
+ return { kind: "reject", code: "malformedField", message: `Malformed ${defect} in event ${said} for ${pre}.` };
631
632
  }
632
633
  const local = envelope.local ?? this.local;
633
634
  const init = this.makeKeverEventInit(envelope, local);
@@ -939,16 +940,18 @@ export class Kevery {
939
940
  throw new ValidationError(`Unsupported route=${args.route} in ${Ilks.rpy} reply.`);
940
941
  }
941
942
  const ksn = KeyStateRecord.fromDict(args.serder.ked?.a);
942
- const pre = typeof ksn.i === "string" ? ksn.i : null;
943
- const sn = typeof ksn.s === "string" ? parseInt(ksn.s, 16) : null;
944
- const said = typeof ksn.d === "string" ? ksn.d : null;
945
- const dt = typeof ksn.dt === "string" ? ksn.dt : null;
946
- if (!pre || sn === null || Number.isNaN(sn) || !said || !dt) {
943
+ // Decide the notice's field values before decideReply can escrow it.
944
+ const pre = readPrefixer(ksn.i)?.qb64 ?? null;
945
+ const sn = typeof ksn.s === "string" ? readOrdinal(ksn.s) : null;
946
+ const saider = readDiger(ksn.d);
947
+ const dater = readDater(ksn.dt);
948
+ if (!pre || sn === null || !saider || !dater) {
947
949
  throw new ValidationError("Malformed key state notice reply body.");
948
950
  }
951
+ const said = saider.qb64;
949
952
  if (!this.lax) {
950
953
  const watchers = this.configuredWatchers();
951
- const backers = ksn.b ?? [];
954
+ const backers = Array.isArray(ksn.b) ? ksn.b : [];
952
955
  // In non-lax mode, only the controller, a reported KSN backer, or a
953
956
  // locally configured watcher may advance saved key state.
954
957
  const trusted = args.aid === pre
@@ -986,8 +989,6 @@ export class Kevery {
986
989
  if (ldig && ldig !== said) {
987
990
  throw new ValidationError(`Mismatch key state at sn=${sn} with accepted event log for ${pre}.`);
988
991
  }
989
- const saider = new Diger({ qb64: said });
990
- const dater = new Dater({ qb64: encodeDateTimeToDater(dt) });
991
992
  const cues = [{ kin: "keyStateSaved", ksn }];
992
993
  this.rvy.writeReply(reply);
993
994
  this.updateKeyState(args.aid, ksn, saider, dater);
@@ -1025,15 +1026,11 @@ export class Kevery {
1025
1026
  throw new ValidationError(`Unsupported route=${args.route} in ${Ilks.rpy} reply.`);
1026
1027
  }
1027
1028
  const data = args.serder.ked?.a;
1028
- const cid = typeof data?.cid === "string"
1029
- ? new Prefixer({ qb64: data.cid }).qb64
1030
- : null;
1031
- const oid = typeof data?.oid === "string"
1032
- ? new Prefixer({ qb64: data.oid }).qb64
1033
- : null;
1029
+ const cid = readPrefixer(data?.cid)?.qb64 ?? null;
1030
+ const oid = readPrefixer(data?.oid)?.qb64 ?? null;
1034
1031
  const oobi = typeof data?.oobi === "string" ? data.oobi : null;
1035
1032
  if (!cid || !oid) {
1036
- throw new ValidationError("Missing one of cid/oid in /watcher reply.");
1033
+ throw new ValidationError("Missing or malformed one of cid/oid in /watcher reply.");
1037
1034
  }
1038
1035
  const keys = [cid, args.aid, oid];
1039
1036
  let osaider = this.db.wwas.get(keys);
@@ -1,8 +1,9 @@
1
- import { Dater, Diger, Ilks, NumberPrimitive, Prefixer, SealSource, Siger, Tholder, Verfer, } from "cesr-ts";
1
+ import { Dater, Diger, Ilks, NumberPrimitive, Prefixer, SealEvent, SealSource, Siger, Tholder, Verfer, } from "cesr-ts";
2
2
  import { dgKey } from "../../db/core/keys.js";
3
3
  import { encodeDateTimeToDater, makeNowIso8601 } from "../../time/mod.js";
4
4
  import { ValidationError } from "../errors.js";
5
5
  import { KeyStateRecord } from "../records.js";
6
+ import { readEventSeal } from "../wire-fields.js";
6
7
  import { deriveRotatedWitnessSet } from "./witnesses.js";
7
8
  /**
8
9
  * Convert one integer into the Huge-number CESR family used for durable replay
@@ -1434,16 +1435,21 @@ export class Kever {
1434
1435
  }
1435
1436
  return this.db.getState(pre)?.di || null;
1436
1437
  }
1437
- /** Return the zero-based index of the matching event seal or `-1`. */
1438
+ /**
1439
+ * Return the zero-based index of the matching event seal among the candidate's event-shaped seals, or `-1`.
1440
+ *
1441
+ * The candidate is an accepted delegator event, which is remote-authored: its `a` may hold any seal shape and
1442
+ * undecodable values. Like KERIpy, the index counts entries with the `{i, s, d}` field shape; other shapes and
1443
+ * malformed values never match (`readEventSeal`).
1444
+ */
1438
1445
  eventAnchorSealIndex(candidate, serder) {
1439
- for (const [index, seal] of candidate.eventSeals.entries()) {
1440
- if (seal.i.qb64 === serder.pre
1446
+ return candidate.seals.filter((raw) => SealEvent.isSad(raw)).findIndex((raw) => {
1447
+ const seal = readEventSeal(raw);
1448
+ return seal !== null
1449
+ && seal.i.qb64 === serder.pre
1441
1450
  && seal.s.numh === serder.snh
1442
- && seal.d.qb64 === serder.said) {
1443
- return index;
1444
- }
1445
- }
1446
- return -1;
1451
+ && seal.d.qb64 === serder.said;
1452
+ });
1447
1453
  }
1448
1454
  /**
1449
1455
  * Verify one indexed-signature set against a given verifier list.
@@ -29,6 +29,14 @@ function storedCacheType(db, ilk, route) {
29
29
  export function singleKeyQueryWindow(db, route) {
30
30
  return storedCacheType(db, "qry", route)?.sl ?? null;
31
31
  }
32
+ /**
33
+ * How long an exchange of a message of `ilk` on `route` stays open after its opener under the windows this store
34
+ * is configured with now, in milliseconds: the exchange lag `xl`. Null when this store has no KRAM cache types
35
+ * (KRAM never ran on it).
36
+ */
37
+ export function exchangeWindow(db, ilk, route) {
38
+ return storedCacheType(db, ilk, route)?.xl ?? null;
39
+ }
32
40
  /** Validate caller configuration before any durable policy mutation. */
33
41
  function windows(value, transitional = false) {
34
42
  if (!Array.isArray(value) || value.length !== 7 || value.some((v) => !Number.isSafeInteger(v) || v < 0)) {