@interop/wallet-core 0.48.0 → 0.50.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 (163) hide show
  1. package/README.md +5 -2
  2. package/dist/clientAnnex/credentialAnchoredGenesis.d.ts +118 -0
  3. package/dist/clientAnnex/credentialAnchoredGenesis.d.ts.map +1 -0
  4. package/dist/clientAnnex/credentialAnchoredGenesis.js +174 -0
  5. package/dist/clientAnnex/credentialAnchoredGenesis.js.map +1 -0
  6. package/dist/clientAnnex/forget.d.ts +138 -0
  7. package/dist/clientAnnex/forget.d.ts.map +1 -0
  8. package/dist/clientAnnex/forget.js +133 -0
  9. package/dist/clientAnnex/forget.js.map +1 -0
  10. package/dist/clientAnnex/forgetLast.d.ts +267 -0
  11. package/dist/clientAnnex/forgetLast.d.ts.map +1 -0
  12. package/dist/clientAnnex/forgetLast.js +374 -0
  13. package/dist/clientAnnex/forgetLast.js.map +1 -0
  14. package/dist/clientAnnex/gc.d.ts +255 -0
  15. package/dist/clientAnnex/gc.d.ts.map +1 -0
  16. package/dist/clientAnnex/gc.js +445 -0
  17. package/dist/clientAnnex/gc.js.map +1 -0
  18. package/dist/clientAnnex/index.d.ts +60 -0
  19. package/dist/clientAnnex/index.d.ts.map +1 -0
  20. package/dist/clientAnnex/index.js +60 -0
  21. package/dist/clientAnnex/index.js.map +1 -0
  22. package/dist/{unlock → clientAnnex}/ladder.d.ts +99 -23
  23. package/dist/clientAnnex/ladder.d.ts.map +1 -0
  24. package/dist/clientAnnex/ladder.js +474 -0
  25. package/dist/clientAnnex/ladder.js.map +1 -0
  26. package/dist/clientAnnex/ladderAnchored.d.ts +276 -0
  27. package/dist/clientAnnex/ladderAnchored.d.ts.map +1 -0
  28. package/dist/clientAnnex/ladderAnchored.js +631 -0
  29. package/dist/clientAnnex/ladderAnchored.js.map +1 -0
  30. package/dist/{webvh/companion.d.ts → clientAnnex/log.d.ts} +373 -152
  31. package/dist/clientAnnex/log.d.ts.map +1 -0
  32. package/dist/{webvh/companion.js → clientAnnex/log.js} +654 -261
  33. package/dist/clientAnnex/log.js.map +1 -0
  34. package/dist/clientAnnex/recoveryLadderAnchored.d.ts +103 -0
  35. package/dist/clientAnnex/recoveryLadderAnchored.d.ts.map +1 -0
  36. package/dist/clientAnnex/recoveryLadderAnchored.js +263 -0
  37. package/dist/clientAnnex/recoveryLadderAnchored.js.map +1 -0
  38. package/dist/{unlock → clientAnnex}/selfEnroll.d.ts +1 -1
  39. package/dist/clientAnnex/selfEnroll.d.ts.map +1 -0
  40. package/dist/{unlock → clientAnnex}/selfEnroll.js +1 -1
  41. package/dist/clientAnnex/selfEnroll.js.map +1 -0
  42. package/dist/clientAnnex/zcap.d.ts +49 -0
  43. package/dist/clientAnnex/zcap.d.ts.map +1 -0
  44. package/dist/clientAnnex/zcap.js +101 -0
  45. package/dist/clientAnnex/zcap.js.map +1 -0
  46. package/dist/clients/index.d.ts +9 -6
  47. package/dist/clients/index.d.ts.map +1 -1
  48. package/dist/clients/index.js +8 -5
  49. package/dist/clients/index.js.map +1 -1
  50. package/dist/clients/listing.d.ts +32 -0
  51. package/dist/clients/listing.d.ts.map +1 -1
  52. package/dist/clients/listing.js +47 -1
  53. package/dist/clients/listing.js.map +1 -1
  54. package/dist/clients/revocation.d.ts +33 -1
  55. package/dist/clients/revocation.d.ts.map +1 -1
  56. package/dist/clients/revocation.js +16 -4
  57. package/dist/clients/revocation.js.map +1 -1
  58. package/dist/enrollment/index.d.ts +6 -4
  59. package/dist/enrollment/index.d.ts.map +1 -1
  60. package/dist/enrollment/index.js +6 -4
  61. package/dist/enrollment/index.js.map +1 -1
  62. package/dist/enrollment/onboardingInvite.d.ts +5 -88
  63. package/dist/enrollment/onboardingInvite.d.ts.map +1 -1
  64. package/dist/enrollment/onboardingInvite.js +5 -168
  65. package/dist/enrollment/onboardingInvite.js.map +1 -1
  66. package/dist/genesis/index.d.ts +5 -0
  67. package/dist/genesis/index.d.ts.map +1 -1
  68. package/dist/genesis/index.js +5 -0
  69. package/dist/genesis/index.js.map +1 -1
  70. package/dist/keys/index.d.ts +1 -1
  71. package/dist/keys/index.d.ts.map +1 -1
  72. package/dist/keys/index.js +1 -1
  73. package/dist/keys/index.js.map +1 -1
  74. package/dist/keys/spaceEpochs.d.ts +7 -2
  75. package/dist/keys/spaceEpochs.d.ts.map +1 -1
  76. package/dist/keys/spaceEpochs.js +8 -2
  77. package/dist/keys/spaceEpochs.js.map +1 -1
  78. package/dist/keys/userKeyRoster.d.ts +39 -0
  79. package/dist/keys/userKeyRoster.d.ts.map +1 -1
  80. package/dist/keys/userKeyRoster.js +43 -1
  81. package/dist/keys/userKeyRoster.js.map +1 -1
  82. package/dist/recovery/index.d.ts +5 -3
  83. package/dist/recovery/index.d.ts.map +1 -1
  84. package/dist/recovery/index.js +4 -2
  85. package/dist/recovery/index.js.map +1 -1
  86. package/dist/recovery/recoveryDelegation.d.ts +35 -12
  87. package/dist/recovery/recoveryDelegation.d.ts.map +1 -1
  88. package/dist/recovery/recoveryDelegation.js +84 -86
  89. package/dist/recovery/recoveryDelegation.js.map +1 -1
  90. package/dist/recovery/recoveryWebvh.d.ts +32 -1
  91. package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
  92. package/dist/recovery/recoveryWebvh.js +2 -2
  93. package/dist/recovery/recoveryWebvh.js.map +1 -1
  94. package/dist/request/capabilityRequest.d.ts +33 -0
  95. package/dist/request/capabilityRequest.d.ts.map +1 -0
  96. package/dist/request/capabilityRequest.js +35 -0
  97. package/dist/request/capabilityRequest.js.map +1 -0
  98. package/dist/request/ephemeralExchange.d.ts +118 -0
  99. package/dist/request/ephemeralExchange.d.ts.map +1 -0
  100. package/dist/request/ephemeralExchange.js +224 -0
  101. package/dist/request/ephemeralExchange.js.map +1 -0
  102. package/dist/request/index.d.ts +6 -0
  103. package/dist/request/index.d.ts.map +1 -1
  104. package/dist/request/index.js +6 -0
  105. package/dist/request/index.js.map +1 -1
  106. package/dist/space/activity.d.ts +38 -0
  107. package/dist/space/activity.d.ts.map +1 -1
  108. package/dist/space/activity.js +41 -1
  109. package/dist/space/activity.js.map +1 -1
  110. package/dist/space/index.d.ts +1 -1
  111. package/dist/space/index.d.ts.map +1 -1
  112. package/dist/space/index.js +1 -1
  113. package/dist/space/index.js.map +1 -1
  114. package/dist/unlock/index.d.ts +11 -23
  115. package/dist/unlock/index.d.ts.map +1 -1
  116. package/dist/unlock/index.js +10 -21
  117. package/dist/unlock/index.js.map +1 -1
  118. package/dist/unlock/retire.d.ts +47 -3
  119. package/dist/unlock/retire.d.ts.map +1 -1
  120. package/dist/unlock/retire.js +21 -5
  121. package/dist/unlock/retire.js.map +1 -1
  122. package/dist/unlock/standingWebvh.d.ts +37 -82
  123. package/dist/unlock/standingWebvh.d.ts.map +1 -1
  124. package/dist/unlock/standingWebvh.js +64 -247
  125. package/dist/unlock/standingWebvh.js.map +1 -1
  126. package/dist/unlock/unlockRecord.d.ts +13 -6
  127. package/dist/unlock/unlockRecord.d.ts.map +1 -1
  128. package/dist/unlock/unlockRecord.js +14 -8
  129. package/dist/unlock/unlockRecord.js.map +1 -1
  130. package/dist/webvh/delegatedLogStore.d.ts +5 -5
  131. package/dist/webvh/delegatedLogStore.js +1 -1
  132. package/dist/webvh/didWebvh.d.ts +20 -10
  133. package/dist/webvh/didWebvh.d.ts.map +1 -1
  134. package/dist/webvh/didWebvh.js +17 -17
  135. package/dist/webvh/didWebvh.js.map +1 -1
  136. package/dist/webvh/index.d.ts +9 -23
  137. package/dist/webvh/index.d.ts.map +1 -1
  138. package/dist/webvh/index.js +9 -22
  139. package/dist/webvh/index.js.map +1 -1
  140. package/dist/webvh/revokeClient.d.ts +77 -5
  141. package/dist/webvh/revokeClient.d.ts.map +1 -1
  142. package/dist/webvh/revokeClient.js +154 -61
  143. package/dist/webvh/revokeClient.js.map +1 -1
  144. package/dist/webvh/standingZcap.d.ts +11 -1
  145. package/dist/webvh/standingZcap.d.ts.map +1 -1
  146. package/dist/webvh/standingZcap.js +13 -14
  147. package/dist/webvh/standingZcap.js.map +1 -1
  148. package/dist/webvh/wasIdStore.d.ts +13 -7
  149. package/dist/webvh/wasIdStore.d.ts.map +1 -1
  150. package/dist/webvh/wasIdStore.js +8 -4
  151. package/dist/webvh/wasIdStore.js.map +1 -1
  152. package/dist/webvh/zcap.d.ts +0 -24
  153. package/dist/webvh/zcap.d.ts.map +1 -1
  154. package/dist/webvh/zcap.js +0 -42
  155. package/dist/webvh/zcap.js.map +1 -1
  156. package/package.json +40 -34
  157. package/dist/unlock/ladder.d.ts.map +0 -1
  158. package/dist/unlock/ladder.js +0 -254
  159. package/dist/unlock/ladder.js.map +0 -1
  160. package/dist/unlock/selfEnroll.d.ts.map +0 -1
  161. package/dist/unlock/selfEnroll.js.map +0 -1
  162. package/dist/webvh/companion.d.ts.map +0 -1
  163. package/dist/webvh/companion.js.map +0 -1
