keri-ts 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/esm/keri/npm/src/acdc/chaining.js +223 -89
  2. package/esm/keri/npm/src/acdc/observing.js +2 -2
  3. package/esm/keri/npm/src/acdc/regbasing.js +20 -29
  4. package/esm/keri/npm/src/acdc/regengine.js +4 -6
  5. package/esm/keri/npm/src/acdc/registering.js +13 -10
  6. package/esm/keri/npm/src/acdc/registraring.js +4 -9
  7. package/esm/keri/npm/src/acdc/salting.js +127 -25
  8. package/esm/keri/npm/src/acdc/unblinding.js +2 -4
  9. package/esm/keri/npm/src/app/bootstrap/oobiery.js +1 -1
  10. package/esm/keri/npm/src/app/credentials/root-replicating.js +123 -0
  11. package/esm/keri/npm/src/app/credentials/verifying.js +21 -3
  12. package/esm/keri/npm/src/app/delivery/forwarding.js +49 -7
  13. package/esm/keri/npm/src/app/exchanges/exchanging.js +11 -12
  14. package/esm/keri/npm/src/app/identity/configing.js +17 -43
  15. package/esm/keri/npm/src/app/identity/habbing.js +54 -35
  16. package/esm/keri/npm/src/app/identity/keeping.js +1 -1
  17. package/esm/keri/npm/src/app/identity/lmdb-habery.js +12 -0
  18. package/esm/keri/npm/src/app/ipex/ipex-graph.js +7 -70
  19. package/esm/keri/npm/src/app/ipex/ipex-grouping.js +8 -5
  20. package/esm/keri/npm/src/app/ipex/ipex-v2.js +21 -5
  21. package/esm/keri/npm/src/app/ipex/ipexing.js +1 -1
  22. package/esm/keri/npm/src/app/mailbox/mailboxing.js +5 -9
  23. package/esm/keri/npm/src/app/multisig/group-members.js +14 -0
  24. package/esm/keri/npm/src/app/multisig/grouping.js +4 -3
  25. package/esm/keri/npm/src/app/multisig/multisig-workflows.js +7 -2
  26. package/esm/keri/npm/src/app/notifications/notifying.js +5 -9
  27. package/esm/keri/npm/src/app/protocol/protocol-processing.js +35 -3
  28. package/esm/keri/npm/src/app/queries/witness-inquisitor.js +2 -2
  29. package/esm/keri/npm/src/app/runtime/agent-runtime.js +7 -10
  30. package/esm/keri/npm/src/app/transport/byte-source.js +6 -42
  31. package/esm/keri/npm/src/app/transport/cesr-egress.js +77 -13
  32. package/esm/keri/npm/src/app/transport/file-bytes.js +41 -0
  33. package/esm/keri/npm/src/app/transport/http-messenger.js +14 -9
  34. package/esm/keri/npm/src/app/transport/message-source.js +1 -1
  35. package/esm/keri/npm/src/app/transport/messengering.js +3 -4
  36. package/esm/keri/npm/src/app/transport/node-http-client.js +21 -0
  37. package/esm/keri/npm/src/app/transport/tcp-dialer.js +14 -0
  38. package/esm/keri/npm/src/app/witness/receipt-client.js +2 -2
  39. package/esm/keri/npm/src/cli/common/context.js +2 -2
  40. package/esm/keri/npm/src/cli/common/existing.js +3 -5
  41. package/esm/keri/npm/src/cli/credentials/ipex.js +2 -1
  42. package/esm/keri/npm/src/cli/endpoints/oobi-clean.js +4 -3
  43. package/esm/keri/npm/src/cli/endpoints/oobi.js +2 -2
  44. package/esm/keri/npm/src/cli/identity/challenge.js +2 -2
  45. package/esm/keri/npm/src/cli/identity/incept.js +2 -2
  46. package/esm/keri/npm/src/cli/identity/init.js +4 -3
  47. package/esm/keri/npm/src/cli/identity/interact.js +2 -2
  48. package/esm/keri/npm/src/cli/identity/rotate.js +2 -2
  49. package/esm/keri/npm/src/cli/messaging/exchange.js +2 -2
  50. package/esm/keri/npm/src/cli/messaging/multisig.js +61 -3
  51. package/esm/keri/npm/src/cli/tools/annotate.js +2 -1
  52. package/esm/keri/npm/src/cli/tools/benchmark.js +2 -1
  53. package/esm/keri/npm/src/cli/tools/db-dump.js +1 -4
  54. package/esm/keri/npm/src/core/bytes.js +9 -0
  55. package/esm/keri/npm/src/core/kel/delegation-ancestry.js +92 -0
  56. package/esm/keri/npm/src/core/kel/eventing.js +1 -2
  57. package/esm/keri/npm/src/core/kel/kever.js +11 -9
  58. package/esm/keri/npm/src/core/mailbox/kram-policy.js +2 -3
  59. package/esm/keri/npm/src/core/mailbox/kram-pruning.js +15 -22
  60. package/esm/keri/npm/src/core/mailbox/kram-staging.js +2 -2
  61. package/esm/keri/npm/src/core/routing.js +2 -2
  62. package/esm/keri/npm/src/db/basing.js +149 -203
  63. package/esm/keri/npm/src/db/core/dber.js +1062 -0
  64. package/esm/keri/npm/src/db/core/index.js +4 -2
  65. package/esm/keri/npm/src/db/core/lmdb-opener.js +30 -0
  66. package/esm/keri/npm/src/db/core/lmdber.js +199 -1067
  67. package/esm/keri/npm/src/db/core/memstore.js +532 -0
  68. package/esm/keri/npm/src/db/core/path-manager.js +109 -223
  69. package/esm/keri/npm/src/db/core/store-owner.js +104 -0
  70. package/esm/keri/npm/src/db/core/webdber.js +252 -0
  71. package/esm/keri/npm/src/db/escrowing.js +2 -2
  72. package/esm/keri/npm/src/db/exchange/exchange-nests.js +3 -2
  73. package/esm/keri/npm/src/db/keeping.js +27 -121
  74. package/esm/keri/npm/src/db/koming.js +1 -1
  75. package/esm/keri/npm/src/db/lmdb-stores.js +68 -0
  76. package/esm/keri/npm/src/db/lmdb.js +6 -0
  77. package/esm/keri/npm/src/db/mailbox/mailboxing.js +12 -65
  78. package/esm/keri/npm/src/db/noting.js +11 -55
  79. package/esm/keri/npm/src/db/reger.js +43 -105
  80. package/esm/keri/npm/src/db/replay/witness-replay.js +2 -20
  81. package/esm/keri/npm/src/db/subing.js +1 -1
  82. package/esm/keri/npm/src/npm/db-lmdb.js +3 -0
  83. package/esm/keri/npm/src/npm/runtime-node.js +3 -0
  84. package/esm/keri/npm/src/runtime/index.js +9 -10
  85. package/esm/keri/npm/src/runtime/node.js +14 -0
  86. package/esm/keri/npm/src/vdr/eventing.js +1 -1
  87. package/esm/keri/npm/src/vdr/tel-publication.js +3 -3
  88. package/esm/keri/npm/src/version.js +2 -2
  89. package/package.json +11 -4
  90. package/types/acdc/chaining.d.ts +56 -6
  91. package/types/acdc/chaining.d.ts.map +1 -1
  92. package/types/acdc/regbasing.d.ts +7 -6
  93. package/types/acdc/regbasing.d.ts.map +1 -1
  94. package/types/acdc/regengine.d.ts.map +1 -1
  95. package/types/acdc/registering.d.ts +8 -4
  96. package/types/acdc/registering.d.ts.map +1 -1
  97. package/types/acdc/registraring.d.ts.map +1 -1
  98. package/types/acdc/salting.d.ts +53 -6
  99. package/types/acdc/salting.d.ts.map +1 -1
  100. package/types/acdc/unblinding.d.ts.map +1 -1
  101. package/types/app/credentials/root-replicating.d.ts +85 -0
  102. package/types/app/credentials/root-replicating.d.ts.map +1 -0
  103. package/types/app/credentials/verifying.d.ts +2 -1
  104. package/types/app/credentials/verifying.d.ts.map +1 -1
  105. package/types/app/delivery/forwarding.d.ts +10 -0
  106. package/types/app/delivery/forwarding.d.ts.map +1 -1
  107. package/types/app/exchanges/exchanging.d.ts +2 -0
  108. package/types/app/exchanges/exchanging.d.ts.map +1 -1
  109. package/types/app/identity/configing.d.ts +7 -5
  110. package/types/app/identity/configing.d.ts.map +1 -1
  111. package/types/app/identity/habbing.d.ts +23 -5
  112. package/types/app/identity/habbing.d.ts.map +1 -1
  113. package/types/app/identity/keeping.d.ts +1 -1
  114. package/types/app/identity/lmdb-habery.d.ts +6 -0
  115. package/types/app/identity/lmdb-habery.d.ts.map +1 -0
  116. package/types/app/ipex/ipex-graph.d.ts +4 -4
  117. package/types/app/ipex/ipex-graph.d.ts.map +1 -1
  118. package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
  119. package/types/app/ipex/ipex-v2.d.ts +11 -2
  120. package/types/app/ipex/ipex-v2.d.ts.map +1 -1
  121. package/types/app/ipex/ipexing.d.ts +3 -0
  122. package/types/app/ipex/ipexing.d.ts.map +1 -1
  123. package/types/app/mailbox/mailboxing.d.ts +1 -1
  124. package/types/app/mailbox/mailboxing.d.ts.map +1 -1
  125. package/types/app/multisig/group-members.d.ts +9 -0
  126. package/types/app/multisig/group-members.d.ts.map +1 -1
  127. package/types/app/multisig/grouping.d.ts.map +1 -1
  128. package/types/app/multisig/multisig-workflows.d.ts +2 -1
  129. package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
  130. package/types/app/notifications/notifying.d.ts +1 -1
  131. package/types/app/notifications/notifying.d.ts.map +1 -1
  132. package/types/app/protocol/parsering.d.ts +3 -1
  133. package/types/app/protocol/parsering.d.ts.map +1 -1
  134. package/types/app/protocol/protocol-processing.d.ts +2 -0
  135. package/types/app/protocol/protocol-processing.d.ts.map +1 -1
  136. package/types/app/runtime/agent-runtime.d.ts +7 -0
  137. package/types/app/runtime/agent-runtime.d.ts.map +1 -1
  138. package/types/app/transport/byte-source.d.ts +13 -10
  139. package/types/app/transport/byte-source.d.ts.map +1 -1
  140. package/types/app/transport/cesr-egress.d.ts +21 -7
  141. package/types/app/transport/cesr-egress.d.ts.map +1 -1
  142. package/types/app/transport/file-bytes.d.ts +11 -0
  143. package/types/app/transport/file-bytes.d.ts.map +1 -0
  144. package/types/app/transport/http-client.d.ts +3 -0
  145. package/types/app/transport/http-client.d.ts.map +1 -1
  146. package/types/app/transport/http-messenger.d.ts +15 -10
  147. package/types/app/transport/http-messenger.d.ts.map +1 -1
  148. package/types/app/transport/message-source.d.ts +1 -1
  149. package/types/app/transport/messengering.d.ts +2 -2
  150. package/types/app/transport/messengering.d.ts.map +1 -1
  151. package/types/app/transport/node-http-client.d.ts +10 -0
  152. package/types/app/transport/node-http-client.d.ts.map +1 -0
  153. package/types/app/transport/tcp-dialer.d.ts +21 -0
  154. package/types/app/transport/tcp-dialer.d.ts.map +1 -0
  155. package/types/cli/common/existing.d.ts.map +1 -1
  156. package/types/cli/credentials/ipex.d.ts.map +1 -1
  157. package/types/cli/endpoints/oobi-clean.d.ts.map +1 -1
  158. package/types/cli/identity/init.d.ts.map +1 -1
  159. package/types/cli/index.d.ts +1 -1
  160. package/types/cli/index.d.ts.map +1 -1
  161. package/types/cli/messaging/multisig.d.ts +10 -1
  162. package/types/cli/messaging/multisig.d.ts.map +1 -1
  163. package/types/cli/tools/annotate.d.ts.map +1 -1
  164. package/types/cli/tools/benchmark.d.ts.map +1 -1
  165. package/types/cli/tools/db-dump.d.ts.map +1 -1
  166. package/types/core/bytes.d.ts +2 -0
  167. package/types/core/bytes.d.ts.map +1 -1
  168. package/types/core/kel/delegation-ancestry.d.ts +41 -0
  169. package/types/core/kel/delegation-ancestry.d.ts.map +1 -0
  170. package/types/core/kel/eventing.d.ts.map +1 -1
  171. package/types/core/kel/kever.d.ts +5 -4
  172. package/types/core/kel/kever.d.ts.map +1 -1
  173. package/types/core/mailbox/kram-policy.d.ts.map +1 -1
  174. package/types/core/mailbox/kram-pruning.d.ts +1 -3
  175. package/types/core/mailbox/kram-pruning.d.ts.map +1 -1
  176. package/types/db/basing.d.ts +14 -23
  177. package/types/db/basing.d.ts.map +1 -1
  178. package/types/db/core/dber.d.ts +505 -0
  179. package/types/db/core/dber.d.ts.map +1 -0
  180. package/types/db/core/index.d.ts +4 -2
  181. package/types/db/core/index.d.ts.map +1 -1
  182. package/types/db/core/lmdb-opener.d.ts +17 -0
  183. package/types/db/core/lmdb-opener.d.ts.map +1 -0
  184. package/types/db/core/lmdber.d.ts +60 -383
  185. package/types/db/core/lmdber.d.ts.map +1 -1
  186. package/types/db/core/memstore.d.ts +129 -0
  187. package/types/db/core/memstore.d.ts.map +1 -0
  188. package/types/db/core/path-manager.d.ts +22 -45
  189. package/types/db/core/path-manager.d.ts.map +1 -1
  190. package/types/db/core/store-owner.d.ts +59 -0
  191. package/types/db/core/store-owner.d.ts.map +1 -0
  192. package/types/db/core/webdber.d.ts +127 -0
  193. package/types/db/core/webdber.d.ts.map +1 -0
  194. package/types/db/escrowing.d.ts +3 -3
  195. package/types/db/escrowing.d.ts.map +1 -1
  196. package/types/db/exchange/exchange-nests.d.ts.map +1 -1
  197. package/types/db/keeping.d.ts +9 -23
  198. package/types/db/keeping.d.ts.map +1 -1
  199. package/types/db/koming.d.ts +7 -8
  200. package/types/db/koming.d.ts.map +1 -1
  201. package/types/db/lmdb-stores.d.ts +45 -0
  202. package/types/db/lmdb-stores.d.ts.map +1 -0
  203. package/types/db/lmdb.d.ts +7 -0
  204. package/types/db/lmdb.d.ts.map +1 -0
  205. package/types/db/mailbox/mailboxing.d.ts +7 -21
  206. package/types/db/mailbox/mailboxing.d.ts.map +1 -1
  207. package/types/db/noting.d.ts +7 -12
  208. package/types/db/noting.d.ts.map +1 -1
  209. package/types/db/reger.d.ts +7 -11
  210. package/types/db/reger.d.ts.map +1 -1
  211. package/types/db/replay/witness-replay.d.ts +0 -2
  212. package/types/db/replay/witness-replay.d.ts.map +1 -1
  213. package/types/db/subing.d.ts +24 -25
  214. package/types/db/subing.d.ts.map +1 -1
  215. package/types/npm/db-lmdb.d.ts +4 -0
  216. package/types/npm/db-lmdb.d.ts.map +1 -0
  217. package/types/npm/runtime-node.d.ts +4 -0
  218. package/types/npm/runtime-node.d.ts.map +1 -0
  219. package/types/runtime/index.d.ts +9 -10
  220. package/types/runtime/index.d.ts.map +1 -1
  221. package/types/runtime/node.d.ts +15 -0
  222. package/types/runtime/node.d.ts.map +1 -0
  223. package/types/version.d.ts +2 -2
  224. package/esm/keri/npm/src/acdc/relating.js +0 -59
  225. package/esm/keri/npm/src/db/baser-defaults.js +0 -11
  226. package/types/acdc/relating.d.ts +0 -21
  227. package/types/acdc/relating.d.ts.map +0 -1
  228. package/types/db/baser-defaults.d.ts +0 -5
  229. package/types/db/baser-defaults.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import * as dntShim from "../../_dnt.shims.js";
