keri-ts 2.2.4 → 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 (233) 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 +32 -10
  5. package/esm/keri/npm/src/app/bootstrap/oobiery.js +363 -30
  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 +177 -43
  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 +71 -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 +47 -21
  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 +180 -34
  47. package/esm/keri/npm/src/app/runtime/agent-runtime.js +41 -72
  48. package/esm/keri/npm/src/app/runtime/cue-runtime.js +45 -5
  49. package/esm/keri/npm/src/app/runtime/operation-owners.js +26 -56
  50. package/esm/keri/npm/src/app/runtime/workflow-contract.js +7 -4
  51. package/esm/keri/npm/src/app/transport/http-client.js +20 -2
  52. package/esm/keri/npm/src/app/transport/message-source.js +2 -1
  53. package/esm/keri/npm/src/app/transport/messengering.js +39 -8
  54. package/esm/keri/npm/src/app/witness/ingress.js +7 -0
  55. package/esm/keri/npm/src/app/witness/receipt-client.js +43 -16
  56. package/esm/keri/npm/src/app/witness/receipting.js +62 -20
  57. package/esm/keri/npm/src/cli/messaging/multisig.js +4 -3
  58. package/esm/keri/npm/src/core/errors.js +73 -0
  59. package/esm/keri/npm/src/core/kel/delegation-ancestry.js +11 -2
  60. package/esm/keri/npm/src/core/kel/eventing.js +25 -28
  61. package/esm/keri/npm/src/core/kel/kever.js +15 -9
  62. package/esm/keri/npm/src/core/mailbox/kram-policy.js +8 -0
  63. package/esm/keri/npm/src/core/mailbox/kram-pruning.js +25 -6
  64. package/esm/keri/npm/src/core/mailbox/kram-time.js +34 -6
  65. package/esm/keri/npm/src/core/mailbox/kraming.js +58 -2
  66. package/esm/keri/npm/src/core/routing.js +18 -25
  67. package/esm/keri/npm/src/core/scheming.js +12 -5
  68. package/esm/keri/npm/src/core/wire-fields.js +126 -0
  69. package/esm/keri/npm/src/db/basing.js +4 -13
  70. package/esm/keri/npm/src/db/core/indexeddb-persistence.js +3 -2
  71. package/esm/keri/npm/src/db/core/webdber.js +85 -27
  72. package/esm/keri/npm/src/db/mailbox/mailboxing.js +13 -0
  73. package/esm/keri/npm/src/db/subing.js +4 -0
  74. package/esm/keri/npm/src/runtime/index.js +11 -5
  75. package/esm/keri/npm/src/time/mod.js +5 -0
  76. package/esm/keri/npm/src/vdr/credential-tsn-record.js +2 -7
  77. package/esm/keri/npm/src/vdr/eventing.js +28 -19
  78. package/esm/keri/npm/src/vdr/registry-tsn-record.js +3 -13
  79. package/esm/keri/npm/src/version.js +2 -2
  80. package/package.json +1 -1
  81. package/types/_dnt.polyfills.d.ts +9 -9
  82. package/types/_dnt.polyfills.d.ts.map +1 -1
  83. package/types/acdc/registering.d.ts +6 -0
  84. package/types/acdc/registering.d.ts.map +1 -1
  85. package/types/acdc/salting.d.ts.map +1 -1
  86. package/types/app/bootstrap/authenticating.d.ts +12 -2
  87. package/types/app/bootstrap/authenticating.d.ts.map +1 -1
  88. package/types/app/bootstrap/oobiery.d.ts +144 -7
  89. package/types/app/bootstrap/oobiery.d.ts.map +1 -1
  90. package/types/app/credentials/credential-importing.d.ts +8 -0
  91. package/types/app/credentials/credential-importing.d.ts.map +1 -1
  92. package/types/app/credentials/root-replicator.d.ts +19 -8
  93. package/types/app/credentials/root-replicator.d.ts.map +1 -1
  94. package/types/app/credentials/schema-resolving.d.ts.map +1 -1
  95. package/types/app/credentials/tel-publishing.d.ts +4 -2
  96. package/types/app/credentials/tel-publishing.d.ts.map +1 -1
  97. package/types/app/delivery/forwarding.d.ts +69 -5
  98. package/types/app/delivery/forwarding.d.ts.map +1 -1
  99. package/types/app/delivery/relaying.d.ts +144 -0
  100. package/types/app/delivery/relaying.d.ts.map +1 -0
  101. package/types/app/delivery/respondant.d.ts +9 -2
  102. package/types/app/delivery/respondant.d.ts.map +1 -1
  103. package/types/app/exchanges/exchanging.d.ts +36 -0
  104. package/types/app/exchanges/exchanging.d.ts.map +1 -1
  105. package/types/app/identity/delegating.d.ts +6 -0
  106. package/types/app/identity/delegating.d.ts.map +1 -1
  107. package/types/app/identity/habbing.d.ts +106 -19
  108. package/types/app/identity/habbing.d.ts.map +1 -1
  109. package/types/app/identity/keeping.d.ts +54 -19
  110. package/types/app/identity/keeping.d.ts.map +1 -1
  111. package/types/app/identity/retire-identifier.d.ts +24 -0
  112. package/types/app/identity/retire-identifier.d.ts.map +1 -0
  113. package/types/app/ipex/disclosure-replaying.d.ts +61 -0
  114. package/types/app/ipex/disclosure-replaying.d.ts.map +1 -0
  115. package/types/app/ipex/group-ipex.d.ts +60 -14
  116. package/types/app/ipex/group-ipex.d.ts.map +1 -1
  117. package/types/app/ipex/group-issuance.d.ts +48 -6
  118. package/types/app/ipex/group-issuance.d.ts.map +1 -1
  119. package/types/app/ipex/ipex-discloser.d.ts +14 -2
  120. package/types/app/ipex/ipex-discloser.d.ts.map +1 -1
  121. package/types/app/ipex/ipex-grouping.d.ts +23 -2
  122. package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
  123. package/types/app/ipex/ipex-v2.d.ts +5 -2
  124. package/types/app/ipex/ipex-v2.d.ts.map +1 -1
  125. package/types/app/ipex/issuance.d.ts +35 -11
  126. package/types/app/ipex/issuance.d.ts.map +1 -1
  127. package/types/app/ipex/pending-presentations.d.ts +4 -2
  128. package/types/app/ipex/pending-presentations.d.ts.map +1 -1
  129. package/types/app/ipex/presenting.d.ts.map +1 -1
  130. package/types/app/ipex/receive-issuance.d.ts +2 -1
  131. package/types/app/ipex/receive-issuance.d.ts.map +1 -1
  132. package/types/app/ipex/unblinding-workflow.d.ts +4 -1
  133. package/types/app/ipex/unblinding-workflow.d.ts.map +1 -1
  134. package/types/app/mailbox/agent-inbox.d.ts +126 -0
  135. package/types/app/mailbox/agent-inbox.d.ts.map +1 -0
  136. package/types/app/mailbox/forward-handler.d.ts +114 -14
  137. package/types/app/mailbox/forward-handler.d.ts.map +1 -1
  138. package/types/app/mailbox/kel-publisher.d.ts +12 -22
  139. package/types/app/mailbox/kel-publisher.d.ts.map +1 -1
  140. package/types/app/mailbox/mailbox-poller.d.ts +40 -24
  141. package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
  142. package/types/app/mailbox/mailboxing.d.ts +19 -5
  143. package/types/app/mailbox/mailboxing.d.ts.map +1 -1
  144. package/types/app/multisig/counseling.d.ts +32 -11
  145. package/types/app/multisig/counseling.d.ts.map +1 -1
  146. package/types/app/multisig/group-members.d.ts +17 -1
  147. package/types/app/multisig/group-members.d.ts.map +1 -1
  148. package/types/app/multisig/grouping.d.ts +17 -1
  149. package/types/app/multisig/grouping.d.ts.map +1 -1
  150. package/types/app/multisig/multisig-workflows.d.ts +0 -8
  151. package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
  152. package/types/app/multisig/proposing.d.ts +4 -19
  153. package/types/app/multisig/proposing.d.ts.map +1 -1
  154. package/types/app/multisig/rejoin-group.d.ts +119 -0
  155. package/types/app/multisig/rejoin-group.d.ts.map +1 -0
  156. package/types/app/multisig/replace-member.d.ts +15 -1
  157. package/types/app/multisig/replace-member.d.ts.map +1 -1
  158. package/types/app/protocol/parsering.d.ts +21 -2
  159. package/types/app/protocol/parsering.d.ts.map +1 -1
  160. package/types/app/protocol/protocol-processing.d.ts +25 -5
  161. package/types/app/protocol/protocol-processing.d.ts.map +1 -1
  162. package/types/app/queries/query-replies.d.ts +4 -8
  163. package/types/app/queries/query-replies.d.ts.map +1 -1
  164. package/types/app/queries/relay-refresh.d.ts +26 -0
  165. package/types/app/queries/relay-refresh.d.ts.map +1 -0
  166. package/types/app/queries/witness-inquisitor.d.ts +56 -5
  167. package/types/app/queries/witness-inquisitor.d.ts.map +1 -1
  168. package/types/app/runtime/agent-runtime.d.ts +34 -3
  169. package/types/app/runtime/agent-runtime.d.ts.map +1 -1
  170. package/types/app/runtime/cue-runtime.d.ts +15 -3
  171. package/types/app/runtime/cue-runtime.d.ts.map +1 -1
  172. package/types/app/runtime/operation-owners.d.ts +16 -26
  173. package/types/app/runtime/operation-owners.d.ts.map +1 -1
  174. package/types/app/runtime/workflow-contract.d.ts +21 -6
  175. package/types/app/runtime/workflow-contract.d.ts.map +1 -1
  176. package/types/app/transport/http-client.d.ts +9 -0
  177. package/types/app/transport/http-client.d.ts.map +1 -1
  178. package/types/app/transport/message-source.d.ts +6 -3
  179. package/types/app/transport/message-source.d.ts.map +1 -1
  180. package/types/app/transport/messengering.d.ts +10 -1
  181. package/types/app/transport/messengering.d.ts.map +1 -1
  182. package/types/app/witness/ingress.d.ts +1 -0
  183. package/types/app/witness/ingress.d.ts.map +1 -1
  184. package/types/app/witness/receipt-client.d.ts +19 -1
  185. package/types/app/witness/receipt-client.d.ts.map +1 -1
  186. package/types/app/witness/receipting.d.ts +24 -1
  187. package/types/app/witness/receipting.d.ts.map +1 -1
  188. package/types/cli/messaging/multisig.d.ts.map +1 -1
  189. package/types/core/cues.d.ts +53 -1
  190. package/types/core/cues.d.ts.map +1 -1
  191. package/types/core/errors.d.ts +32 -0
  192. package/types/core/errors.d.ts.map +1 -1
  193. package/types/core/kel/delegation-ancestry.d.ts.map +1 -1
  194. package/types/core/kel/eventing.d.ts.map +1 -1
  195. package/types/core/kel/kever-decisions.d.ts +3 -1
  196. package/types/core/kel/kever-decisions.d.ts.map +1 -1
  197. package/types/core/kel/kever.d.ts +7 -1
  198. package/types/core/kel/kever.d.ts.map +1 -1
  199. package/types/core/mailbox/kram-policy.d.ts +6 -0
  200. package/types/core/mailbox/kram-policy.d.ts.map +1 -1
  201. package/types/core/mailbox/kram-pruning.d.ts +5 -1
  202. package/types/core/mailbox/kram-pruning.d.ts.map +1 -1
  203. package/types/core/mailbox/kram-time.d.ts +26 -2
  204. package/types/core/mailbox/kram-time.d.ts.map +1 -1
  205. package/types/core/mailbox/kraming.d.ts +14 -1
  206. package/types/core/mailbox/kraming.d.ts.map +1 -1
  207. package/types/core/routing.d.ts +3 -7
  208. package/types/core/routing.d.ts.map +1 -1
  209. package/types/core/scheming.d.ts.map +1 -1
  210. package/types/core/wire-fields.d.ts +56 -0
  211. package/types/core/wire-fields.d.ts.map +1 -0
  212. package/types/db/basing.d.ts.map +1 -1
  213. package/types/db/core/indexeddb-persistence.d.ts +3 -2
  214. package/types/db/core/indexeddb-persistence.d.ts.map +1 -1
  215. package/types/db/core/webdber.d.ts +32 -6
  216. package/types/db/core/webdber.d.ts.map +1 -1
  217. package/types/db/exchange/exchange-records.d.ts +4 -0
  218. package/types/db/exchange/exchange-records.d.ts.map +1 -1
  219. package/types/db/mailbox/mailboxing.d.ts +10 -1
  220. package/types/db/mailbox/mailboxing.d.ts.map +1 -1
  221. package/types/db/subing.d.ts +2 -0
  222. package/types/db/subing.d.ts.map +1 -1
  223. package/types/runtime/index.d.ts +20 -14
  224. package/types/runtime/index.d.ts.map +1 -1
  225. package/types/time/mod.d.ts +2 -0
  226. package/types/time/mod.d.ts.map +1 -1
  227. package/types/vdr/credential-tsn-record.d.ts.map +1 -1
  228. package/types/vdr/eventing.d.ts.map +1 -1
  229. package/types/vdr/registry-tsn-record.d.ts.map +1 -1
  230. package/types/version.d.ts +2 -2
  231. package/esm/keri/npm/src/app/runtime/signer-conditions.js +0 -51
  232. package/types/app/runtime/signer-conditions.d.ts +0 -32
  233. package/types/app/runtime/signer-conditions.d.ts.map +0 -1