package/README.md CHANGED
@@ -57,8 +57,11 @@ The subpaths:
57
57
  injected), the App Connect app-key credential module (query validation,
58
58
  matching, minting, store-time refusal, legacy re-issue), the
59
59
  `WalletOnboardingQuery` transport vocabulary (compose and classification), the
60
- VC-API exchange client, and VCALM `interaction:` URL handling. The VPR type
61
- vocabulary lives in
60
+ VC-API exchange client, VCALM `interaction:` URL handling, the requester's
61
+ half of a server's ephemeral exchange (`createEphemeralExchange` /
62
+ `pollEphemeralExchange`, the poll bounded by a caller signal or its own
63
+ deadline), and `composeCapabilityRequest`, the zcap-only VPR a requester
64
+ stores on such an exchange. The VPR type vocabulary lives in
62
65
  [`@interop/data-integrity-core`](https://npm.im/@interop/data-integrity-core)
63
66
  and is re-exported here.
64
67
 
@@ -0,0 +1,118 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The account-genesis ceremony's CREDENTIAL-ANCHORED variant: such a
6
+ * signup mints no durable client, so the ceremony is anchored on the unlock
7
+ * credential's ladder alone. The stage order mirrors `ensureAccountGenesis`
8
+ * with the durable-client members swapped for their ladder analogs:
9
+ *
10
+ * 1. Space provisioning under the ladder VM's bare did:key -- the bootstrap
11
+ * controller (wallet-core decision 0004, freewallet decision 0002's
12
+ * resolution of the orphan-Space tear): re-derivable from the unlock
13
+ * record's ladder seed, so a tab death here strands nothing a later
14
+ * login cannot finish or unwind.
15
+ * 2. The ladder-anchored did:webvh genesis
16
+ * (`ensureLadderAnchoredDidWebvh`): the entry signed by ladder rung 0, `updateKeys` = [rung 0], `nextKeyHashes`
17
+ * = [hash(rung 0), hash(rung 1)], the ladder VM and the credential's
18
+ * `keyAgreement` posture folded in, `portable` unchanged. There is no KMS
19
+ * stage: the keystore is DEFERRED to the first durable enrollment (a
20
+ * ladder-anchored account has no consumer for the convenience VM, and a
21
+ * keystore under an evaporating identity would orphan).
22
+ * 3. The user-key roster genesis, wrapped to the CREDENTIAL's standing
23
+ * key-agreement key -- the only recipient a ladder-anchored account has
24
+ * -- with the entry proof signed by the ladder VM (the ceremony-tail
25
+ * license's first-entry shape). The account is credential-recoverable
26
+ * from the moment this lands.
27
+ * 4. Epoch[0] on every encrypted roster collection -- gated on the roster
28
+ * stage landing, unlike the durable flow: the user key here exists only
29
+ * in this tab's memory, so installing collection epochs under a key the
30
+ * roster does not deliver would strand the collections on a key that
31
+ * dies with the tab. With the gate, the tear heal is always clean: no
32
+ * roster means no epochs, and a fresh user key re-runs both.
33
+ * 5. Space-controller promotion, last -- every earlier stage ran under the
34
+ * bootstrap did:key the Space's stored controller authorizes.
35
+ *
36
+ * Idempotent end to end on the durable flow's convention; the did:webvh
37
+ * stage adopts a published log iff the ladder attributes it (a naive re-run
38
+ * would mint a different SCID, so adoption IS the torn-signup convergence).
39
+ */
40
+ import { type RecipientPublicKey } from '@interop/was-client/edv';
41
+ import type { WasClient } from '@interop/was-client';
42
+ import type { EncryptionDescriptorStore } from '@interop/was-client/edv';
43
+ import type { WebvhIdStore } from '../webvh/didWebvh.js';
44
+ import type { UnlockKeyAgreementPublication } from '../unlock/standingWebvh.js';
45
+ import { type UserKey } from '../keys/index.js';
46
+ import type { ResourceLogPinStore } from '../resourceLog/index.js';
47
+ import { type AccountGenesisResult } from '../genesis/accountGenesis.js';
48
+ /**
49
+ * The key set a credential-anchored signup mints locally before anything
50
+ * touches the network: the data Space id and the account's user key -- nothing else.
51
+ * No client seed and no client update-key seeds exist (that is the point);
52
+ * the ladder seed is minted by the unlock layer beside the record that
53
+ * carries it, and every other key derives from it.
54
+ *
55
+ * @returns {Promise<{ spaceId: string, userKey: Required<UserKey> }>}
56
+ */
57
+ export declare function mintCredentialAnchoredAccountKeySet(): Promise<{
58
+ spaceId: string;
59
+ userKey: Required<UserKey>;
60
+ }>;
61
+ /**
62
+ * Runs the credential-anchored account-genesis ceremony (see the module doc
63
+ * for the stage order and its whys). The caller has already durably written the
64
+ * unlock record carrying the ladder seed -- the transposed
65
+ * persist-before-publish rule: rung 0 must never publish before the seed
66
+ * that derives it is recoverable.
67
+ *
68
+ * @param options {object}
69
+ * @param options.was {WasClient} signing as the ladder VM's bare did:key
70
+ * (the bootstrap controller; build it over `ladderVmAgent`)
71
+ * @param options.wasServerUrl {string}
72
+ * @param options.spaceId {string}
73
+ * @param options.ladderSeed {Uint8Array} the credential's ladder seed,
74
+ * already sealed into the durably written unlock record
75
+ * @param options.keyAgreement {UnlockKeyAgreementPublication} the
76
+ * credential's key-agreement publication (commitment or verbatim), folded
77
+ * into the genesis document
78
+ * @param options.standingRecipient {RecipientPublicKey} the credential's
79
+ * standing key-agreement key as a roster recipient (`id` its roster kid)
80
+ * @param options.userKey {UserKey} the account's user key, epoch[0] of the
81
+ * roster and recipient zero of every encrypted collection
82
+ * @param options.idStore {WebvhIdStore} the account's `id` collection
83
+ * store, signing as the same bootstrap did:key
84
+ * @param options.rosterStoreFor {Function} `({ did }) =>
85
+ * EncryptionDescriptorStore` -- the user-key roster's store once the DID is
86
+ * known, with a LADDER-signed `ResourceLogSigner` (the first-entry shape)
87
+ * @param [options.expectedDid] {string} the account DID, when the caller
88
+ * holds a pointer that already names one (a heal re-run); a fresh signup
89
+ * and a fresh-terminal heal legitimately hold none
90
+ * @param [options.accountLogPinStore] {ResourceLogPinStore} the chain-head
91
+ * pins the genesis read and create ride (a transient signup passes the
92
+ * visit's in-memory store)
93
+ * @param [options.onDidPublished] {Function} `({ did }) => Promise<void>`
94
+ * @param [options.promoteController] {boolean} default `true`; an app whose
95
+ * account pointer must durably name the DID first (freewallet's record
96
+ * re-bind) passes `false` and promotes after that write
97
+ * @returns {Promise<AccountGenesisResult>}
98
+ */
99
+ export declare function ensureCredentialAnchoredAccountGenesis({ was, wasServerUrl, spaceId, ladderSeed, keyAgreement, standingRecipient, userKey, idStore, rosterStoreFor, expectedDid, accountLogPinStore, onDidPublished, promoteController }: {
100
+ was: WasClient;
101
+ wasServerUrl: string;
102
+ spaceId: string;
103
+ ladderSeed: Uint8Array;
104
+ keyAgreement: UnlockKeyAgreementPublication;
105
+ standingRecipient: RecipientPublicKey;
106
+ userKey: UserKey;
107
+ idStore: WebvhIdStore;
108
+ rosterStoreFor: (options: {
109
+ did: string;
110
+ }) => EncryptionDescriptorStore;
111
+ expectedDid?: string;
112
+ accountLogPinStore?: ResourceLogPinStore;
113
+ onDidPublished?: (published: {
114
+ did: string;
115
+ }) => Promise<void>;
116
+ promoteController?: boolean;
117
+ }): Promise<AccountGenesisResult>;
118
+ //# sourceMappingURL=credentialAnchoredGenesis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentialAnchoredGenesis.d.ts","sourceRoot":"","sources":["../../src/clientAnnex/credentialAnchoredGenesis.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,EAAwB,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AAIxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAExD,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAA;AAC/E,OAAO,EAGL,KAAK,OAAO,EAEb,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,8BAA8B,CAAA;AAErC;;;;;;;;GAQG;AACH,wBAAsB,mCAAmC,IAAI,OAAO,CAAC;IACnE,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;CAC3B,CAAC,CAED;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,sCAAsC,CAAC,EAC3D,GAAG,EACH,YAAY,EACZ,OAAO,EACP,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,OAAO,EACP,OAAO,EACP,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,iBAAwB,EACzB,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,UAAU,CAAA;IACtB,YAAY,EAAE,6BAA6B,CAAA;IAC3C,iBAAiB,EAAE,kBAAkB,CAAA;IACrC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,YAAY,CAAA;IACrB,cAAc,EAAE,CAAC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,yBAAyB,CAAA;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,mBAAmB,CAAA;IACxC,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA+EhC"}
@@ -0,0 +1,174 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The account-genesis ceremony's CREDENTIAL-ANCHORED variant: such a
6
+ * signup mints no durable client, so the ceremony is anchored on the unlock
7
+ * credential's ladder alone. The stage order mirrors `ensureAccountGenesis`
8
+ * with the durable-client members swapped for their ladder analogs:
9
+ *
10
+ * 1. Space provisioning under the ladder VM's bare did:key -- the bootstrap
11
+ * controller (wallet-core decision 0004, freewallet decision 0002's
12
+ * resolution of the orphan-Space tear): re-derivable from the unlock
13
+ * record's ladder seed, so a tab death here strands nothing a later
14
+ * login cannot finish or unwind.
15
+ * 2. The ladder-anchored did:webvh genesis
16
+ * (`ensureLadderAnchoredDidWebvh`): the entry signed by ladder rung 0, `updateKeys` = [rung 0], `nextKeyHashes`
17
+ * = [hash(rung 0), hash(rung 1)], the ladder VM and the credential's
18
+ * `keyAgreement` posture folded in, `portable` unchanged. There is no KMS
19
+ * stage: the keystore is DEFERRED to the first durable enrollment (a
20
+ * ladder-anchored account has no consumer for the convenience VM, and a
21
+ * keystore under an evaporating identity would orphan).
22
+ * 3. The user-key roster genesis, wrapped to the CREDENTIAL's standing
23
+ * key-agreement key -- the only recipient a ladder-anchored account has
24
+ * -- with the entry proof signed by the ladder VM (the ceremony-tail
25
+ * license's first-entry shape). The account is credential-recoverable
26
+ * from the moment this lands.
27
+ * 4. Epoch[0] on every encrypted roster collection -- gated on the roster
28
+ * stage landing, unlike the durable flow: the user key here exists only
29
+ * in this tab's memory, so installing collection epochs under a key the
30
+ * roster does not deliver would strand the collections on a key that
31
+ * dies with the tab. With the gate, the tear heal is always clean: no
32
+ * roster means no epochs, and a fresh user key re-runs both.
33
+ * 5. Space-controller promotion, last -- every earlier stage ran under the
34
+ * bootstrap did:key the Space's stored controller authorizes.
35
+ *
36
+ * Idempotent end to end on the durable flow's convention; the did:webvh
37
+ * stage adopts a published log iff the ladder attributes it (a naive re-run
38
+ * would mint a different SCID, so adoption IS the torn-signup convergence).
39
+ */
40
+ import { initRecipients } from '@interop/was-client/edv';
41
+ import { provisionWalletSpace } from '../space/index.js';
42
+ import { ladderVmAgent } from './zcap.js';
43
+ import { ensureLadderAnchoredDidWebvh } from './ladderAnchored.js';
44
+ import { ensureWalletSpaceEpochs, mintUserKey } from '../keys/index.js';
45
+ import { AccountGenesisSpaceError, ensurePromotedSpaceController, mintSpaceId } from '../genesis/accountGenesis.js';
46
+ /**
47
+ * The key set a credential-anchored signup mints locally before anything
48
+ * touches the network: the data Space id and the account's user key -- nothing else.
49
+ * No client seed and no client update-key seeds exist (that is the point);
50
+ * the ladder seed is minted by the unlock layer beside the record that
51
+ * carries it, and every other key derives from it.
52
+ *
53
+ * @returns {Promise<{ spaceId: string, userKey: Required<UserKey> }>}
54
+ */
55
+ export async function mintCredentialAnchoredAccountKeySet() {
56
+ return { spaceId: mintSpaceId(), userKey: await mintUserKey() };
57
+ }
58
+ /**
59
+ * Runs the credential-anchored account-genesis ceremony (see the module doc
60
+ * for the stage order and its whys). The caller has already durably written the
61
+ * unlock record carrying the ladder seed -- the transposed
62
+ * persist-before-publish rule: rung 0 must never publish before the seed
63
+ * that derives it is recoverable.
64
+ *
65
+ * @param options {object}
66
+ * @param options.was {WasClient} signing as the ladder VM's bare did:key
67
+ * (the bootstrap controller; build it over `ladderVmAgent`)
68
+ * @param options.wasServerUrl {string}
69
+ * @param options.spaceId {string}
70
+ * @param options.ladderSeed {Uint8Array} the credential's ladder seed,
71
+ * already sealed into the durably written unlock record
72
+ * @param options.keyAgreement {UnlockKeyAgreementPublication} the
73
+ * credential's key-agreement publication (commitment or verbatim), folded
74
+ * into the genesis document
75
+ * @param options.standingRecipient {RecipientPublicKey} the credential's
76
+ * standing key-agreement key as a roster recipient (`id` its roster kid)
77
+ * @param options.userKey {UserKey} the account's user key, epoch[0] of the
78
+ * roster and recipient zero of every encrypted collection
79
+ * @param options.idStore {WebvhIdStore} the account's `id` collection
80
+ * store, signing as the same bootstrap did:key
81
+ * @param options.rosterStoreFor {Function} `({ did }) =>
82
+ * EncryptionDescriptorStore` -- the user-key roster's store once the DID is
83
+ * known, with a LADDER-signed `ResourceLogSigner` (the first-entry shape)
84
+ * @param [options.expectedDid] {string} the account DID, when the caller
85
+ * holds a pointer that already names one (a heal re-run); a fresh signup
86
+ * and a fresh-terminal heal legitimately hold none
87
+ * @param [options.accountLogPinStore] {ResourceLogPinStore} the chain-head
88
+ * pins the genesis read and create ride (a transient signup passes the
89
+ * visit's in-memory store)
90
+ * @param [options.onDidPublished] {Function} `({ did }) => Promise<void>`
91
+ * @param [options.promoteController] {boolean} default `true`; an app whose
92
+ * account pointer must durably name the DID first (freewallet's record
93
+ * re-bind) passes `false` and promotes after that write
94
+ * @returns {Promise<AccountGenesisResult>}
95
+ */
96
+ export async function ensureCredentialAnchoredAccountGenesis({ was, wasServerUrl, spaceId, ladderSeed, keyAgreement, standingRecipient, userKey, idStore, rosterStoreFor, expectedDid, accountLogPinStore, onDidPublished, promoteController = true }) {
97
+ const failed = [];
98
+ const bootstrap = await ladderVmAgent({ ladderSeed });
99
+ // 1. The Space and its collection roster, create-if-absent under the
100
+ // ladder VM's bare did:key. The typed refusal, exactly as on the durable
101
+ // flow: nothing downstream can proceed without a Space.
102
+ try {
103
+ await provisionWalletSpace({ was, spaceId, controllerDid: bootstrap.id });
104
+ }
105
+ catch (err) {
106
+ throw new AccountGenesisSpaceError({ spaceId, cause: err });
107
+ }
108
+ // 2. The ladder-anchored did:webvh genesis -- probe, adopt
109
+ // (ladder-attributed), or create-and-publish. Fatal on failure, like the durable stage.
110
+ const { did } = await ensureLadderAnchoredDidWebvh({
111
+ idStore,
112
+ wasServerUrl,
113
+ spaceId,
114
+ ladderSeed,
115
+ keyAgreement,
116
+ ...(expectedDid !== undefined ? { expectedDid } : {}),
117
+ ...(accountLogPinStore ? { pinStore: accountLogPinStore } : {})
118
+ });
119
+ await onDidPublished?.({ did });
120
+ // 3. The roster genesis: epoch[0] IS the user key, wrapped once, to the
121
+ // credential's standing key-agreement key. The store's ladder-signed
122
+ // genesis append is the ceremony-tail license's first-entry shape.
123
+ let rosterDescriptor;
124
+ try {
125
+ const store = rosterStoreFor({ did });
126
+ const current = await store.read();
127
+ rosterDescriptor =
128
+ current !== null
129
+ ? current.descriptor
130
+ : await initRecipients({
131
+ store,
132
+ recipients: [standingRecipient],
133
+ epoch: { epochId: userKey.id, secret: userKey.secret }
134
+ });
135
+ }
136
+ catch (err) {
137
+ failed.push({ stage: 'roster', error: err });
138
+ }
139
+ // 4. Epoch[0] on every encrypted roster collection, only behind a landed
140
+ // roster (see the module doc: the user key is memory-only here).
141
+ let epochs;
142
+ if (rosterDescriptor) {
143
+ try {
144
+ epochs = await ensureWalletSpaceEpochs({ was, spaceId, userKey });
145
+ }
146
+ catch (err) {
147
+ failed.push({ stage: 'epochs', error: err });
148
+ }
149
+ }
150
+ // 5. The controller promotion, last. The bootstrap client IS the bare
151
+ // did:key client, so it serves the heal branch too.
152
+ let promotion;
153
+ if (promoteController) {
154
+ try {
155
+ promotion = await ensurePromotedSpaceController({
156
+ was,
157
+ wasAsClient: was,
158
+ spaceId,
159
+ did
160
+ });
161
+ }
162
+ catch (err) {
163
+ failed.push({ stage: 'promotion', error: err });
164
+ }
165
+ }
166
+ return {
167
+ did,
168
+ ...(rosterDescriptor ? { rosterDescriptor } : {}),
169
+ ...(epochs ? { epochs } : {}),
170
+ ...(promotion ? { promotion } : {}),
171
+ failed
172
+ };
173
+ }
174
+ //# sourceMappingURL=credentialAnchoredGenesis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentialAnchoredGenesis.js","sourceRoot":"","sources":["../../src/clientAnnex/credentialAnchoredGenesis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The forget ceremony: a remembered browser's durable client removes ITSELF
6
+ * from the account, through the standing unlock credential's bridge, before
7
+ * the app wipes its local state. Self-enrollment in reverse, run with what a
8
+ * live durable session already holds -- the credential's ladder seed and its
9
+ * delegated `did.jsonl` bridge -- and refused for the account's last enrolled
10
+ * durable client ({@link LastDurableClientForgetError}), whose forget is the
11
+ * ladder-anchored transition, a separate ceremony.
12
+ *
13
+ * The stage order INVERTS the revocation cascade's document-edit-first rule,
14
+ * and the inversion is forced twice over: once the removal entry lands, the
15
+ * forgetting client's key is out of `assertionMethod` at the post-edit head,
16
+ * so it can sign no roster append (the entry-proof rule), and its WAS
17
+ * invocations stop verifying (the current-key-set rule), so the collection
18
+ * fan-out cannot run either -- while a ladder-signed append is licensed only
19
+ * at a posture-changing document version, which a not-last-client removal is
20
+ * not. So:
21
+ *
22
+ * 1. **The roster rotation**, PRE-edit, signed by the still-enrolled
23
+ * forgetting client: the user key rotates off this client's wrap, retired
24
+ * by its roster kid explicitly (document convergence cannot drop a client
25
+ * the document still lists). The fresh key is read back through the
26
+ * CREDENTIAL's standing wrap -- the forgetting client just retired its
27
+ * own -- which the rotation fan-out keeps escrowed into every epoch.
28
+ * 2. **The collection fan-out**, still under this client's invocation
29
+ * authority: every encrypted collection re-epochs onto the fresh key, so
30
+ * writes stop landing under epochs a later holder of this browser's
31
+ * residue could open.
32
+ * 3. **The removal entry** ({@link forgetWebvhClient}): one atomic
33
+ * ladder-signed entry through the bridge takes the client's whole document
34
+ * footprint out. The app's local wipe runs after this ceremony returns, so
35
+ * a tear anywhere before the entry reads as "not forgotten" and a re-click
36
+ * resumes.
37
+ *
38
+ * Torn-state map: a run torn after stage 1 leaves the account rotated off a
39
+ * client the document still lists -- writes already land under the fresh key,
40
+ * the client-key record still stands, and the re-run's rotation no-ops (the
41
+ * wrap is already gone) while its later stages finish. Torn after stage 3
42
+ * (the removal published, the app's wipe not run) is the
43
+ * finish-the-wipe state the app's next login maps. What no re-run needs:
44
+ * the roster log's head stays anchored BEFORE the removal entry -- no signer
45
+ * this ceremony holds may append after it -- which is exactly the unsealed
46
+ * state any other enrolled client's login sweep detects and seals.
47
+ *
48
+ * There is deliberately no recovery-delegation or generation-delegation
49
+ * re-mint stage: the forgetting client cannot re-mint with a key that dies at
50
+ * stage 3 (the replacement would rot moments later), so a delegation this
51
+ * client had signed is left to the standing self-heals (a durable login's
52
+ * bridge refresh and `ensureGenerationDelegationCurrent` on the
53
+ * account-document axis).
54
+ *
55
+ * The honest limitation is the cascade's, as everywhere: ciphertext this
56
+ * browser already fetched stays forensically recoverable from its storage,
57
+ * and old epochs stay open to keys it already held. The rotation stops future
58
+ * reads; the wipe removes the local copies.
59
+ */
60
+ import type { IKeyAgreementKey } from '@interop/data-integrity-core';
61
+ import type { CollectionEncryption } from '@interop/was-client';
62
+ import type { EncryptionDescriptorStore } from '@interop/was-client/edv';
63
+ import type { RevokedClientKeys } from '../webvh/revokeClient.js';
64
+ import { type CascadeCollections, type UserKey, type UserKeyCascadeResult } from '../keys/index.js';
65
+ import type { UnlockLogStore } from '../unlock/standingWebvh.js';
66
+ /**
67
+ * What a completed forget reports: whether the roster's wrap for the
68
+ * forgotten client was actually retired on this run (a re-run of a
69
+ * torn-after-rotation ceremony reports `false`), the per-collection fan-out
70
+ * result, the document as the removal entry left it, and -- when the account
71
+ * has a roster -- the rotated key with the roster descriptor it was read
72
+ * from.
73
+ */
74
+ export interface DurableClientForgetResult {
75
+ rotated: boolean;
76
+ collections: UserKeyCascadeResult;
77
+ did: string;
78
+ document: object;
79
+ userKey?: UserKey;
80
+ rosterDescriptor?: CollectionEncryption;
81
+ }
82
+ /**
83
+ * Forgets one enrolled durable client -- this browser's own -- from its
84
+ * account. See the module doc for the stage order (rotation and fan-out
85
+ * BEFORE the removal entry, the self-forget inversion) and the torn-state
86
+ * map. The caller runs the local wipe only after this resolves.
87
+ *
88
+ * @param options {object}
89
+ * @param options.logStore {UnlockLogStore} the credential's delegated
90
+ * `did.jsonl` bridge store; also serves the ceremony's public pre-edit
91
+ * read. The caller is expected to have verified the account log under its
92
+ * own chain-head pins in the same session (the ceremony re-reads through
93
+ * the seam and checks `expectedDid`, but carries no pin store itself)
94
+ * @param options.ladderSeed {Uint8Array} the credential's ladder seed
95
+ * @param options.forgottenClient {RevokedClientKeys} this client's public
96
+ * halves; an `updateKeyMultibase` the log does not authorize (stale, or the
97
+ * staged key) is re-derived from the log
98
+ * @param options.forgottenKeyAgreementKeyMultibase {string} this client's
99
+ * identity key-agreement key (the X25519 twin), naming its roster wrap
100
+ * @param [options.knownLatentHashes] {string[]} standing latent commitments
101
+ * the caller vouches for (the recovery registry's update-key hashes),
102
+ * excluded from the staged-hash attribution
103
+ * @param options.expectedDid {string} the account DID from the caller's
104
+ * stored account pointer
105
+ * @param options.rosterStore {EncryptionDescriptorStore} the
106
+ * `key-map/user-key.jsonl` roster store, signing as the forgetting client
107
+ * @param options.credentialKeyAgreementKey {IKeyAgreementKey} the standing
108
+ * credential's key-agreement key -- the recipient whose wrap survives the
109
+ * rotation, reading the fresh key back and unwrapping the generations for
110
+ * the fan-out
111
+ * @param [options.userKey] {UserKey} this client's cached user key
112
+ * @param [options.pinnedEpochId] {string} the locally pinned latest-seen
113
+ * roster epoch
114
+ * @param [options.onUserKeyAdopted] {Function} persists a rotated key:
115
+ * called with `{ userKey, latestEpochId, descriptor }` after the roster
116
+ * read and BEFORE the fan-out
117
+ * @param options.collections {CascadeCollections} the fan-out's work
118
+ * @returns {Promise<DurableClientForgetResult>}
119
+ */
120
+ export declare function forgetDurableClient({ logStore, ladderSeed, forgottenClient, forgottenKeyAgreementKeyMultibase, knownLatentHashes, expectedDid, rosterStore, credentialKeyAgreementKey, userKey, pinnedEpochId, onUserKeyAdopted, collections }: {
121
+ logStore: UnlockLogStore;
122
+ ladderSeed: Uint8Array;
123
+ forgottenClient: RevokedClientKeys;
124
+ forgottenKeyAgreementKeyMultibase: string;
125
+ knownLatentHashes?: string[];
126
+ expectedDid: string;
127
+ rosterStore: EncryptionDescriptorStore;
128
+ credentialKeyAgreementKey: IKeyAgreementKey;
129
+ userKey?: UserKey;
130
+ pinnedEpochId?: string | null;
131
+ onUserKeyAdopted?: (adopted: {
132
+ userKey: UserKey;
133
+ latestEpochId: string;
134
+ descriptor: CollectionEncryption;
135
+ }) => Promise<void>;
136
+ collections: CascadeCollections;
137
+ }): Promise<DurableClientForgetResult>;
138
+ //# sourceMappingURL=forget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forget.d.ts","sourceRoot":"","sources":["../../src/clientAnnex/forget.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AAGxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAKL,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,oBAAoB,EAC1B,MAAM,kBAAkB,CAAA;AAKzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAEhE;;;;;;;GAOG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,oBAAoB,CAAA;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gBAAgB,CAAC,EAAE,oBAAoB,CAAA;CACxC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,QAAQ,EACR,UAAU,EACV,eAAe,EACf,iCAAiC,EACjC,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,yBAAyB,EACzB,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,WAAW,EACZ,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;IACtB,eAAe,EAAE,iBAAiB,CAAA;IAClC,iCAAiC,EAAE,MAAM,CAAA;IACzC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,yBAAyB,CAAA;IACtC,yBAAyB,EAAE,gBAAgB,CAAA;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC3B,OAAO,EAAE,OAAO,CAAA;QAChB,aAAa,EAAE,MAAM,CAAA;QACrB,UAAU,EAAE,oBAAoB,CAAA;KACjC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACnB,WAAW,EAAE,kBAAkB,CAAA;CAChC,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAoGrC"}
@@ -0,0 +1,133 @@
1
+ import { readPublishedLog, relationIds } from '../webvh/didWebvh.js';
2
+ import { cascadeCollectionsToUserKey, readUserKeyRoster, rosterRecipientKid, rotateUserKeyRoster } from '../keys/index.js';
3
+ import { forgetWebvhClient, LastDurableClientForgetError } from './ladderAnchored.js';
4
+ /**
5
+ * Forgets one enrolled durable client -- this browser's own -- from its
6
+ * account. See the module doc for the stage order (rotation and fan-out
7
+ * BEFORE the removal entry, the self-forget inversion) and the torn-state
8
+ * map. The caller runs the local wipe only after this resolves.
9
+ *
10
+ * @param options {object}
11
+ * @param options.logStore {UnlockLogStore} the credential's delegated
12
+ * `did.jsonl` bridge store; also serves the ceremony's public pre-edit
13
+ * read. The caller is expected to have verified the account log under its
14
+ * own chain-head pins in the same session (the ceremony re-reads through
15
+ * the seam and checks `expectedDid`, but carries no pin store itself)
16
+ * @param options.ladderSeed {Uint8Array} the credential's ladder seed
17
+ * @param options.forgottenClient {RevokedClientKeys} this client's public
18
+ * halves; an `updateKeyMultibase` the log does not authorize (stale, or the
19
+ * staged key) is re-derived from the log
20
+ * @param options.forgottenKeyAgreementKeyMultibase {string} this client's
21
+ * identity key-agreement key (the X25519 twin), naming its roster wrap
22
+ * @param [options.knownLatentHashes] {string[]} standing latent commitments
23
+ * the caller vouches for (the recovery registry's update-key hashes),
24
+ * excluded from the staged-hash attribution
25
+ * @param options.expectedDid {string} the account DID from the caller's
26
+ * stored account pointer
27
+ * @param options.rosterStore {EncryptionDescriptorStore} the
28
+ * `key-map/user-key.jsonl` roster store, signing as the forgetting client
29
+ * @param options.credentialKeyAgreementKey {IKeyAgreementKey} the standing
30
+ * credential's key-agreement key -- the recipient whose wrap survives the
31
+ * rotation, reading the fresh key back and unwrapping the generations for
32
+ * the fan-out
33
+ * @param [options.userKey] {UserKey} this client's cached user key
34
+ * @param [options.pinnedEpochId] {string} the locally pinned latest-seen
35
+ * roster epoch
36
+ * @param [options.onUserKeyAdopted] {Function} persists a rotated key:
37
+ * called with `{ userKey, latestEpochId, descriptor }` after the roster
38
+ * read and BEFORE the fan-out
39
+ * @param options.collections {CascadeCollections} the fan-out's work
40
+ * @returns {Promise<DurableClientForgetResult>}
41
+ */
42
+ export async function forgetDurableClient({ logStore, ladderSeed, forgottenClient, forgottenKeyAgreementKeyMultibase, knownLatentHashes, expectedDid, rosterStore, credentialKeyAgreementKey, userKey, pinnedEpochId, onUserKeyAdopted, collections }) {
43
+ // The pre-edit read, doing double duty: the last-client refusal must fire
44
+ // BEFORE the rotation (or a refused forget would already have retired this
45
+ // client's wrap), and the rotation's recipient resolver needs the pre-edit
46
+ // document (which still keys every other recipient).
47
+ const published = await readPublishedLog({
48
+ // readPublishedLog only calls getIdResourceRaw, so the narrow seam is
49
+ // safe.
50
+ idStore: logStore,
51
+ expectedDid
52
+ });
53
+ if (!published) {
54
+ throw new Error('did:webvh: did.jsonl is missing; nothing to forget from.');
55
+ }
56
+ const signingVmId = `${published.did}#${forgottenClient.signingKeyMultibase}`;
57
+ const invocationIds = relationIds(published.doc.capabilityInvocation);
58
+ if (invocationIds.includes(signingVmId) &&
59
+ invocationIds.every(id => id === signingVmId)) {
60
+ throw new LastDurableClientForgetError();
61
+ }
62
+ // Stages 1 and 2: the roster rotation off this client's wrap and the
63
+ // collection fan-out, both under this client's still-standing authority.
64
+ let rotated = false;
65
+ let read;
66
+ let cascade = { outcomes: {}, failed: [] };
67
+ const current = await rosterStore.read();
68
+ if (current !== null) {
69
+ const forgottenKid = rosterRecipientKid({
70
+ signingKeyMultibase: forgottenClient.signingKeyMultibase,
71
+ keyAgreementKeyMultibase: forgottenKeyAgreementKeyMultibase
72
+ });
73
+ let descriptor = current.descriptor;
74
+ const currentEpoch = (descriptor.epochs ?? []).find(epoch => epoch.id === descriptor.currentEpoch);
75
+ const wrapped = currentEpoch?.recipients.some(entry => entry.header.kid === forgottenKid);
76
+ if (wrapped) {
77
+ descriptor = await rotateUserKeyRoster({
78
+ store: rosterStore,
79
+ document: published.doc,
80
+ retireRecipientId: forgottenKid
81
+ });
82
+ rotated = true;
83
+ }
84
+ // The fresh key comes back through the credential's standing wrap -- this
85
+ // client's own is gone from the current epoch -- with the continuity and
86
+ // possession checks still running on the threaded descriptor.
87
+ const adopted = await readUserKeyRoster({
88
+ store: rosterStore,
89
+ descriptor,
90
+ ...(userKey ? { userKey } : {}),
91
+ clientKeyAgreementKey: credentialKeyAgreementKey,
92
+ pinnedEpochId
93
+ });
94
+ if (adopted.rotated) {
95
+ await onUserKeyAdopted?.({
96
+ userKey: adopted.userKey,
97
+ latestEpochId: adopted.latestEpochId,
98
+ descriptor: adopted.descriptor
99
+ });
100
+ }
101
+ read = { userKey: adopted.userKey, descriptor: adopted.descriptor };
102
+ cascade = await cascadeCollectionsToUserKey({
103
+ collectionIds: typeof collections.collectionIds === 'function'
104
+ ? await collections.collectionIds()
105
+ : collections.collectionIds,
106
+ storeFor: collections.storeFor,
107
+ ...(collections.isEncrypted
108
+ ? { isEncrypted: collections.isEncrypted }
109
+ : {}),
110
+ rosterDescriptor: adopted.descriptor,
111
+ clientKeyAgreementKey: credentialKeyAgreementKey,
112
+ userKey: adopted.userKey
113
+ });
114
+ }
115
+ // Stage 3: the atomic ladder-signed removal entry, through the bridge.
116
+ const removed = await forgetWebvhClient({
117
+ store: logStore,
118
+ ladderSeed,
119
+ forgottenClient,
120
+ ...(knownLatentHashes ? { knownLatentHashes } : {}),
121
+ expectedDid
122
+ });
123
+ return {
124
+ rotated,
125
+ collections: cascade,
126
+ did: removed.did,
127
+ document: removed.doc,
128
+ ...(read
129
+ ? { userKey: read.userKey, rosterDescriptor: read.descriptor }
130
+ : {})
131
+ };
132
+ }
133
+ //# sourceMappingURL=forget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forget.js","sourceRoot":"","sources":["../../src/clientAnnex/forget.ts"],"names":[],"mappings":"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"}