2
- import { basename, dirname, join } from "../../deps/jsr.io/@std/path/1.1.6/mod.js";
2
+ import { basename, dirname, extname, join } from "../../deps/jsr.io/@std/path/1.1.6/mod.js";
3
3
  import { ValidationError } from "../../core/errors.js";
4
4
  import { PathManager } from "../../db/core/path-manager.js";
5
5
  const CONFIGER_DEFAULTS = {
@@ -23,7 +23,7 @@ const CONFIGER_DEFAULTS = {
23
23
  * - currently supports JSON only
24
24
  */
25
25
  export class Configer {
26
- /** Create a config manager bound to a PathManager-backed directory. */
26
+ /** Create a config manager bound to a PathManager-resolved file. */
27
27
  constructor(options = {}) {
28
28
  Object.defineProperty(this, "name", {
29
29
  enumerable: true,
@@ -62,7 +62,7 @@ export class Configer {
62
62
  value: null
63
63
  });
64
64
  this.name = options.name || "conf";
65
- this.base = options.base || "";
65
+ this.base = options.base ?? "main";
66
66
  this.temp = options.temp || false;
67
67
  this.fext = options.fext || CONFIGER_DEFAULTS.fext;
68
68
  this.pathManager = new PathManager({
@@ -70,27 +70,24 @@ export class Configer {
70
70
  name: this.name,
71
71
  base: this.base,
72
72
  temp: this.temp,
73
+ filed: true,
74
+ fext: this.fext,
73
75
  }, {
74
76
  tailDirPath: CONFIGER_DEFAULTS.tailDirPath,
75
77
  cleanTailDirPath: CONFIGER_DEFAULTS.cleanTailDirPath,
76
- altTailDirPath: CONFIGER_DEFAULTS.altTailDirPath,
77
- altCleanTailDirPath: CONFIGER_DEFAULTS.altCleanTailDirPath,
78
+ altTailDirPath: options.compat ? ".keri/cf" : CONFIGER_DEFAULTS.altTailDirPath,
79
+ altCleanTailDirPath: options.compat ? ".keri/clean/cf" : CONFIGER_DEFAULTS.altCleanTailDirPath,
78
80
  tempPrefix: CONFIGER_DEFAULTS.tempPrefix,
79
81
  });
80
82
  }
81
83
  get opened() {
82
84
  return this.path !== null;
83
85
  }
84
- /** Resolve and validate the config filename and extension. */
85
- resolvedFileName() {
86
- if (this.name.endsWith(`.${this.fext}`)) {
87
- return this.name;
88
- }
89
- const hasExt = this.name.includes(".");
90
- if (hasExt) {
86
+ /** Refuse a config name carrying an extension this manager cannot read. */
87
+ assertSupportedExtension() {
88
+ if (extname(this.name) && !this.name.endsWith(`.${this.fext}`)) {
91
89
  throw new ValidationError(`Unsupported config extension for '${this.name}'. Only .${this.fext} is supported.`);
92
90
  }
93
- return `${this.name}.${this.fext}`;
94
91
  }
95
92
  ensureOpenPath() {
96
93
  if (!this.path) {
@@ -194,44 +191,21 @@ export class Configer {
194
191
  throw pendingError;
195
192
  }
196
193
  }
197
- /** Open or create the config file in the resolved Configer directory. */
194
+ /** Open the config file at KERIpy's `{head}/keri/cf/{base}/{name}.{fext}`, creating it when absent. */
198
195
  *reopen(options = {}) {
199
- const opened = yield* this.pathManager.reopen({
200
- ...options,
201
- name: this.name,
202
- base: this.base,
203
- temp: this.temp,
204
- });
196
+ this.assertSupportedExtension();
197
+ const opened = yield* this.pathManager.reopen(options);
205
198
  if (!opened || !this.pathManager.path) {
206
199
  return false;
207
200
  }
208
- const fileName = this.resolvedFileName();
209
- this.path = join(this.pathManager.path, fileName);
210
- try {
211
- dntShim.Deno.statSync(this.path);
212
- }
213
- catch (error) {
214
- if (error instanceof dntShim.Deno.errors.NotFound) {
215
- this.writeAtomic(this.path, "{}\n");
216
- }
217
- else {
218
- throw error;
219
- }
201
+ this.path = this.pathManager.path;
202
+ if (dntShim.Deno.statSync(this.path).size === 0) {
203
+ this.writeAtomic(this.path, "{}\n");
220
204
  }
221
205
  return true;
222
206
  }
223
- /** Close the config manager and optionally remove files. */
207
+ /** Close the config manager and optionally remove the file. */
224
208
  *close(clear = false) {
225
- if (clear && this.path) {
226
- try {
227
- dntShim.Deno.removeSync(this.path);
228
- }
229
- catch (error) {
230
- if (!(error instanceof dntShim.Deno.errors.NotFound)) {
231
- throw error;
232
- }
233
- }
234
- }
235
209
  this.path = null;
236
210
  yield* this.pathManager.close(clear);
237
211
  return true;
@@ -23,13 +23,13 @@ import { HabitatRecord } from "../../core/records.js";
23
23
  import { Roles } from "../../core/roles.js";
24
24
  import { BasicReplyRouteHandler, Revery, Router } from "../../core/routing.js";
25
25
  import { Schemes } from "../../core/schemes.js";
26
- import { createBaser } from "../../db/basing.js";
27
- import { createKeeper, PreSit } from "../../db/keeping.js";
26
+ import { Baser } from "../../db/basing.js";
27
+ import { openOwner } from "../../db/core/store-owner.js";
28
+ import { Keeper, PreSit } from "../../db/keeping.js";
28
29
  import { makeNowIso8601 } from "../../time/mod.js";
29
30
  import { oobiReplyMessages } from "../bootstrap/oobi-replies.js";
30
31
  import { loadStoredReply } from "../delivery/stored-replies.js";
31
32
  import { acceptedGroupMembers, latestGroupContribution } from "../multisig/group-members.js";
32
- import { createConfiger } from "./configing.js";
33
33
  import { branToSaltQb64, encodeHugeNumber, ensureKeeperCryptoReady, Manager, normalizeSaltQb64, saltySigner, } from "./keeping.js";
34
34
  /** Reserved alias for the local signatory habitat record. */
35
35
  export const SIGNER = "__signatory__";
@@ -43,13 +43,16 @@ const KERI_V1 = Object.freeze({ major: 1, minor: 0 });
43
43
  /**
44
44
  * Select the KERI version for a new event on an existing identifier.
45
45
  *
46
- * An identifier keeps the KERI version of its accepted KEL, so rotation and
47
- * interaction never mix versions within one KEL. An explicit selection that
48
- * conflicts with the accepted KEL is refused rather than silently ignored.
46
+ * An identifier follows the KERI version of its latest accepted event. An explicit
47
+ * selection that conflicts with it is refused rather than silently ignored, except
48
+ * for a deliberate upgrade rotation, which is how a v1 identifier moves up to v2.
49
+ * Stricter than KERIpy, whose per-event default can mix versions without being asked.
49
50
  */
50
- function acceptedKelVersion(kever, requested) {
51
+ function acceptedKelVersion(kever, requested, { allowUpgrade = false } = {}) {
51
52
  const accepted = kever.serder.pvrsn.major === 1 ? Vrsn_1_0 : Vrsn_2_0;
52
53
  if (requested && (requested.major !== accepted.major || requested.minor !== accepted.minor)) {
54
+ if (allowUpgrade && requested.major > accepted.major)
55
+ return requested;
53
56
  throw new ValidationError(`KEL of ${kever.prefixer.qb64} is KERI ${accepted.major}.${accepted.minor}; `
54
57
  + `refusing a KERI ${requested.major}.${requested.minor} event.`);
55
58
  }
@@ -473,7 +476,7 @@ export class Hab {
473
476
  if (!kever) {
474
477
  throw new ValidationError(`Missing accepted key state for ${this.pre}.`);
475
478
  }
476
- const version = acceptedKelVersion(kever, args.version);
479
+ const version = acceptedKelVersion(kever, args.version, { allowUpgrade: true });
477
480
  const priorSit = this.ks.getSits(this.pre);
478
481
  if (!priorSit) {
479
482
  throw new ValidationError(`Missing keeper state for ${this.pre}.`);
@@ -1077,7 +1080,7 @@ export class Signator {
1077
1080
  */
1078
1081
  export class Habery {
1079
1082
  /** Compose one habery from already-opened storage, manager, and config surfaces. */
1080
- constructor(name, base, temp, headDirPath, compat, readonly, db, ks, mgr, cf, skipSignator = false) {
1083
+ constructor(name, base, temp, headDirPath, compat, readonly, db, ks, mgr, cf, skipSignator = false, opener, borrowsStores = false) {
1081
1084
  Object.defineProperty(this, "name", {
1082
1085
  enumerable: true,
1083
1086
  configurable: true,
@@ -1114,6 +1117,19 @@ export class Habery {
1114
1117
  writable: true,
1115
1118
  value: void 0
1116
1119
  });
1120
+ Object.defineProperty(this, "storeOpener", {
1121
+ enumerable: true,
1122
+ configurable: true,
1123
+ writable: true,
1124
+ value: void 0
1125
+ });
1126
+ /** True when `db` and `ks` were borrowed; their owner closes them. */
1127
+ Object.defineProperty(this, "borrowsStores", {
1128
+ enumerable: true,
1129
+ configurable: true,
1130
+ writable: true,
1131
+ value: void 0
1132
+ });
1117
1133
  Object.defineProperty(this, "db", {
1118
1134
  enumerable: true,
1119
1135
  configurable: true,
@@ -1174,6 +1190,8 @@ export class Habery {
1174
1190
  writable: true,
1175
1191
  value: void 0
1176
1192
  });
1193
+ this.storeOpener = opener;
1194
+ this.borrowsStores = borrowsStores;
1177
1195
  this.name = name;
1178
1196
  this.base = base;
1179
1197
  this.temp = temp;
@@ -1688,10 +1706,20 @@ export class Habery {
1688
1706
  this.habs.set(pre, hab);
1689
1707
  return hab;
1690
1708
  }
1691
- /** Closes backing databases and optionally clears temp storage. */
1709
+ /** Creates this Habery's sidecar stores. */
1710
+ get opener() {
1711
+ if (!this.storeOpener)
1712
+ throw new ValidationError("This Habery was given stores without an opener for sidecars.");
1713
+ return this.storeOpener;
1714
+ }
1715
+ /** Identity shared by this Habery's stores and sidecars. */
1716
+ storeIdentity() {
1717
+ return { name: this.name, base: this.base, temp: this.temp, reopen: true, readonly: this.readonly };
1718
+ }
1719
+ /** Closes owned backing databases and optionally clears temp storage. */
1692
1720
  *close(clear = false) {
1693
1721
  const failures = [];
1694
- const closing = [this.db.close(clear), this.ks.close(clear)];
1722
+ const closing = this.borrowsStores ? [] : [this.db.close(clear), this.ks.close(clear)];
1695
1723
  if (this.cf)
1696
1724
  closing.push(this.cf.close(clear && this.temp));
1697
1725
  for (const operation of closing) {
@@ -1726,38 +1754,29 @@ export function branToSeedAeid(bran) {
1726
1754
  * The returned `Habery` immediately reconstructs its local habitat cache from
1727
1755
  * durable DB state rather than depending on process-local creation history.
1728
1756
  */
1729
- export function* createHabery(args) {
1730
- const { name, base = "", temp = false, headDirPath, compat = false, readonly = false, cf: providedCf, skipConfig = false, skipSignator = false, bran, seed, aeid, salt, algo, } = args;
1757
+ export function* openHabery(args) {
1758
+ const { name, base = "", temp = false, headDirPath, compat = false, readonly = false, cf: providedCf, createConfig, skipConfig = false, skipSignator = false, opener, stores, bran, seed, aeid, salt, algo, } = args;
1731
1759
  let db;
1732
1760
  let ks;
1733
1761
  let cf;
1734
1762
  let delivered = false;
1735
1763
  let failure;
1736
1764
  try {
1737
- db = yield* createBaser({
1738
- name,
1739
- base,
1740
- temp,
1741
- headDirPath,
1742
- compat,
1743
- reopen: true,
1744
- readonly,
1745
- });
1746
- ks = yield* createKeeper({
1747
- name,
1748
- base,
1749
- temp,
1750
- headDirPath,
1751
- compat,
1752
- reopen: true,
1753
- readonly,
1754
- });
1765
+ if (!opener && !stores)
1766
+ throw new ValidationError("A Habery needs a store opener or borrowed stores.");
1767
+ if (!providedCf && !skipConfig && !createConfig) {
1768
+ throw new ValidationError("A Habery needs a Configer, a Configer factory or skipConfig.");
1769
+ }
1770
+ const identity = { name, base, temp, reopen: true, readonly };
1771
+ db = stores?.db ?? (yield* openOwner(new Baser(opener.create(Baser.Family, identity)), "Baser", identity));
1772
+ ks = stores?.ks ?? (yield* openOwner(new Keeper(opener.create(Keeper.Family, identity)), "Keeper", identity));
1755
1773
  cf = providedCf
1756
- ?? (skipConfig ? undefined : (yield* createConfiger({
1774
+ ?? (skipConfig ? undefined : (yield* createConfig({
1757
1775
  name,
1758
1776
  base,
1759
1777
  temp,
1760
1778
  headDirPath,
1779
+ compat,
1761
1780
  reopen: true,
1762
1781
  clear: false,
1763
1782
  })));
@@ -1779,7 +1798,7 @@ export function* createHabery(args) {
1779
1798
  algo,
1780
1799
  salt: normalizeSaltQb64(salt),
1781
1800
  });
1782
- const hby = new Habery(name, base, temp, headDirPath, compat, readonly, db, ks, mgr, cf, skipSignator);
1801
+ const hby = new Habery(name, base, temp, headDirPath, compat, readonly, db, ks, mgr, cf, skipSignator, opener, !!stores);
1783
1802
  delivered = true;
1784
1803
  return hby;
1785
1804
  }
@@ -1796,9 +1815,9 @@ export function* createHabery(args) {
1796
1815
  const closing = [];
1797
1816
  if (cf && cf !== providedCf)
1798
1817
  closing.push(cf.close());
1799
- if (ks)
1818
+ if (ks && !stores)
1800
1819
  closing.push(ks.close());
1801
- if (db)
1820
+ if (db && !stores)
1802
1821
  closing.push(db.close());
1803
1822
  for (const operation of closing) {
1804
1823
  try {
@@ -1195,7 +1195,7 @@ export function b64DecodeUrl(text) {
1195
1195
  /**
1196
1196
  * Backward-compatibility no-op for older app-layer startup seams.
1197
1197
  *
1198
- * CESR primitives now own libsodium readiness through their own module
1198
+ * CESR primitives now own crypto backend readiness through their own module
1199
1199
  * initialization, so higher layers no longer need an explicit keeper-crypto
1200
1200
  * readiness step.
1201
1201
  */
@@ -0,0 +1,12 @@
1
+ /** Default LMDB-backed `Habery` construction, kept apart so `openHabery` stays backend-neutral. */
2
+ import { LmdbStoreOpener } from "../../db/core/lmdb-opener.js";
3
+ import { createConfiger } from "./configing.js";
4
+ import { openHabery } from "./habbing.js";
5
+ /** Open a `Habery` on LMDB at its established paths, unless the caller supplies stores or an opener. */
6
+ export function* createHabery(args) {
7
+ return yield* openHabery({
8
+ opener: new LmdbStoreOpener({ headDirPath: args.headDirPath, compat: args.compat }),
9
+ createConfig: createConfiger,
10
+ ...args,
11
+ });
12
+ }
@@ -1,6 +1,6 @@
1
1
  /** Borrowed IPEX frame DAGs, negotiated disclosure paths and traversal policy. */
2
2
  import { Compactor, makePather, SerderACDC } from "cesr-ts";
3
- import { acdcEdgeBlocks as blocks, acdcEdgeTarget, acdcGroupLabels } from "../../acdc/chaining.js";
3
+ import { walkAcdcGraph } from "../../acdc/chaining.js";
4
4
  const nodeIlks = new Set([null, "acm", "ace", "act", "acg"]);
5
5
  /** Empty UUID marks field-map metadata; fixed-field public credentials keep that slot empty too. */
6
6
  export function isIpexMetadata(node) {
@@ -58,15 +58,15 @@ export function singleDagDisclosurePlan(value) {
58
58
  return Array.isArray(value) && value.length === 1 && validDisclosurePlan(value[0]) ? value[0] : null;
59
59
  }
60
60
  /**
61
- * Index unique disclosed credential nodes and walk their reachable graph in BFS order.
62
- * Offers may omit far nodes; grants require every referenced node. Nested ownership
61
+ * Index unique disclosed credential frames, then delegate the graph walk to `walkAcdcGraph`.
62
+ * Offers always walk open; Grants use the caller's walk policy. Nested ownership
63
63
  * does not make these nodes independently accepted runtime messages.
64
64
  */
65
- export function walkIpexGraph(origin, nests, closed) {
65
+ export function walkIpexGraph(origin, nests, walk) {
66
66
  const nodes = new Map();
67
67
  for (const [index, frame] of nests.entries()) {
68
68
  const body = frame.body;
69
- if (!(body instanceof SerderACDC) || !body.verify() || !nodeIlks.has(body.ilk)
69
+ if (!(body instanceof SerderACDC) || !nodeIlks.has(body.ilk)
70
70
  || !body.said || (index === 0 && body.said !== origin) || nodes.has(body.said))
71
71
  return null;
72
72
  // Descendants are not additional implicit evidence for this disclosed node.
@@ -74,71 +74,8 @@ export function walkIpexGraph(origin, nests, closed) {
74
74
  return null;
75
75
  nodes.set(body.said, frame);
76
76
  }
77
- if (!nodes.has(origin))
78
- return null;
79
- const seen = new Set();
80
- const order = [];
81
- const dependencies = new Map();
82
- const queue = [origin];
83
- for (let index = 0; index < queue.length; index++) {
84
- const said = queue[index];
85
- if (seen.has(said))
86
- continue;
87
- seen.add(said);
88
- order.push(said);
89
- const farNodes = new Set();
90
- dependencies.set(said, farNodes);
91
- const edges = blocks(nodes.get(said).body.sad.e);
92
- if (!edges)
93
- return null;
94
- const groups = [...edges];
95
- for (let g = 0; g < groups.length; g++) {
96
- const edge = groups[g];
97
- const target = acdcEdgeTarget(edge);
98
- if (target === false)
99
- return null;
100
- if (typeof target === "string") {
101
- if (!nodes.has(target)) {
102
- if (closed)
103
- return null;
104
- }
105
- else {
106
- farNodes.add(target);
107
- if (!seen.has(target))
108
- queue.push(target);
109
- }
110
- }
111
- else {
112
- if (!record(edge))
113
- return null;
114
- for (const [key, value] of Object.entries(edge)) {
115
- if (acdcGroupLabels.has(key) || typeof value === "string")
116
- continue;
117
- groups.push(value);
118
- }
119
- }
120
- }
121
- }
122
- if (seen.size !== nodes.size)
123
- return null;
124
- const parents = new Map();
125
- for (const [near, children] of dependencies) {
126
- for (const far of children) {
127
- if (!parents.has(far))
128
- parents.set(far, new Set());
129
- parents.get(far).add(near);
130
- }
131
- }
132
- const dependencyOrder = order.filter((said) => dependencies.get(said).size === 0);
133
- for (let i = 0; i < dependencyOrder.length; i++) {
134
- for (const near of parents.get(dependencyOrder[i]) ?? []) {
135
- const children = dependencies.get(near);
136
- children.delete(dependencyOrder[i]);
137
- if (!children.size)
138
- dependencyOrder.push(near);
139
- }
140
- }
141
- return dependencyOrder.length === nodes.size ? { nodes, order } : null;
77
+ const order = walkAcdcGraph(origin, nodes, walk);
78
+ return order ? { nodes, order, walk } : null;
142
79
  }
143
80
  /** Compare SAD values structurally while retaining list order; map insertion order is not semantic here. */
144
81
  function equal(a, b) {
@@ -29,10 +29,11 @@ import { collectBytes } from "../transport/byte-source.js";
29
29
  import { credentialGrantReady, credentialPresentationSupportMessages, credentialSaidFromGrant, validateAdmitGrant, } from "./ipex-credentialing.js";
30
30
  import { walkIpexGraph } from "./ipex-graph.js";
31
31
  import { authenticateIpexNode, issuedNodeVerdict } from "./ipex-node-authentication.js";
32
+ import { ipexGrantWalk } from "./ipex-v2.js";
32
33
  import { IPEX_ADMIT_ROUTE, IPEX_GRANT_ROUTE, ipexConversationReady, ipexRecipient } from "./ipexing.js";
33
34
  /** Authenticate the exact disclosed graph; only the existing evaluator's explicit true authorizes new use. */
34
- function acdcV2GraphUse(hby, registry, origin, nests) {
35
- const graph = walkIpexGraph(origin, nests, true);
35
+ function acdcV2GraphUse(hby, registry, origin, nests, walk) {
36
+ const graph = walkIpexGraph(origin, nests, walk);
36
37
  if (!graph)
37
38
  throw new ValidationError("ACDC v2 group graph is malformed or incomplete.");
38
39
  const verdicts = new Map();
@@ -55,7 +56,7 @@ function acdcV2GrantUse(hby, exchanger, registry, said) {
55
56
  if (!Array.isArray(origins) || origins.length !== 1 || typeof origins[0] !== "string") {
56
57
  throw new ValidationError("ACDC v2 group Grant requires one origin.");
57
58
  }
58
- return acdcV2GraphUse(hby, registry, origins[0], loadNests(hby.db, said));
59
+ return acdcV2GraphUse(hby, registry, origins[0], loadNests(hby.db, said), ipexGrantWalk(exchanger));
59
60
  }
60
61
  /** Recheck current graph authorization at an accepted group's new delivery, output or consent action. */
61
62
  export function requireAcdcV2GroupUse(hby, protocol, embedded) {
@@ -110,8 +111,10 @@ export function* prepareAcdcV2GroupIpex(hby, protocol, group, raw) {
110
111
  if (!Array.isArray(origins) || origins.length !== 1 || typeof origins[0] !== "string") {
111
112
  throw new ValidationError("ACDC v2 group Grant requires one origin.");
112
113
  }
113
- if (acdcV2GraphUse(hby, protocol.registry?.store ?? null, origins[0], frame.nests ?? []) !== true)
114
+ const walk = ipexGrantWalk(protocol.exc);
115
+ if (acdcV2GraphUse(hby, protocol.registry?.store ?? null, origins[0], frame.nests ?? [], walk) !== true) {
114
116
  return null;
117
+ }
115
118
  }
116
119
  else if (embedded.route === IPEX_ADMIT_ROUTE) {
117
120
  if (typeof embedded.sad.p !== "string"
@@ -353,7 +356,7 @@ function* prepareGroupIpexMessages(hby, protocol, groupHab, embedded, childGvrsn
353
356
  if (child.route === IPEX_GRANT_ROUTE) {
354
357
  const origins = child.sad.a.o;
355
358
  if (!Array.isArray(origins) || origins.length !== 1 || typeof origins[0] !== "string"
356
- || acdcV2GraphUse(hby, protocol.registry?.store ?? null, origins[0], childFrame.nests ?? []) !== true) {
359
+ || acdcV2GraphUse(hby, protocol.registry?.store ?? null, origins[0], childFrame.nests ?? [], ipexGrantWalk(protocol.exc)) !== true) {
357
360
  throw new ValidationError("ACDC v2 group graph is unavailable before proposal publication.");
358
361
  }
359
362
  }
@@ -1,7 +1,7 @@
1
1
  import { IPEX_V2_PREDECESSORS, isIpexVerb, nestedIpexRoute, } from "../../core/exchange/protocol-exchanging.js";
2
2
  /** V2 IPEX route policy; v1 routing remains independently registered. */
3
3
  import { Diger, ParserError, Prefixer, } from "cesr-ts";
4
- import { evaluateChain } from "../../acdc/chaining.js";
4
+ import { DEFAULT_GRANT_WALK, evaluateChain } from "../../acdc/chaining.js";
5
5
  import { validateUpdate, verifyRegistryAnchor, vet, vetBinds, vetBlind, vetBound, } from "../../acdc/regeventing.js";
6
6
  import { validateSchema } from "../../acdc/scheming.js";
7
7
  import { DatabaseOperationError } from "../../core/errors.js";
@@ -31,9 +31,11 @@ function missing(reason) {
31
31
  * Authenticate each disclosed node independently before Exchanger persists an EXN.
32
32
  * Registry evidence is caller-owned; notifier delivery follows durable acceptance.
33
33
  * Presentation registries require the retained receiver receipt and node-local proofs.
34
+ * `grantWalk` selects how a Grant treats an undisclosed far node (`AcdcWalkPolicy`);
35
+ * a far node the disclosure plan selects but the Grant does not nest always refuses.
34
36
  */
35
37
  export class IpexV2Handler {
36
- constructor(resource, hby, notifier = null, registry = null) {
38
+ constructor(resource, hby, notifier = null, registry = null, grantWalk = DEFAULT_GRANT_WALK) {
37
39
  Object.defineProperty(this, "resource", {
38
40
  enumerable: true,
39
41
  configurable: true,
@@ -58,6 +60,12 @@ export class IpexV2Handler {
58
60
  writable: true,
59
61
  value: registry
60
62
  });
63
+ Object.defineProperty(this, "grantWalk", {
64
+ enumerable: true,
65
+ configurable: true,
66
+ writable: true,
67
+ value: grantWalk
68
+ });
61
69
  Object.defineProperty(this, "protocolMajor", {
62
70
  enumerable: true,
63
71
  configurable: true,
@@ -266,7 +274,7 @@ export class IpexV2Handler {
266
274
  if (verb === "offer" && nests.length) {
267
275
  if (nests.some(hasIpexNodeAttachments))
268
276
  return false;
269
- const graph = walkIpexGraph(attrs.o[0], nests, false);
277
+ const graph = walkIpexGraph(attrs.o[0], nests, "open");
270
278
  return graph ? this.nodeSchemas(graph) : false;
271
279
  }
272
280
  if (verb !== "grant")
@@ -287,7 +295,7 @@ export class IpexV2Handler {
287
295
  if (!single(attrs.o, "string") || !this.validOrigin(attrs.o[0]) || !nests.length)
288
296
  return false;
289
297
  const origin = attrs.o[0];
290
- const graph = walkIpexGraph(origin, nests, true);
298
+ const graph = walkIpexGraph(origin, nests, this.grantWalk);
291
299
  if (!graph || [...graph.nodes.values()].some((node) => isIpexMetadata(node.body)))
292
300
  return false;
293
301
  if (!verifyIpexDisclosure(plan, origin, graph))
@@ -492,7 +500,7 @@ export class IpexV2Handler {
492
500
  const nests = loadNests(this.hby.db, offerSaid);
493
501
  if (!nests.length)
494
502
  return missing(`Missing metadata ACDC for accepted Offer ${offerSaid}.`);
495
- const offered = walkIpexGraph(offeredOrigin, nests, false);
503
+ const offered = walkIpexGraph(offeredOrigin, nests, "open");
496
504
  if (!offered)
497
505
  return false;
498
506
  const metadata = offered.nodes.get(offeredOrigin).body;
@@ -690,3 +698,11 @@ export class IpexV2Handler {
690
698
  return [[issuee, regid]];
691
699
  }
692
700
  }
701
+ /**
702
+ * The Grant walk policy of the exchanger's v2 Grant owner. Later use of an
703
+ * accepted Grant re-walks it under this policy so reuse agrees with admission.
704
+ */
705
+ export function ipexGrantWalk(exchanger) {
706
+ const handler = exchanger.routeHandler("/ipex/grant", 2);
707
+ return handler instanceof IpexV2Handler ? handler.grantWalk : DEFAULT_GRANT_WALK;
708
+ }
@@ -111,7 +111,7 @@ export class IpexHandler {
111
111
  export function loadIpexHandlers(hby, exchanger, options = {}) {
112
112
  for (const route of IPEX_ROUTES) {
113
113
  exchanger.addHandler(new IpexHandler(route, hby, options.notifier ?? null));
114
- exchanger.addHandler(new IpexV2Handler(route, hby, options.notifier ?? null, options.registry ?? null));
114
+ exchanger.addHandler(new IpexV2Handler(route, hby, options.notifier ?? null, options.registry ?? null, options.grantWalk));
115
115
  }
116
116
  }
117
117
  /** Apply for an ACDC. */
@@ -1,6 +1,7 @@
1
1
  import { TopicsRecord } from "../../core/records.js";
2
2
  import { Roles } from "../../core/roles.js";
3
- import { createMailboxer } from "../../db/mailbox/mailboxing.js";
3
+ import { openOwner } from "../../db/core/store-owner.js";
4
+ import { Mailboxer } from "../../db/mailbox/mailboxing.js";
4
5
  /**
5
6
  * Derive mailbox open options from one existing habery.
6
7
  *
@@ -13,13 +14,7 @@ import { createMailboxer } from "../../db/mailbox/mailboxing.js";
13
14
  */
14
15
  export function mailboxerOptionsForHabery(hby, options = {}) {
15
16
  return {
16
- name: hby.name,
17
- base: hby.base,
18
- temp: hby.temp,
19
- headDirPath: hby.headDirPath,
20
- compat: hby.compat,
21
- readonly: hby.readonly,
22
- reopen: true,
17
+ ...hby.storeIdentity(),
23
18
  ...options,
24
19
  };
25
20
  }
@@ -35,7 +30,8 @@ export function mailboxerOptionsForHabery(hby, options = {}) {
35
30
  * @returns Provider sidecar whose acquisition/lifetime belongs to the caller scope.
36
31
  */
37
32
  export function* openMailboxerForHabery(hby, options = {}) {
38
- return yield* createMailboxer(mailboxerOptionsForHabery(hby, options));
33
+ const identity = mailboxerOptionsForHabery(hby, options);
34
+ return yield* openOwner(new Mailboxer(hby.opener.create(Mailboxer.Family, identity)), "Mailboxer", identity);
39
35
  }
40
36
  /**
41
37
  * Build the stored mailbox topic key used by KERIpy-style mailbox storage.
@@ -64,6 +64,20 @@ export function groupWitnesserSelection(db, group, { sn, said }) {
64
64
  }
65
65
  return null;
66
66
  }
67
+ /**
68
+ * Return the current signing member elected by `coordinator`, as KERIpy
69
+ * `GroupHab.elected`: the member whose latest contribution to the coordinator's
70
+ * establishment keys holds the lowest verified signing index. Null when the
71
+ * event has no election or that contributor is no longer a current signing
72
+ * member. The result depends on locally stored signatures, so it suits
73
+ * idempotent duties only; frozen operations recheck their earlier choice instead.
74
+ */
75
+ export function groupElectedMember(hby, group, coordinator) {
76
+ const selection = groupWitnesserSelection(hby.db, group, coordinator);
77
+ if (!selection)
78
+ return null;
79
+ return groupSigningMembers(hby, group).find(member => latestGroupContribution(hby.db, member, selection.establishment.verfers)?.index === selection.index) ?? null;
80
+ }
67
81
  /** Return current group signing member AIDs in signing-index order. */
68
82
  export function groupSigningMembers(hby, groupPre) {
69
83
  const stored = hby.ks.getSmids(groupPre).map((tuple) => tuple[0].qb64);
@@ -304,8 +304,9 @@ export function* prepareMultisigEventExn(sender, proposal, wire, options = {}) {
304
304
  // Keep the route selection stable through parser suspension; attribute values remain immutable borrows.
305
305
  const captured = { ...proposal };
306
306
  const { route, attrs } = captured;
307
- // KERIpy `kli multisig incept|interact|rotate` and `ends add` send these routes as v1
308
- // `e`-embed wrappers whatever the group KEL version, and `kli multisig join` reads only `e`.
307
+ // KERIpy `kli multisig incept|interact` and `ends add` send these routes as v1 `e`-embed
308
+ // wrappers whatever the group KEL version. `kli multisig rotate --version 2.0` sends a v2
309
+ // nested wrapper, and `kli multisig join` replies in its proposal's form, as approvals here do.
309
310
  const keripyEmbedRoute = KERIPY_EMBED_PROPOSAL_ROUTES.includes(route);
310
311
  const version = options.pvrsn ?? options.version ?? (keripyEmbedRoute ? Vrsn_1_0 : Vrsn_2_0);
311
312
  const field = route.slice(route.lastIndexOf("/") + 1);
@@ -547,7 +548,7 @@ export class Multiplexor {
547
548
  if (this.isLocalHab(sender))
548
549
  yield* this.processApproved(nests);
549
550
  }
550
- const { existing, submitters } = this.hby.db.env.transactionSync(() => {
551
+ const { existing, submitters } = this.hby.db.transaction(() => {
551
552
  const indexed = this.hby.db.meids.get([embeddedSaid]);
552
553
  const existing = isV2RegistryRoute(route) || v2Ipex
553
554
  ? indexed.filter(diger => {
@@ -152,9 +152,10 @@ export function* publishAcdcV2Registry(hby, postman, group, approval) {
152
152
  * @param proposal Route-correlated group proposal with its named business fields.
153
153
  * @param embeddedMessage Signed child wire to include under the route-derived label.
154
154
  * @param childGvrsn Optional explicit attachment genus for the child.
155
+ * @param wrapperVersion Optional wrapper protocol version; an approval mirrors its proposal's.
155
156
  * @returns Stable proposal/wrapper identities and completed deliveries, independent of group acceptance.
156
157
  */
157
- export function* publishProposal(hby, protocol, postman, member, recipients, proposal, embeddedMessage, childGvrsn) {
158
+ export function* publishProposal(hby, protocol, postman, member, recipients, proposal, embeddedMessage, childGvrsn, wrapperVersion) {
158
159
  const input = new Reactor(protocol);
159
160
  const outcomes = [];
160
161
  for (const recipient of remoteGroupMembers(hby, member, recipients)) {
@@ -162,6 +163,7 @@ export function* publishProposal(hby, protocol, postman, member, recipients, pro
162
163
  const prepared = yield* prepareMultisigEventExn(member.pre, proposal, embeddedMessage, {
163
164
  recipient: destination,
164
165
  childGvrsn,
166
+ ...(wrapperVersion ? { pvrsn: wrapperVersion } : {}),
165
167
  });
166
168
  const signed = yield* prepareGroupMessage(protocol.exc, member, destination, prepared);
167
169
  outcomes.push(yield* admitAndSendGroupMessage(input, postman, signed));
@@ -444,7 +446,10 @@ function* approveKelProposal(hby, runtime, exn, route, options) {
444
446
  yield* runtime.reactor.processBytes(concatBytes(Counter.makeGVC(approval.genus), localMessage), { local: true });
445
447
  yield* runtime.reactor.processBytes(concatBytes(Counter.makeGVC(approval.genus), serder.raw, approval.attachment), { local: true });
446
448
  yield* runtime.protocol.processEscrowsOnce();
447
- const publication = yield* publishProposal(hby, runtime.protocol, runtime.postman, member, members, proposal, localMessage, approval.genus);
449
+ const publication = yield* publishProposal(hby, runtime.protocol, runtime.postman, member, members, proposal, localMessage, approval.genus,
450
+ // KERIpy indexes contributions by wrapper form (v1 `e` SAID, v2 child SAID), so an
451
+ // approval must reuse its proposal's wrapper version for the proposer to merge it.
452
+ exn.pvrsn);
448
453
  const accepted = eventAccepted(hby, serder);
449
454
  if (proposal.route !== MULTISIG_IXN_ROUTE) {
450
455
  commitAcceptedGroupMembers(hby, serder, smids, proposal.attrs.rmids);