@@ -1,4 +1,4 @@
1
- import { Ilks, Prefixer, SerderKERI } from "cesr-ts";
1
+ import { Ilks, ParserError, Prefixer, SerderKERI, WireParseError } from "cesr-ts";
2
2
  import { call, resource, scoped } from "effection";
3
3
  import { ValidationError } from "../../core/errors.js";
4
4
  import { exchange } from "../../core/exchange/protocol-exchanging.js";
@@ -6,13 +6,31 @@ import { OOBI_MAILBOX_TOPIC } from "../../core/mailbox/mailbox-topics.js";
6
6
  import { Roles } from "../../core/roles.js";
7
7
  import { acceptReplyDecision, unverifiedReplyDecision } from "../../core/routing.js";
8
8
  import { Schemer } from "../../core/scheming.js";
9
+ import { isRelayOrigin, relayAuthorized, relayEndpoint, RelayOnlyError, relayRefusal, } from "../delivery/relaying.js";
9
10
  import { persistResolvedContact } from "../identity/organizing.js";
11
+ import { servingHost } from "../mailbox/forward-handler.js";
10
12
  import { abortable } from "../runtime/abortable.js";
11
13
  import { doEveryOnWrite, PROTOCOL_PACING } from "../runtime/doing.js";
12
14
  import { systemClock } from "../runtime/runtime-clock.js";
13
15
  import { collectBytes } from "../transport/byte-source.js";
16
+ import { isRequestableHttpUrl } from "../transport/http-client.js";
14
17
  import { processMessages } from "../transport/message-source.js";
18
+ import { TransportError } from "../transport/transport-error.js";
15
19
  export const OOBI_REQUEST_ROUTE = "/oobis";
20
+ /**
21
+ * How long an edge's OOBI job waits for its relay to hold the answer before it fails: 30 seconds, measured from the
22
+ * job's first relay attempt on the composed clock. It matches KERIpy's `Oobiery.RetryDelay` and the default
23
+ * `bootstrapOobis` timeout; the keri-ts Oobiery has no other retry, and a direct fetch fails on its first non-OK
24
+ * answer.
25
+ */
26
+ export const RELAY_OOBI_TIMEOUT_MS = 30_000;
27
+ /** Job states of an OOBI an edge resolves through its relay; any other state starts a new relay wait. */
28
+ const RelayOobiStates = Object.freeze({
29
+ /** Waiting; the relay has not been asked yet (its location or a serving authorization is missing). */
30
+ pending: "relay-pending",
31
+ /** Waiting; the relay was asked with `/oobis` and has not answered with the result yet. */
32
+ requested: "relay-requested",
33
+ });
16
34
  /**
17
35
  * Return the current terminal state projection for one requested OOBI URL.
18
36
  *
@@ -49,6 +67,29 @@ export function oobiTerminalState(db, url, kind = queueKindFor(url)) {
49
67
  }
50
68
  return { status: "pending", via: "none", record: null };
51
69
  }
70
+ /**
71
+ * Classify an error that ended one OOBI or authentication job: the {@link OobiJobFailure} a remote party can cause,
72
+ * or null for a fault that must propagate and end the runtime (a store error such as `DatabaseError`, corrupt
73
+ * durable state, a programmer error such as `TypeError`, a plain `Error`).
74
+ *
75
+ * Limits: a cesr `ParserError`, `SyntaxError` or `RangeError` raised while decoding local stored state during the
76
+ * same dispatch is indistinguishable by class from one raised by a remote field, so it also ends only that job.
77
+ *
78
+ * @param error What escaped the job's request or response ingestion.
79
+ * @returns The failure to record, or null to rethrow.
80
+ */
81
+ export function oobiJobFailure(error) {
82
+ if (error instanceof RelayOnlyError)
83
+ return "egress-refused";
84
+ if (error instanceof TransportError)
85
+ return "transport-failed";
86
+ if (error instanceof WireParseError)
87
+ return "unparseable-response";
88
+ if (error instanceof ParserError || error instanceof SyntaxError || error instanceof RangeError) {
89
+ return "invalid-response-frame";
90
+ }
91
+ return null;
92
+ }
52
93
  /**
53
94
  * Durable OOBI processing component for one `Habery`.
54
95
  *
@@ -71,10 +112,11 @@ export class Oobiery {
71
112
  * @param rvy Actual shared reply verifier used for OOBI reply proof persistence.
72
113
  * @param http Borrowed HTTP implementation selected by composition.
73
114
  * @param cues Required application result Deck supplied by composition.
115
+ * @param relaying Relay collaborators of an edge runtime; null (a forwarder) resolves every OOBI directly.
74
116
  */
75
117
  constructor(hby, protocol,
76
118
  /** Borrowed reply verifier; route installation and proof writes share this owner. */
77
- rvy, http, cues) {
119
+ rvy, http, cues, relaying = null) {
78
120
  Object.defineProperty(this, "protocol", {
79
121
  enumerable: true,
80
122
  configurable: true,
@@ -115,6 +157,13 @@ export class Oobiery {
115
157
  writable: true,
116
158
  value: void 0
117
159
  });
160
+ /** Relay collaborators of an edge; null resolves every OOBI directly. */
161
+ Object.defineProperty(this, "relaying", {
162
+ enumerable: true,
163
+ configurable: true,
164
+ writable: true,
165
+ value: void 0
166
+ });
118
167
  /** Prevent overlapping passes over this owner's durable job queue. */
119
168
  Object.defineProperty(this, "active", {
120
169
  enumerable: true,
@@ -122,6 +171,16 @@ export class Oobiery {
122
171
  writable: true,
123
172
  value: false
124
173
  });
174
+ /**
175
+ * URL of the last job handled with a relay, so a job left waiting does not starve the jobs after it: the next
176
+ * pass takes the first queued URL after this one. Always null without a relay.
177
+ */
178
+ Object.defineProperty(this, "relayCursor", {
179
+ enumerable: true,
180
+ configurable: true,
181
+ writable: true,
182
+ value: null
183
+ });
125
184
  this.hby = hby;
126
185
  this.kvy = protocol.makeKevery();
127
186
  if (this.kvy.db !== hby.db || this.kvy.rvy !== rvy) {
@@ -129,6 +188,7 @@ export class Oobiery {
129
188
  }
130
189
  this.cues = cues;
131
190
  this.http = http;
191
+ this.relaying = relaying;
132
192
  }
133
193
  /**
134
194
  * Register `/introduce` on the shared reply router.
@@ -209,16 +269,24 @@ export class Oobiery {
209
269
  *oobiDo(clock = systemClock, pacing = PROTOCOL_PACING) {
210
270
  return yield* doEveryOnWrite(clock, pacing, this.hby.db, () => this.processOnce());
211
271
  }
212
- /** Select queued work, including a surviving in-flight row whose former process is gone. */
272
+ /**
273
+ * Select queued work, including a surviving in-flight row whose former process is gone. After a relay job, the
274
+ * first queued URL after it is taken, wrapping around, so jobs waiting on the relay take turns.
275
+ */
213
276
  nextQueued() {
277
+ const after = this.relayCursor;
278
+ let first = null;
214
279
  for (const store of [this.hby.db.oobis, this.hby.db.coobi]) {
215
280
  for (const [keys, record] of store.getTopItemIter()) {
216
281
  const url = keys[0];
217
- if (url)
282
+ if (!url)
283
+ continue;
284
+ if (after === null || url > after)
218
285
  return ["oobis", url, record];
286
+ first ??= ["oobis", url, record];
219
287
  }
220
288
  }
221
- return null;
289
+ return first;
222
290
  }
223
291
  /** Select a parent only after its current child attempts, including authentication, settle. */
224
292
  nextReadyMoobi() {
@@ -238,7 +306,7 @@ export class Oobiery {
238
306
  * Stores touched:
239
307
  * - `oobis.` / `woobi.` as the durable queued worklists
240
308
  * - `coobi.` while a fetched response is being processed
241
- * - `eoobi.` on HTTP failure
309
+ * - `eoobi.` on HTTP failure or an {@link OobiJobFailure}; the queued row is removed so it is not re-read
242
310
  * - `roobi.` after parser/routing success
243
311
  *
244
312
  * @param kind Durable OOBI queue family identifying the job path.
@@ -246,6 +314,17 @@ export class Oobiery {
246
314
  * @param record Pending durable job; retained through failure and joined cleanup.
247
315
  */
248
316
  *processJob(kind, url, record) {
317
+ if (!isRequestableHttpUrl(url)) {
318
+ // Neither this host nor a relay can fetch it; fail it before any request instead of on every pass.
319
+ this.remQueueStore(kind, url);
320
+ this.failFetchedOobi(url, record, "url-refused");
321
+ return;
322
+ }
323
+ if (this.relaying && !isRelayOrigin(this.hby, this.relaying.relay, url)) {
324
+ this.relayCursor = url;
325
+ yield* this.processRelayJob(this.relaying, kind, url, record);
326
+ return;
327
+ }
249
328
  const http = this.http;
250
329
  const meta = {
251
330
  ...parseOobiUrl(url, record.oobialias ?? undefined),
@@ -288,6 +367,14 @@ export class Oobiery {
288
367
  this.cues.push({ kin: "oobiFailed", url, reason: `HTTP ${refused}` });
289
368
  }
290
369
  }
370
+ catch (error) {
371
+ // What the remote party caused ends this job only; the response scope has closed.
372
+ const failure = oobiJobFailure(error);
373
+ if (failure === null)
374
+ throw error;
375
+ this.remQueueStore(kind, url);
376
+ this.failFetchedOobi(url, queuedRecord, failure);
377
+ }
291
378
  finally {
292
379
  this.hby.db.coobi.rem(url);
293
380
  }
@@ -349,13 +436,186 @@ export class Oobiery {
349
436
  role: meta.role ?? undefined,
350
437
  eid: meta.eid ?? undefined,
351
438
  };
439
+ yield* this.ingestCesrOobiResponse(kind, url, queuedRecord, response);
440
+ this.hby.db.roobi.pin(url, { ...queuedRecord, date: new Date().toISOString(), state: "resolved" });
441
+ persistResolvedContact(this.hby, meta.cid, { alias: queuedRecord.oobialias, oobi: url });
442
+ this.cues.push(cue);
443
+ }
444
+ /**
445
+ * Dispatch one complete CESR OOBI response through this owner's Kevery and the shared semantic handlers, keeping
446
+ * the queued row and an in-flight `coobi.` row until the stream is consumed. Writes no terminal state.
447
+ *
448
+ * @param kind Durable OOBI queue family identifying the job path.
449
+ * @param url Exact OOBI URL used as the durable job key.
450
+ * @param queuedRecord Original durable job metadata retained until terminal publication.
451
+ * @param response Borrowed scoped HTTP response.
452
+ */
453
+ *ingestCesrOobiResponse(kind, url, queuedRecord, response) {
352
454
  this.pinQueueStore(kind, url, queuedRecord);
353
455
  this.hby.db.coobi.pin(url, { ...queuedRecord, state: "streaming" });
354
456
  yield* processMessages((signal) => response.body(signal), this.protocol, this.kvy);
355
457
  this.hby.db.coobi.rem(url);
356
- this.hby.db.roobi.pin(url, { ...queuedRecord, date: new Date().toISOString(), state: "resolved" });
357
- persistResolvedContact(this.hby, meta.cid, { alias: queuedRecord.oobialias, oobi: url });
358
- this.cues.push(cue);
458
+ }
459
+ /**
460
+ * Resolve one job through the relay: ask it once, then fetch the result from the relay's own OOBI endpoint.
461
+ *
462
+ * The job stays in `oobis.` while it waits, dated from its first relay attempt; a pass that changes nothing
463
+ * writes nothing. It waits while the relay answers 404, is unreachable, or answers a controller OOBI without the
464
+ * controller's KEL. It fails at {@link RELAY_OOBI_TIMEOUT_MS}: `relay-unresolved` when the relay was asked,
465
+ * `relay-unserved` when it never could be (no accepted relay location, or no local identifier authorized the
466
+ * relay as its mailbox). Another non-OK answer fails at once as `http-<status>`, as a direct fetch does, an answer
467
+ * that is not CESR or whose frames do not decode fails at once with its {@link OobiJobFailure}, and a URL that names
468
+ * neither an identifier nor a SAID fails at once as `relay-unmappable-oobi`.
469
+ *
470
+ * @param relaying Relay collaborators of this edge.
471
+ * @param kind Durable OOBI queue family identifying the job path.
472
+ * @param url Exact third-party OOBI URL used as the durable job key; it is never fetched.
473
+ * @param record Pending durable job.
474
+ */
475
+ *processRelayJob(relaying, kind, url, record) {
476
+ const { relay, postman, clock } = relaying;
477
+ const meta = { ...parseOobiUrl(url, record.oobialias ?? undefined), ...record, url };
478
+ const path = relayOobiPath(meta);
479
+ if (path === null) {
480
+ this.remQueueStore(kind, url);
481
+ this.failFetchedOobi(url, record, "relay-unmappable-oobi");
482
+ return;
483
+ }
484
+ let waiting = record;
485
+ if (record.state !== RelayOobiStates.pending && record.state !== RelayOobiStates.requested) {
486
+ waiting = {
487
+ ...record,
488
+ date: new Date(clock.now()).toISOString(),
489
+ state: RelayOobiStates.pending,
490
+ cid: meta.cid ?? null,
491
+ eid: meta.eid ?? null,
492
+ role: meta.role ?? null,
493
+ oobialias: meta.alias ?? meta.oobialias ?? null,
494
+ said: meta.said ?? null,
495
+ };
496
+ this.hby.db.transaction(() => {
497
+ this.pinQueueStore(kind, url, waiting);
498
+ this.hby.db.eoobi.rem(url);
499
+ this.hby.db.roobi.rem(url);
500
+ });
501
+ }
502
+ if (waiting.state === RelayOobiStates.pending) {
503
+ const asker = this.relayAsker(relay);
504
+ if (asker) {
505
+ const exn = oobiRequestExn(asker, relay.aid, url);
506
+ postman.send({
507
+ src: asker.pre,
508
+ dest: relay.aid,
509
+ topic: OOBI_MAILBOX_TOPIC,
510
+ msg: asker.endorse(exn, { pipelined: false }),
511
+ });
512
+ waiting = { ...waiting, state: RelayOobiStates.requested };
513
+ this.pinQueueStore(kind, url, waiting);
514
+ }
515
+ }
516
+ const endpoint = relayEndpoint(this.hby, relay);
517
+ const base = endpoint?.urls.https ?? endpoint?.urls.http;
518
+ if (base) {
519
+ const refused = yield* this.fetchFromRelay(kind, url, waiting, meta, new URL(path, withSlash(base)).href);
520
+ if (refused === "resolved")
521
+ return;
522
+ if (typeof refused === "number") {
523
+ this.remQueueStore(kind, url);
524
+ this.hby.db.eoobi.pin(url, { ...waiting, date: new Date().toISOString(), state: `http-${refused}` });
525
+ this.cues.push({ kin: "oobiFailed", url, reason: `HTTP ${refused}` });
526
+ return;
527
+ }
528
+ if (refused !== "waiting") {
529
+ this.remQueueStore(kind, url);
530
+ this.failFetchedOobi(url, waiting, refused);
531
+ return;
532
+ }
533
+ }
534
+ if (clock.now() - Date.parse(waiting.date ?? "") < RELAY_OOBI_TIMEOUT_MS)
535
+ return;
536
+ const state = waiting.state === RelayOobiStates.requested ? "relay-unresolved" : "relay-unserved";
537
+ this.remQueueStore(kind, url);
538
+ this.failFetchedOobi(url, waiting, state);
539
+ }
540
+ /**
541
+ * Fetch what the relay holds for one job and ingest it under the ordinary verification.
542
+ *
543
+ * @param kind Durable OOBI queue family identifying the job path.
544
+ * @param url Exact third-party OOBI URL used as the durable job key.
545
+ * @param record Waiting durable job metadata.
546
+ * @param meta Parsed OOBI URL metadata selecting the response interpretation.
547
+ * @param relayUrl URL on the relay's origin that serves the job's answer.
548
+ * @returns `resolved` after a terminal write, `waiting` when the relay does not hold the answer yet or cannot be
549
+ * reached, the refusing HTTP status, or the {@link OobiJobFailure} of an answer that cannot be ingested.
550
+ */
551
+ *fetchFromRelay(kind, url, record, meta, relayUrl) {
552
+ const http = this.http;
553
+ // deno-lint-ignore no-this-alias -- The scoped body borrows this OOBI worker's durable queue.
554
+ const self = this;
555
+ try {
556
+ return yield* scoped(function* () {
557
+ const response = yield* http.open(relayUrl);
558
+ if (response.status === 404)
559
+ return "waiting";
560
+ if (!response.ok)
561
+ return response.status;
562
+ const type = mediaType(response.headers.get("content-type") ?? "");
563
+ if (!isCesrMediaType(type) && type.includes("json")) {
564
+ // Schema and JSON OOBI bodies reach a terminal state under the direct-fetch rules.
565
+ yield* self.processResponse(kind, url, record, meta, response);
566
+ self.remQueueStore(kind, url);
567
+ return "resolved";
568
+ }
569
+ yield* self.ingestCesrOobiResponse(kind, url, record, response);
570
+ // An answer without the controller's KEL is not the result yet; the relay may still be resolving it.
571
+ if (meta.cid && !self.hby.db.getKever(meta.cid))
572
+ return "waiting";
573
+ self.remQueueStore(kind, url);
574
+ self.hby.db.roobi.pin(url, { ...record, date: new Date().toISOString(), state: "resolved" });
575
+ persistResolvedContact(self.hby, meta.cid, { alias: record.oobialias, oobi: url });
576
+ self.cues.push({
577
+ kin: "oobiResolved",
578
+ url,
579
+ cid: meta.cid ?? undefined,
580
+ role: meta.role ?? undefined,
581
+ eid: meta.eid ?? undefined,
582
+ });
583
+ return "resolved";
584
+ });
585
+ }
586
+ catch (error) {
587
+ // An unreachable relay is the same wait as a relay that has not resolved the job yet.
588
+ if (error instanceof TransportError && !(error instanceof RelayOnlyError))
589
+ return "waiting";
590
+ // A relay answering what cannot be ingested fails the job, as a direct fetch does.
591
+ const failure = oobiJobFailure(error);
592
+ if (failure === null)
593
+ throw error;
594
+ return failure;
595
+ }
596
+ finally {
597
+ // Remove only a row this fetch left; a pass that changed nothing writes nothing.
598
+ if (this.hby.db.coobi.get(url))
599
+ this.hby.db.coobi.rem(url);
600
+ }
601
+ }
602
+ /**
603
+ * The local identifier that asks the relay to resolve an OOBI: one the relay serves, which has authorized the
604
+ * relay as its mailbox ({@link relayAuthorized}). A group cannot sign the request alone, so groups never ask.
605
+ *
606
+ * @param relay The configured relay agent.
607
+ * @returns The asking habitat, or null while the relay has no accepted location or no local identifier it serves.
608
+ */
609
+ relayAsker(relay) {
610
+ if (relayRefusal(this.hby, relay, null))
611
+ return null;
612
+ for (const hab of this.hby.habs.values()) {
613
+ const pre = hab.pre;
614
+ if (pre && pre !== relay.aid && !this.hby.db.groups.has(pre) && relayAuthorized(this.hby, pre, relay)) {
615
+ return hab;
616
+ }
617
+ }
618
+ return null;
359
619
  }
360
620
  /** Handle schema data-OOBIs before generic JSON reply handling. */
361
621
  processSchemaOobiResponse(url, record, bytes) {
@@ -401,18 +661,19 @@ export class Oobiery {
401
661
  /** Fan one multi-OOBI reply out into child `oobis.` jobs and a parent `moobi.` record. */
402
662
  processMultiOobiReply(url, record, serder) {
403
663
  const data = serder.ked?.a;
404
- const cid = typeof data?.aid === "string" ? new Prefixer({ qb64: data.aid }).qb64 : null;
664
+ const cid = remotePrefix(data?.aid);
405
665
  const urls = Array.isArray(data?.urls)
406
666
  ? [
407
667
  ...new Set(data.urls.filter((entry) => typeof entry === "string")),
408
668
  ]
409
669
  : [];
670
+ // A child the HTTP client would refuse fails the whole reply, before the first queue write: no partial fanout.
410
671
  if (!cid || cid !== (record.cid ?? parseOobiUrl(url).cid ?? null)
411
- || urls.length === 0) {
672
+ || urls.length === 0 || !urls.every(isRequestableHttpUrl)) {
412
673
  this.failFetchedOobi(url, record, "invalid-multi-oobi");
413
674
  return;
414
675
  }
415
- // Normalize every child before the first queue write; malformed later URLs cannot leave partial fanout.
676
+ // Normalize every child before the first queue write.
416
677
  const children = urls.map((childUrl) => {
417
678
  const meta = parseOobiUrl(childUrl, record.oobialias ?? undefined);
418
679
  return { url: childUrl, kind: queueKindFor(childUrl), meta, record: this.queuedRecord(meta) };
@@ -496,15 +757,15 @@ export class Oobiery {
496
757
  throw new ValidationError(`Unsupported route=${args.route} in ${Ilks.rpy} reply.`);
497
758
  }
498
759
  const data = args.serder.ked?.a;
499
- const cid = typeof data?.cid === "string" ? new Prefixer({ qb64: data.cid }).qb64 : null;
760
+ const cid = remotePrefix(data?.cid);
500
761
  const oobi = typeof data?.oobi === "string" ? data.oobi : null;
501
762
  const dt = typeof args.serder.ked?.dt === "string" ? args.serder.ked.dt : new Date().toISOString();
502
763
  if (!cid || !oobi) {
503
- throw new ValidationError("Missing cid/oobi in /introduce reply.");
764
+ throw new ValidationError("Missing or invalid cid/oobi in /introduce reply.");
504
765
  }
505
- const parsed = new URL(oobi);
506
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
507
- throw new ValidationError(`Invalid introduced OOBI scheme ${parsed.protocol}.`);
766
+ // Refused at intake: a URL the HTTP client refuses by construction can never resolve.
767
+ if (!isRequestableHttpUrl(oobi)) {
768
+ throw new ValidationError(`Introduced OOBI ${oobi} is not an HTTP(S) URL without credentials.`);
508
769
  }
509
770
  const proof = this.rvy.decideReply({
510
771
  serder: args.serder,
@@ -541,8 +802,17 @@ export class Oobiery {
541
802
  return acceptReplyDecision();
542
803
  }
543
804
  }
544
- export function loadOobiHandlers(hby, exchanger, notifier = null) {
545
- exchanger.addHandler(new OobiRequestHandler(hby, notifier));
805
+ /**
806
+ * Register the `/oobis` exchange handler.
807
+ *
808
+ * @param hby Borrowed local identities and the durable OOBI queue.
809
+ * @param exchanger Exchanger receiving the route.
810
+ * @param notifier Optional operator notice producer.
811
+ * @param relaying Whether this host relays (the agent side of relay mode): requests are then accepted only from a
812
+ * served sender.
813
+ */
814
+ export function loadOobiHandlers(hby, exchanger, notifier = null, relaying = false) {
815
+ exchanger.addHandler(new OobiRequestHandler(hby, notifier, relaying));
546
816
  }
547
817
  /**
548
818
  * Peer-to-peer OOBI request handler for `/oobis` exchange messages.
@@ -557,15 +827,27 @@ export function loadOobiHandlers(hby, exchanger, notifier = null) {
557
827
  *
558
828
  * Local `keri-ts` adaptation:
559
829
  * - verification stays lightweight and route-local: we only require a string
560
- * `oobi` payload with an `http:` or `https:` scheme
830
+ * `oobi` payload that is an `http:` or `https:` URL without embedded
831
+ * credentials, which is what the HTTP client can request
561
832
  * - durable follow-on work still belongs to `Oobiery` itself; this handler is
562
833
  * just the exchange ingress seam that translates an EXN into queued resolver
563
834
  * state
564
835
  * - unlike KERIpy's DoDoer-centric runtime, the queued record is later
565
836
  * advanced by the shared `AgentRuntime` turn that owns OOBI processing
837
+ * - on a relaying host (the agent side of relay mode) a request is accepted
838
+ * only from a served sender, by the same rule `/fwd` intake relays for
839
+ * ({@link servingHost}): the sender authorized a local habitat as its
840
+ * `mailbox` or `agent`. An edge resolves third-party OOBIs this way; nobody
841
+ * else can make the agent fetch a URL. Without relaying, any authenticated
842
+ * sender is accepted, as in KERIpy.
566
843
  */
567
844
  export class OobiRequestHandler {
568
- constructor(hby, notifier = null) {
845
+ /**
846
+ * @param hby Borrowed local identities and the durable OOBI queue.
847
+ * @param notifier Optional operator notice producer.
848
+ * @param relaying Restrict requests to served senders (a relaying host); false accepts any sender.
849
+ */
850
+ constructor(hby, notifier = null, relaying = false) {
569
851
  Object.defineProperty(this, "resource", {
570
852
  enumerable: true,
571
853
  configurable: true,
@@ -584,21 +866,30 @@ export class OobiRequestHandler {
584
866
  writable: true,
585
867
  value: void 0
586
868
  });
869
+ /** Whether requests are restricted to served senders. */
870
+ Object.defineProperty(this, "relaying", {
871
+ enumerable: true,
872
+ configurable: true,
873
+ writable: true,
874
+ value: void 0
875
+ });
587
876
  this.hby = hby;
588
877
  this.notifier = notifier;
878
+ this.relaying = relaying;
589
879
  }
880
+ /**
881
+ * Accept a request carrying an `oobi` the HTTP client can request ({@link isRequestableHttpUrl}: HTTP(S), no
882
+ * embedded credentials); on a relaying host, only from a served sender. Anything else could never resolve.
883
+ *
884
+ * @param args Authenticated exchange and the receiving hosted endpoint, if the request named one.
885
+ * @returns False to reject the exchange.
886
+ */
590
887
  verify(args) {
591
- const payload = args.serder.ked?.a;
592
- if (typeof payload?.["oobi"] !== "string") {
593
- return false;
594
- }
595
- try {
596
- const parsed = new URL(payload.oobi);
597
- return parsed.protocol === "http:" || parsed.protocol === "https:";
598
- }
599
- catch {
888
+ if (this.relaying && !servingHost(this.hby, { sender: args.serder.pre, origin: null }, args.mailboxAid)) {
600
889
  return false;
601
890
  }
891
+ const payload = args.serder.ked?.a;
892
+ return typeof payload?.["oobi"] === "string" && isRequestableHttpUrl(payload.oobi);
602
893
  }
603
894
  handle(args) {
604
895
  const src = args.serder.pre;
@@ -698,6 +989,48 @@ function findPathSequence(parts, sequence) {
698
989
  }
699
990
  return -1;
700
991
  }
992
+ /**
993
+ * Path, relative to the relay's location, at which the relay serves what an OOBI job names. The relay's OOBI
994
+ * endpoint answers for what it holds, so the third party's host and base path are dropped:
995
+ * - a controller, witness, agent or mailbox OOBI `.../oobi/{cid}/{role}[/{eid}]` maps to
996
+ * `oobi/{cid}/{role}[/{eid}]`; the relay answers with the controller's KEL (receipts included) and the replies
997
+ * it holds for that role;
998
+ * - a data OOBI `.../oobi/{said}` (a schema) maps to `oobi/{said}`.
999
+ * Well-known OOBIs are authentication jobs, not Oobiery jobs. A URL naming neither maps to nothing.
1000
+ *
1001
+ * @param job Parsed job metadata.
1002
+ * @returns The relative path, or null when the job names no identifier or SAID.
1003
+ */
1004
+ function relayOobiPath(job) {
1005
+ if (job.cid && job.role) {
1006
+ return ["oobi", job.cid, job.role, ...(job.eid ? [job.eid] : [])].map(encodeURIComponent).join("/");
1007
+ }
1008
+ if (job.said)
1009
+ return `oobi/${encodeURIComponent(job.said)}`;
1010
+ return null;
1011
+ }
1012
+ /** A base URL that resolves relative paths beneath it rather than beside its last segment. */
1013
+ function withSlash(url) {
1014
+ return url.endsWith("/") ? url : `${url}/`;
1015
+ }
1016
+ /**
1017
+ * The qualified prefix a remote reply names, or null when the value is not a string or does not decode as one.
1018
+ *
1019
+ * @param value Remote field value.
1020
+ * @returns The prefix's qb64, or null.
1021
+ */
1022
+ function remotePrefix(value) {
1023
+ if (typeof value !== "string")
1024
+ return null;
1025
+ try {
1026
+ return new Prefixer({ qb64: value }).qb64;
1027
+ }
1028
+ catch (error) {
1029
+ if (error instanceof ParserError)
1030
+ return null;
1031
+ throw error;
1032
+ }
1033
+ }
701
1034
  function queueKindFor(url) {
702
1035
  return isWellKnownOobiUrl(url) ? "woobi" : "oobis";
703
1036
  }
@@ -1,6 +1,7 @@
1
1
  /** Finite credential-bundle intake; protocol owners retain and recover their own pending evidence. */
2
2
  import { Ilks, parse, SerderACDC, SerderKERI } from "cesr-ts";
3
3
  import { action, call, ensure, race } from "effection";
4
+ import { validateRip, validateUpdate } from "../../acdc/regeventing.js";
4
5
  import { consoleLogger } from "../../core/logger.js";
5
6
  import { rethrowCleanupFailure } from "../runtime/abortable.js";
6
7
  import { chunkBytes } from "../transport/byte-source.js";
@@ -10,6 +11,14 @@ import { chunkBytes } from "../transport/byte-source.js";
10
11
  * Later input failures preserve earlier accepted state. Receipts, replies and other messages still
11
12
  * use normal dispatch; absence of an importer acceptance lookup is not an additional refusal policy.
12
13
  *
14
+ * The input is retained history the caller already holds (for example an exported disclosure stream: KELs, the v2
15
+ * registry TEL, a Grant and its Admit), so every frame is dispatched with `DispatchOptions.retained`. Its v2
16
+ * exchanges skip KRAM, whose window their original stamps passed long ago; signatures, route verification and
17
+ * escrows are unchanged, and the accepted exchanges raise no notice and get no exchange lifetime. Targets: raw
18
+ * credentials, KEL and v1 TEL events, v2 `rip`/`bup` (accepted at their exact ordinal in the v2 registry store),
19
+ * and exchanges (accepted by the Exchanger; a v2 Admit also needs its Grant's reply row to name it). Importing the
20
+ * same input again changes nothing and reports the same acceptance.
21
+ *
13
22
  * @param protocol Borrowed protocol dispatcher, sharing reger with its TEL/credential handlers.
14
23
  * @param reger Borrowed credential/TEL store whose accepted records establish the result.
15
24
  * @param bytes Borrowed finite CESR bundle; parser chunks are copied during intake.
@@ -120,7 +129,8 @@ function readImportTargets(protocol, reger, bytes, kvy) {
120
129
  if (target)
121
130
  targets.set(target.said, target);
122
131
  }
123
- const { decision } = yield* protocol.dispatch(next.value, {}, kvy);
132
+ // The one dispatch that marks its input as retained history; see the operation docs.
133
+ const { decision } = yield* protocol.dispatch(next.value, { retained: true }, kvy);
124
134
  if (decision && (decision.kind === "reject" || decision.kind === "drop")) {
125
135
  consoleLogger.warn("Credential import message refused", {
126
136
  said: serder.said,
@@ -148,7 +158,16 @@ function readImportTargets(protocol, reger, bytes, kvy) {
148
158
  function importTarget(serder, reger) {
149
159
  const said = serder.said, ilk = serder.ilk;
150
160
  if (serder instanceof SerderACDC) {
151
- return ilk === null ? { ilk, said, savedBefore: reger.saved.get([said]) !== null } : null;
161
+ if (ilk === null)
162
+ return { ilk, said, savedBefore: reger.saved.get([said]) !== null };
163
+ if (ilk !== Ilks.rip && ilk !== Ilks.bup)
164
+ return null;
165
+ // An event that fails field validation, or whose ordinal no store can hold, keeps no coordinates, so it is
166
+ // always reported unaccepted.
167
+ const result = ilk === Ilks.rip ? validateRip(serder) : validateUpdate(serder, serder.regid ?? "");
168
+ return result.kind === "valid" && result.event.sn <= BigInt(Number.MAX_SAFE_INTEGER)
169
+ ? { ilk, said, regid: result.event.regid, sn: Number(result.event.sn) }
170
+ : { ilk, said, regid: null, sn: null };
152
171
  }
153
172
  switch (ilk) {
154
173
  case Ilks.icp:
@@ -164,7 +183,11 @@ function importTarget(serder, reger) {
164
183
  case Ilks.brv:
165
184
  return { ilk, said, pre: serder.pre, sn: serder.sn };
166
185
  case Ilks.exn:
167
- return { ilk, said };
186
+ case Ilks.xip: {
187
+ const prior = serder.sad?.p;
188
+ const admit = serder.pvrsn.major >= 2 && serder.route === "/ipex/admit" && typeof prior === "string";
189
+ return { ilk, said, answers: admit ? prior : null };
190
+ }
168
191
  default:
169
192
  return null;
170
193
  }
@@ -195,7 +218,13 @@ function importTargetAccepted(target, protocol, reger) {
195
218
  case Ilks.bis:
196
219
  case Ilks.brv:
197
220
  return target.pre !== null && target.sn !== null && reger.tels.getOn(target.pre, target.sn)?.qb64 === target.said;
221
+ case Ilks.rip:
222
+ case Ilks.bup:
223
+ return target.regid !== null && target.sn !== null
224
+ && protocol.registry?.store.seqSaider(target.regid, target.sn)?.qb64 === target.said;
198
225
  case Ilks.exn:
199
- return protocol.kvy.db.exns.get([target.said]) !== null;
226
+ case Ilks.xip:
227
+ return protocol.exc.complete(target.said)
228
+ && (target.answers === null || protocol.kvy.db.erpy.get([target.answers])?.qb64 === target.said);
200
229
  }
201
230
  }