@interop/wallet-core 0.61.0 → 0.65.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 (313) hide show
  1. package/README.md +41 -16
  2. package/dist/clientAnnex/credentialAnchoredGenesis.d.ts +46 -26
  3. package/dist/clientAnnex/credentialAnchoredGenesis.d.ts.map +1 -1
  4. package/dist/clientAnnex/credentialAnchoredGenesis.js +91 -39
  5. package/dist/clientAnnex/credentialAnchoredGenesis.js.map +1 -1
  6. package/dist/clientAnnex/establish.d.ts +55 -34
  7. package/dist/clientAnnex/establish.d.ts.map +1 -1
  8. package/dist/clientAnnex/establish.js +102 -56
  9. package/dist/clientAnnex/establish.js.map +1 -1
  10. package/dist/clientAnnex/forget.d.ts +16 -2
  11. package/dist/clientAnnex/forget.d.ts.map +1 -1
  12. package/dist/clientAnnex/forget.js +11 -9
  13. package/dist/clientAnnex/forget.js.map +1 -1
  14. package/dist/clientAnnex/forgetLast.d.ts +126 -35
  15. package/dist/clientAnnex/forgetLast.d.ts.map +1 -1
  16. package/dist/clientAnnex/forgetLast.js +208 -60
  17. package/dist/clientAnnex/forgetLast.js.map +1 -1
  18. package/dist/clientAnnex/gc.d.ts +3 -2
  19. package/dist/clientAnnex/gc.d.ts.map +1 -1
  20. package/dist/clientAnnex/gc.js +7 -7
  21. package/dist/clientAnnex/gc.js.map +1 -1
  22. package/dist/clientAnnex/heal.d.ts +101 -33
  23. package/dist/clientAnnex/heal.d.ts.map +1 -1
  24. package/dist/clientAnnex/heal.js +559 -212
  25. package/dist/clientAnnex/heal.js.map +1 -1
  26. package/dist/clientAnnex/index.d.ts +21 -5
  27. package/dist/clientAnnex/index.d.ts.map +1 -1
  28. package/dist/clientAnnex/index.js +24 -5
  29. package/dist/clientAnnex/index.js.map +1 -1
  30. package/dist/clientAnnex/ladder.d.ts +304 -18
  31. package/dist/clientAnnex/ladder.d.ts.map +1 -1
  32. package/dist/clientAnnex/ladder.js +942 -64
  33. package/dist/clientAnnex/ladder.js.map +1 -1
  34. package/dist/clientAnnex/ladderAnchored.d.ts +261 -28
  35. package/dist/clientAnnex/ladderAnchored.d.ts.map +1 -1
  36. package/dist/clientAnnex/ladderAnchored.js +521 -295
  37. package/dist/clientAnnex/ladderAnchored.js.map +1 -1
  38. package/dist/clientAnnex/log.d.ts +193 -38
  39. package/dist/clientAnnex/log.d.ts.map +1 -1
  40. package/dist/clientAnnex/log.js +445 -190
  41. package/dist/clientAnnex/log.js.map +1 -1
  42. package/dist/clientAnnex/mend.d.ts +12 -6
  43. package/dist/clientAnnex/mend.d.ts.map +1 -1
  44. package/dist/clientAnnex/mend.js +26 -12
  45. package/dist/clientAnnex/mend.js.map +1 -1
  46. package/dist/clientAnnex/recoveryLadderAnchored.d.ts +38 -11
  47. package/dist/clientAnnex/recoveryLadderAnchored.d.ts.map +1 -1
  48. package/dist/clientAnnex/recoveryLadderAnchored.js +177 -55
  49. package/dist/clientAnnex/recoveryLadderAnchored.js.map +1 -1
  50. package/dist/clientAnnex/spaceCapability.d.ts +123 -0
  51. package/dist/clientAnnex/spaceCapability.d.ts.map +1 -0
  52. package/dist/clientAnnex/spaceCapability.js +152 -0
  53. package/dist/clientAnnex/spaceCapability.js.map +1 -0
  54. package/dist/clientAnnex/stages.d.ts +63 -0
  55. package/dist/clientAnnex/stages.d.ts.map +1 -0
  56. package/dist/clientAnnex/stages.js +64 -0
  57. package/dist/clientAnnex/stages.js.map +1 -0
  58. package/dist/clientAnnex/zcap.d.ts +1 -1
  59. package/dist/clientAnnex/zcap.d.ts.map +1 -1
  60. package/dist/clientAnnex/zcap.js +42 -35
  61. package/dist/clientAnnex/zcap.js.map +1 -1
  62. package/dist/clients/policy.d.ts +15 -1
  63. package/dist/clients/policy.d.ts.map +1 -1
  64. package/dist/clients/policy.js +12 -6
  65. package/dist/clients/policy.js.map +1 -1
  66. package/dist/clients/revocation.d.ts +18 -10
  67. package/dist/clients/revocation.d.ts.map +1 -1
  68. package/dist/clients/revocation.js +13 -5
  69. package/dist/clients/revocation.js.map +1 -1
  70. package/dist/clients/rosterPolicy.d.ts +10 -2
  71. package/dist/clients/rosterPolicy.d.ts.map +1 -1
  72. package/dist/clients/rosterPolicy.js +41 -24
  73. package/dist/clients/rosterPolicy.js.map +1 -1
  74. package/dist/descriptors/acquire.d.ts.map +1 -1
  75. package/dist/descriptors/acquire.js +6 -23
  76. package/dist/descriptors/acquire.js.map +1 -1
  77. package/dist/descriptors/cipher.d.ts.map +1 -1
  78. package/dist/descriptors/cipher.js +5 -0
  79. package/dist/descriptors/cipher.js.map +1 -1
  80. package/dist/descriptors/errors.d.ts +43 -0
  81. package/dist/descriptors/errors.d.ts.map +1 -0
  82. package/dist/descriptors/errors.js +45 -0
  83. package/dist/descriptors/errors.js.map +1 -0
  84. package/dist/descriptors/index.d.ts +5 -0
  85. package/dist/descriptors/index.d.ts.map +1 -1
  86. package/dist/descriptors/index.js +5 -0
  87. package/dist/descriptors/index.js.map +1 -1
  88. package/dist/enrollment/enrollment.d.ts +37 -10
  89. package/dist/enrollment/enrollment.d.ts.map +1 -1
  90. package/dist/enrollment/enrollment.js +52 -15
  91. package/dist/enrollment/enrollment.js.map +1 -1
  92. package/dist/genesis/accountGenesis.d.ts +42 -11
  93. package/dist/genesis/accountGenesis.d.ts.map +1 -1
  94. package/dist/genesis/accountGenesis.js +80 -22
  95. package/dist/genesis/accountGenesis.js.map +1 -1
  96. package/dist/genesis/index.d.ts +3 -1
  97. package/dist/genesis/index.d.ts.map +1 -1
  98. package/dist/genesis/index.js +3 -1
  99. package/dist/genesis/index.js.map +1 -1
  100. package/dist/identity/agents.d.ts +17 -1
  101. package/dist/identity/agents.d.ts.map +1 -1
  102. package/dist/identity/agents.js +24 -8
  103. package/dist/identity/agents.js.map +1 -1
  104. package/dist/identity/index.d.ts +3 -1
  105. package/dist/identity/index.d.ts.map +1 -1
  106. package/dist/identity/index.js +3 -1
  107. package/dist/identity/index.js.map +1 -1
  108. package/dist/index.d.ts +6 -3
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +6 -3
  111. package/dist/index.js.map +1 -1
  112. package/dist/keyring/index.d.ts +2 -1
  113. package/dist/keyring/index.d.ts.map +1 -1
  114. package/dist/keyring/index.js +2 -1
  115. package/dist/keyring/index.js.map +1 -1
  116. package/dist/keyring/record.d.ts.map +1 -1
  117. package/dist/keyring/record.js +3 -6
  118. package/dist/keyring/record.js.map +1 -1
  119. package/dist/keyring/unlockSpace.d.ts +14 -5
  120. package/dist/keyring/unlockSpace.d.ts.map +1 -1
  121. package/dist/keyring/unlockSpace.js +31 -36
  122. package/dist/keyring/unlockSpace.js.map +1 -1
  123. package/dist/keys/index.d.ts +10 -6
  124. package/dist/keys/index.d.ts.map +1 -1
  125. package/dist/keys/index.js +9 -5
  126. package/dist/keys/index.js.map +1 -1
  127. package/dist/keys/rosterLogStore.d.ts +12 -11
  128. package/dist/keys/rosterLogStore.d.ts.map +1 -1
  129. package/dist/keys/rosterLogStore.js +15 -13
  130. package/dist/keys/rosterLogStore.js.map +1 -1
  131. package/dist/keys/rosterStore.d.ts +3 -6
  132. package/dist/keys/rosterStore.d.ts.map +1 -1
  133. package/dist/keys/rosterStore.js +10 -9
  134. package/dist/keys/rosterStore.js.map +1 -1
  135. package/dist/keys/spaceEpochs.d.ts.map +1 -1
  136. package/dist/keys/spaceEpochs.js +2 -3
  137. package/dist/keys/spaceEpochs.js.map +1 -1
  138. package/dist/keys/userKeyRoster.d.ts +89 -9
  139. package/dist/keys/userKeyRoster.d.ts.map +1 -1
  140. package/dist/keys/userKeyRoster.js +262 -19
  141. package/dist/keys/userKeyRoster.js.map +1 -1
  142. package/dist/keys/userKeyRosterCascade.d.ts +7 -6
  143. package/dist/keys/userKeyRosterCascade.d.ts.map +1 -1
  144. package/dist/keys/userKeyRosterCascade.js +7 -6
  145. package/dist/keys/userKeyRosterCascade.js.map +1 -1
  146. package/dist/keys/wasLabelsStore.d.ts +9 -2
  147. package/dist/keys/wasLabelsStore.d.ts.map +1 -1
  148. package/dist/keys/wasLabelsStore.js +14 -6
  149. package/dist/keys/wasLabelsStore.js.map +1 -1
  150. package/dist/log.d.ts +7 -2
  151. package/dist/log.d.ts.map +1 -1
  152. package/dist/log.js +6 -1
  153. package/dist/log.js.map +1 -1
  154. package/dist/recovery/index.d.ts +12 -8
  155. package/dist/recovery/index.d.ts.map +1 -1
  156. package/dist/recovery/index.js +11 -7
  157. package/dist/recovery/index.js.map +1 -1
  158. package/dist/recovery/recoveryCode.d.ts +27 -7
  159. package/dist/recovery/recoveryCode.d.ts.map +1 -1
  160. package/dist/recovery/recoveryCode.js +18 -6
  161. package/dist/recovery/recoveryCode.js.map +1 -1
  162. package/dist/recovery/recoveryDelegation.d.ts +4 -1
  163. package/dist/recovery/recoveryDelegation.d.ts.map +1 -1
  164. package/dist/recovery/recoveryDelegation.js +25 -36
  165. package/dist/recovery/recoveryDelegation.js.map +1 -1
  166. package/dist/recovery/recoveryWebvh.d.ts +117 -65
  167. package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
  168. package/dist/recovery/recoveryWebvh.js +279 -126
  169. package/dist/recovery/recoveryWebvh.js.map +1 -1
  170. package/dist/request/classify.d.ts +32 -8
  171. package/dist/request/classify.d.ts.map +1 -1
  172. package/dist/request/classify.js +39 -14
  173. package/dist/request/classify.js.map +1 -1
  174. package/dist/request/ephemeralExchange.d.ts +1 -6
  175. package/dist/request/ephemeralExchange.d.ts.map +1 -1
  176. package/dist/request/ephemeralExchange.js.map +1 -1
  177. package/dist/request/onboarding.d.ts.map +1 -1
  178. package/dist/request/onboarding.js +2 -2
  179. package/dist/request/onboarding.js.map +1 -1
  180. package/dist/request/parse.d.ts.map +1 -1
  181. package/dist/request/parse.js +2 -3
  182. package/dist/request/parse.js.map +1 -1
  183. package/dist/resourceLog/controller.d.ts +34 -12
  184. package/dist/resourceLog/controller.d.ts.map +1 -1
  185. package/dist/resourceLog/controller.js +79 -86
  186. package/dist/resourceLog/controller.js.map +1 -1
  187. package/dist/resourceLog/document.d.ts +182 -0
  188. package/dist/resourceLog/document.d.ts.map +1 -0
  189. package/dist/resourceLog/document.js +159 -0
  190. package/dist/resourceLog/document.js.map +1 -0
  191. package/dist/resourceLog/errors.d.ts +47 -8
  192. package/dist/resourceLog/errors.d.ts.map +1 -1
  193. package/dist/resourceLog/errors.js +54 -8
  194. package/dist/resourceLog/errors.js.map +1 -1
  195. package/dist/resourceLog/index.d.ts +7 -3
  196. package/dist/resourceLog/index.d.ts.map +1 -1
  197. package/dist/resourceLog/index.js +7 -3
  198. package/dist/resourceLog/index.js.map +1 -1
  199. package/dist/resourceLog/ladderRungs.d.ts +35 -0
  200. package/dist/resourceLog/ladderRungs.d.ts.map +1 -0
  201. package/dist/resourceLog/ladderRungs.js +352 -0
  202. package/dist/resourceLog/ladderRungs.js.map +1 -0
  203. package/dist/resourceLog/license.d.ts +42 -17
  204. package/dist/resourceLog/license.d.ts.map +1 -1
  205. package/dist/resourceLog/license.js +38 -24
  206. package/dist/resourceLog/license.js.map +1 -1
  207. package/dist/space/activity.d.ts +15 -15
  208. package/dist/space/activity.d.ts.map +1 -1
  209. package/dist/space/activity.js +15 -15
  210. package/dist/space/activity.js.map +1 -1
  211. package/dist/space/collections.d.ts +11 -0
  212. package/dist/space/collections.d.ts.map +1 -1
  213. package/dist/space/collections.js +13 -0
  214. package/dist/space/collections.js.map +1 -1
  215. package/dist/space/deleteSpace.d.ts +28 -0
  216. package/dist/space/deleteSpace.d.ts.map +1 -0
  217. package/dist/space/deleteSpace.js +44 -0
  218. package/dist/space/deleteSpace.js.map +1 -0
  219. package/dist/space/errors.d.ts.map +1 -1
  220. package/dist/space/errors.js +0 -1
  221. package/dist/space/errors.js.map +1 -1
  222. package/dist/space/index.d.ts +6 -0
  223. package/dist/space/index.d.ts.map +1 -1
  224. package/dist/space/index.js +6 -0
  225. package/dist/space/index.js.map +1 -1
  226. package/dist/space/plaintextCollection.d.ts +43 -0
  227. package/dist/space/plaintextCollection.d.ts.map +1 -0
  228. package/dist/space/plaintextCollection.js +17 -0
  229. package/dist/space/plaintextCollection.js.map +1 -0
  230. package/dist/stages.d.ts +23 -0
  231. package/dist/stages.d.ts.map +1 -0
  232. package/dist/stages.js +23 -0
  233. package/dist/stages.js.map +1 -0
  234. package/dist/sync/index.d.ts +7 -0
  235. package/dist/sync/index.d.ts.map +1 -1
  236. package/dist/sync/index.js +7 -0
  237. package/dist/sync/index.js.map +1 -1
  238. package/dist/sync/push.js +4 -4
  239. package/dist/sync/push.js.map +1 -1
  240. package/dist/sync/remint.js +2 -2
  241. package/dist/sync/remint.js.map +1 -1
  242. package/dist/sync/types.d.ts +41 -1
  243. package/dist/sync/types.d.ts.map +1 -1
  244. package/dist/sync/types.js +47 -1
  245. package/dist/sync/types.js.map +1 -1
  246. package/dist/unlock/index.d.ts +6 -2
  247. package/dist/unlock/index.d.ts.map +1 -1
  248. package/dist/unlock/index.js +5 -1
  249. package/dist/unlock/index.js.map +1 -1
  250. package/dist/unlock/retire.d.ts +95 -14
  251. package/dist/unlock/retire.d.ts.map +1 -1
  252. package/dist/unlock/retire.js +102 -10
  253. package/dist/unlock/retire.js.map +1 -1
  254. package/dist/unlock/standingClient.d.ts.map +1 -1
  255. package/dist/unlock/standingClient.js +5 -1
  256. package/dist/unlock/standingClient.js.map +1 -1
  257. package/dist/unlock/standingWebvh.d.ts +341 -49
  258. package/dist/unlock/standingWebvh.d.ts.map +1 -1
  259. package/dist/unlock/standingWebvh.js +608 -164
  260. package/dist/unlock/standingWebvh.js.map +1 -1
  261. package/dist/webvh/accountEntry.d.ts +146 -0
  262. package/dist/webvh/accountEntry.d.ts.map +1 -0
  263. package/dist/webvh/accountEntry.js +239 -0
  264. package/dist/webvh/accountEntry.js.map +1 -0
  265. package/dist/webvh/didWeb.d.ts +12 -7
  266. package/dist/webvh/didWeb.d.ts.map +1 -1
  267. package/dist/webvh/didWeb.js +2 -2
  268. package/dist/webvh/didWeb.js.map +1 -1
  269. package/dist/webvh/didWebProjection.d.ts +164 -0
  270. package/dist/webvh/didWebProjection.d.ts.map +1 -0
  271. package/dist/webvh/didWebProjection.js +230 -0
  272. package/dist/webvh/didWebProjection.js.map +1 -0
  273. package/dist/webvh/didWebvh.d.ts +278 -138
  274. package/dist/webvh/didWebvh.d.ts.map +1 -1
  275. package/dist/webvh/didWebvh.js +314 -345
  276. package/dist/webvh/didWebvh.js.map +1 -1
  277. package/dist/webvh/enrollClient.d.ts +65 -0
  278. package/dist/webvh/enrollClient.d.ts.map +1 -0
  279. package/dist/webvh/enrollClient.js +172 -0
  280. package/dist/webvh/enrollClient.js.map +1 -0
  281. package/dist/webvh/index.d.ts +37 -12
  282. package/dist/webvh/index.d.ts.map +1 -1
  283. package/dist/webvh/index.js +33 -10
  284. package/dist/webvh/index.js.map +1 -1
  285. package/dist/webvh/listClients.d.ts +1 -33
  286. package/dist/webvh/listClients.d.ts.map +1 -1
  287. package/dist/webvh/listClients.js +2 -28
  288. package/dist/webvh/listClients.js.map +1 -1
  289. package/dist/webvh/revokeClient.d.ts +89 -11
  290. package/dist/webvh/revokeClient.d.ts.map +1 -1
  291. package/dist/webvh/revokeClient.js +182 -47
  292. package/dist/webvh/revokeClient.js.map +1 -1
  293. package/dist/webvh/standingZcap.d.ts +61 -14
  294. package/dist/webvh/standingZcap.d.ts.map +1 -1
  295. package/dist/webvh/standingZcap.js +91 -0
  296. package/dist/webvh/standingZcap.js.map +1 -1
  297. package/dist/webvh/verifyLog.d.ts +59 -5
  298. package/dist/webvh/verifyLog.d.ts.map +1 -1
  299. package/dist/webvh/verifyLog.js +76 -11
  300. package/dist/webvh/verifyLog.js.map +1 -1
  301. package/dist/webvh/wasIdStore.d.ts +8 -8
  302. package/dist/webvh/wasIdStore.d.ts.map +1 -1
  303. package/dist/webvh/wasIdStore.js +39 -14
  304. package/dist/webvh/wasIdStore.js.map +1 -1
  305. package/dist/webvh/zcap.d.ts +14 -1
  306. package/dist/webvh/zcap.d.ts.map +1 -1
  307. package/dist/webvh/zcap.js +3 -27
  308. package/dist/webvh/zcap.js.map +1 -1
  309. package/package.json +5 -5
  310. package/dist/webvh/keyAgreement.d.ts +0 -72
  311. package/dist/webvh/keyAgreement.d.ts.map +0 -1
  312. package/dist/webvh/keyAgreement.js +0 -47
  313. package/dist/webvh/keyAgreement.js.map +0 -1
@@ -1,12 +1,30 @@
1
1
  import { WasClient } from '@interop/was-client';
2
- import { effectiveParameters, readPublishedLog } from '../webvh/didWebvh.js';
3
- import { delegationKeyInDocument, ladderVmIds } from '../webvh/listClients.js';
4
- import { delegationProofKeyId, zcapExpiring } from '../webvh/standingZcap.js';
2
+ import { spacePath } from '@interop/was-client/paths';
3
+ import { currentLogParameters, readPublishedLog, readPublishedLogOrThrow, WebvhLogConflictError, withLogConflictRetry } from '../webvh/didWebvh.js';
4
+ import { ladderVmIds } from '../resourceLog/document.js';
5
+ import { standingZcapStale } from '../webvh/standingZcap.js';
6
+ import { delegateLogWrite } from '../recovery/recoveryDelegation.js';
5
7
  import { accountLogPinId } from '../webvh/verifyLog.js';
6
8
  import { mintSpaceId } from '../genesis/accountGenesis.js';
7
9
  import { attributeLadderRung, ladderRung, ladderVmKeyMultibase } from './ladder.js';
10
+ import { revealLadderRungWebvh } from './ladderAnchored.js';
8
11
  import { ladderVmAgent, ladderVmZcapClient } from './zcap.js';
9
- import { clientAnnexDidParts, clientAnnexLogPinId, clientAnnexLogStore, delegatedClientsDelegationSpaceId, delegatedClientsPointer, ensureGenerationDelegationCurrent, mintCredentialClientAnnexGeneration, mintDelegatedClientsDelegation, mintGenerationDelegation, setDelegatedClientsPointer } from './log.js';
12
+ import { mintSpaceRootVerbCapability } from './spaceCapability.js';
13
+ import { clientAnnexDidParts, clientAnnexLogPinId, clientAnnexLogStore, delegatedClientsDelegationSpaceId, delegatedClientsPointer, ensureClientAnnexSpace, ensureGenerationDelegationCurrent, mintCredentialClientAnnexGeneration, mintDelegatedClientsDelegation, mintGenerationDelegation, setDelegatedClientsPointerOnce } from './log.js';
14
+ /**
15
+ * The HTTP status a raw signed request's rejection carries, when it carries
16
+ * one. `WasClient.request` applies no error mapping, so the status is all a
17
+ * caller has to dispatch on, and different transports hang it in different
18
+ * places (`status`, or `response.status`).
19
+ *
20
+ * @param err {unknown}
21
+ * @returns {number | undefined}
22
+ */
23
+ function rawRequestStatus(err) {
24
+ const raw = err;
25
+ const status = raw?.status ?? raw?.response?.status;
26
+ return typeof status === 'number' ? status : undefined;
27
+ }
10
28
  /**
11
29
  * The typed refusal of {@link ensureCredentialClientAnnexGeneration}: the
12
30
  * account is not in a shape this visit can mend, and nothing was written.
@@ -81,13 +99,18 @@ export function ladderSignedGenerationDelegationMinter({ accountDid, ladderSeed,
81
99
  * -- the storage client for the fresh-Space arm, signing as the ladder
82
100
  * VM's bare did:key (the caller wires the transport; the agent is derived
83
101
  * here from the ladder seed)
84
- * @param options.idStore {WebvhIdStore} the ACCOUNT log's store (a
85
- * bridge-delegated one suffices: pointer entries publish with
86
- * `logOnly: true`)
102
+ * @param options.delegation {IZcap} the record's bridge delegation (PUT on
103
+ * the account's `did.jsonl`), renewed here when it is stale
104
+ * @param options.idStoreFor {Function}
105
+ * `({ delegation }) => WebvhIdStore` -- builds the ACCOUNT log's store over
106
+ * the usable bridge (a bridge-delegated store suffices: pointer entries
107
+ * publish with `logOnly: true`). Called once, after the bridge renewal, so
108
+ * a pointer entry never rides a lapsed delegation
87
109
  * @param options.onRebindRecord {Function}
88
- * `({ delegatedClients }) => Promise<void>` -- REQUIRED: re-seals the
89
- * unlock record with the fresh sibling delegation; called whenever one was
90
- * minted, after the generation and pointer are durable
110
+ * `({ delegation, delegatedClients }) => Promise<void>` -- REQUIRED:
111
+ * re-seals the unlock record with the usable bridge and sibling
112
+ * delegations; called whenever either was freshly minted, after the
113
+ * generation and pointer are durable
91
114
  * @param [options.delegatedClients] {IZcap} the record's sibling
92
115
  * delegation, when the record carries one
93
116
  * @param [options.pinStore] {ResourceLogPinStore} chain-head pins (a
@@ -101,7 +124,8 @@ export function ensureCredentialClientAnnexGeneration(options) {
101
124
  // into the record would strand the very credential this ensure serves.
102
125
  if (typeof options.onRebindRecord !== 'function') {
103
126
  throw new TypeError('ensureCredentialClientAnnexGeneration requires onRebindRecord: a ' +
104
- 'fresh sibling delegation must be re-sealed into the unlock record.');
127
+ 'fresh bridge or sibling delegation must be re-sealed into the ' +
128
+ 'unlock record.');
105
129
  }
106
130
  return ensureCredentialClientAnnexGenerationChecked(options);
107
131
  }
@@ -111,14 +135,17 @@ export function ensureCredentialClientAnnexGeneration(options) {
111
135
  * @param options {object} see {@link ensureCredentialClientAnnexGeneration}
112
136
  * @returns {Promise<ClientAnnexGenerationEnsureOutcome>}
113
137
  */
114
- async function ensureCredentialClientAnnexGenerationChecked({ wasServerUrl, spaceId, account, ladderSeed, standingClient, bootstrapWasFor, idStore, onRebindRecord, delegatedClients, pinStore, now }) {
138
+ async function ensureCredentialClientAnnexGenerationChecked({ wasServerUrl, spaceId, account, ladderSeed, standingClient, bootstrapWasFor, delegation, idStoreFor, onRebindRecord, delegatedClients, pinStore, now }) {
115
139
  // The gate: everything below signs as the ladder (the delegations as the
116
140
  // ladder VM, the annex entries as its per-generation rung), so unless THIS
117
141
  // ladder's VM stands in the document under the ladder-VM relation
118
142
  // asymmetry (`capabilityDelegation` without `capabilityInvocation` -- the
119
143
  // authority the mends actually exercise, which mere key presence says
120
144
  // nothing about), every mend is unverifiable. The honest refusal, before
121
- // anything is written.
145
+ // anything is written. A standing credential's VM stands whether or not the
146
+ // account has enrolled clients, so this fires only for a document that
147
+ // never carried it -- a torn establishment, or another credential's
148
+ // account.
122
149
  const vmKey = await ladderVmKeyMultibase({ ladderSeed });
123
150
  if (!ladderVmIds({ doc: account.doc }).includes(`${account.did}#${vmKey}`)) {
124
151
  throw new ClientAnnexGenerationUnavailableError({
@@ -144,231 +171,420 @@ async function ensureCredentialClientAnnexGenerationChecked({ wasServerUrl, spac
144
171
  spaceId,
145
172
  ...(now !== undefined ? { now } : {})
146
173
  });
147
- if (annexSpaceId === undefined) {
148
- // THE FRESH-SPACE ARM: neither the pointer nor a sibling names a Space.
149
- // The pre-flight attribution runs before anything is minted; the stage
150
- // order mirrors the credential-anchored genesis exactly.
151
- const updateKeys = await pointerEntryUpdateKeys({
152
- ladderSeed,
153
- log: account.log
174
+ // THE BRIDGE RENEWAL, before any arm. The bridge is the credential's one
175
+ // write path into the account log, and both minting arms end in a pointer
176
+ // entry that rides it. A stale one is replaced with a ladder-VM-signed
177
+ // delegation (the gate above proved the VM a document verification
178
+ // method), and the caller's account-log store is built over whichever
179
+ // bridge is usable.
180
+ let bridgeReminted = false;
181
+ let usableBridge = delegation;
182
+ if (standingZcapStale({
183
+ zcap: delegation,
184
+ doc: account.doc,
185
+ ...(now !== undefined ? { now } : {})
186
+ })) {
187
+ usableBridge = await delegateLogWrite({
188
+ zcapClient: ladderClient,
189
+ pointer: { did: account.did, spaceId, host: wasServerUrl },
190
+ recoveryClientDid: standingClient.did,
191
+ ...(now !== undefined ? { now } : {})
154
192
  });
193
+ bridgeReminted = true;
194
+ }
195
+ const idStore = idStoreFor({ delegation: usableBridge });
196
+ /**
197
+ * The record re-seal, through the required `onRebindRecord` seam: the one
198
+ * call site for every arm, run once the generation and pointer are
199
+ * durable. It fires whenever either recorded delegation was freshly
200
+ * minted, and grades a failure by which one that was. A fresh sibling
201
+ * nothing re-seals would strand the credential, so that throw propagates.
202
+ * A bridge-only renewal needs nothing from the re-seal: the fresh bridge
203
+ * is minted offline and already served this visit, so a lost re-seal
204
+ * leaves no wrong state behind and the next visit re-mints. It is reported
205
+ * on the outcome instead.
206
+ *
207
+ * @param options {object}
208
+ * @param options.delegatedClients {IZcap} the usable sibling delegation
209
+ * @param options.siblingReminted {boolean} whether that sibling is fresh
210
+ * @returns {Promise<object>} the outcome's `bridgeResealError` member,
211
+ * present only when a bridge-only re-seal failed
212
+ */
213
+ async function resealRecord({ delegatedClients: usableSibling, siblingReminted }) {
214
+ if (!siblingReminted && !bridgeReminted) {
215
+ return {};
216
+ }
217
+ try {
218
+ await onRebindRecord({
219
+ delegation: usableBridge,
220
+ delegatedClients: usableSibling
221
+ });
222
+ }
223
+ catch (err) {
224
+ if (siblingReminted) {
225
+ throw err;
226
+ }
227
+ return { bridgeResealError: err };
228
+ }
229
+ return {};
230
+ }
231
+ /**
232
+ * The fresh-Space stage, controller-first past the create. The create
233
+ * itself must name the ladder VM's bare did:key: a storage server
234
+ * authorizes a Space create against the controller the request body names,
235
+ * and a root invocation must be signed by that very DID, so a create
236
+ * naming the account did:webvh is refused. The controller is flipped to
237
+ * the account DID in the very next request, before anything publishes into
238
+ * the Space.
239
+ *
240
+ * So the stranding window is one request wide. Inside it the stranded
241
+ * Space is did:key-controlled, which no server orphan sweep can reap,
242
+ * since a did:key resolves from its own bytes forever. Past the flip it is
243
+ * account-controlled and reapable. Narrowing the window to that one
244
+ * request is what this ordering buys; it does not close it.
245
+ *
246
+ * The flip preceding the generation mint is load-bearing rather than
247
+ * cosmetic. The mint and the delegation embed ride the ladder-signed
248
+ * sibling delegation, and the server admits that chain only once the
249
+ * Space's controller is the account DID whose document lists the ladder
250
+ * VM. Minting first and flipping afterwards would need the bootstrap key's
251
+ * own root invocations, which is the ordering this stage replaced.
252
+ *
253
+ * @returns {Promise<string>} the fresh auxiliary Space's id
254
+ */
255
+ async function mintFreshAnnexSpace() {
155
256
  const freshSpaceId = mintSpaceId();
156
257
  const keyAgent = await ladderVmAgent({ ladderSeed });
157
258
  const bootstrapWas = bootstrapWasFor({ keyAgent });
158
- const minted = await mintCredentialClientAnnexGeneration({
259
+ const created = await ensureClientAnnexSpace({
159
260
  was: bootstrapWas,
160
- wasServerUrl,
161
261
  spaceId: freshSpaceId,
162
- controller: keyAgent.id,
163
- ladderSeed
262
+ controller: keyAgent.id
263
+ });
264
+ // The create's own answer is the current Description, so the flip carries
265
+ // it as `current` rather than re-reading what this call just wrote.
266
+ await bootstrapWas
267
+ .space(freshSpaceId)
268
+ .configure({ current: created, controller: account.did, force: true });
269
+ return freshSpaceId;
270
+ }
271
+ /**
272
+ * One Space Description read, judged by its HTTP STATUS alone. A 404 is
273
+ * `'not-found'`; a 2xx is `'present'`, whatever its body says, since a
274
+ * Space served with an unreadable body is a present Space and reading it
275
+ * as absence is exactly what would re-point a live account. Every other
276
+ * answer -- a transport failure, a 5xx, a 4xx that is not 404 -- throws,
277
+ * so nothing but a real 404 can ever reach the absence decision.
278
+ *
279
+ * The read goes through the raw signed request rather than the
280
+ * `describe()` handle, whose null-on-404 translation also swallows 401 and
281
+ * 403 and an unparseable body.
282
+ *
283
+ * @param options {object}
284
+ * @param options.was {WasClient} the client whose signer invokes
285
+ * @param options.annexSpaceId {string}
286
+ * @param [options.capability] {IZcap} the attached capability; absent
287
+ * means a root invocation
288
+ * @returns {Promise<'present' | 'not-found'>}
289
+ */
290
+ async function readSpaceDescription({ was, annexSpaceId, capability }) {
291
+ let status;
292
+ try {
293
+ const response = await was.request({
294
+ path: spacePath(annexSpaceId),
295
+ method: 'GET',
296
+ ...(capability !== undefined ? { capability } : {})
297
+ });
298
+ status = response.status;
299
+ }
300
+ catch (err) {
301
+ if (rawRequestStatus(err) === 404) {
302
+ return 'not-found';
303
+ }
304
+ throw err;
305
+ }
306
+ if (status >= 200 && status < 300) {
307
+ return 'present';
308
+ }
309
+ if (status === 404) {
310
+ return 'not-found';
311
+ }
312
+ throw new Error(`client annex: the Space Description read for "${annexSpaceId}" ` +
313
+ `answered ${status}; the visit cannot tell whether the Space is gone.`);
314
+ }
315
+ /**
316
+ * Whether the auxiliary Space itself is gone from the server, asked only
317
+ * when the pointed generation's log did not read back. A storage server
318
+ * masks an unauthorized read as the same 404 an absent Space answers, so
319
+ * ONE 404 is not absence: it is "absent, or this reader has no authority
320
+ * here". Absence is therefore corroborated by two independent readers, and
321
+ * the Space is gone only when both answer a real 404.
322
+ *
323
+ * The first reader is a ladder-signed GET-only child of the Space's own
324
+ * root, which the account DID's document backs. The sibling delegation
325
+ * cannot carry the question: it targets the items subtree beneath the
326
+ * Space and says nothing about the Space Description.
327
+ *
328
+ * The second is a root invocation as the ladder VM's BARE did:key, the
329
+ * controller a torn establishment leaves behind when its flip never
330
+ * landed. That reader answers 2xx on exactly the Space the first reader
331
+ * has no authority over, so the pair covers both controllers a live annex
332
+ * Space of this credential's can have.
333
+ *
334
+ * The residual bound, stated because no read can close it: a server that
335
+ * does not admit the ladder delegation clause's single-verb predicate
336
+ * refuses the first probe with the same masked 404, and on an
337
+ * account-controlled Space the second probe is refused too. Both readers
338
+ * then say `'not-found'` about a live Space. That is a server older than
339
+ * the one this arm was built against, and the arm's cost there is a
340
+ * re-point of a live generation.
341
+ *
342
+ * @param options {object}
343
+ * @param options.annexSpaceId {string}
344
+ * @param options.was {WasClient} the standing client's storage client
345
+ * @returns {Promise<boolean>}
346
+ */
347
+ async function annexSpaceAbsent({ annexSpaceId, was }) {
348
+ const probe = await mintSpaceRootVerbCapability({
349
+ zcapClient: ladderClient,
350
+ storageServerUrl: wasServerUrl,
351
+ spaceId: annexSpaceId,
352
+ verb: 'GET',
353
+ controller: standingClient.did,
354
+ ...(now !== undefined ? { now } : {})
355
+ });
356
+ const delegated = await readSpaceDescription({
357
+ was,
358
+ annexSpaceId,
359
+ capability: probe
360
+ });
361
+ if (delegated === 'present') {
362
+ return false;
363
+ }
364
+ const bootstrapWas = bootstrapWasFor({
365
+ keyAgent: await ladderVmAgent({ ladderSeed })
366
+ });
367
+ const asBootstrap = await readSpaceDescription({
368
+ was: bootstrapWas,
369
+ annexSpaceId
370
+ });
371
+ return asBootstrap === 'not-found';
372
+ }
373
+ let spaceMinted = false;
374
+ let pointedSpaceMissing = false;
375
+ /**
376
+ * The arms that run inside one auxiliary Space: the sibling delegation,
377
+ * the pointed generation's renewal, and the fresh-generation mint with its
378
+ * pointer move. Called once on the resolved Space, and once more on a
379
+ * fresh Space when the pointed one turns out to be gone -- the replacement
380
+ * is decided before any write, so nothing is stranded by the re-run.
381
+ *
382
+ * @param options {object}
383
+ * @param options.annexSpaceId {string} the Space this run works in
384
+ * @param options.allowSpaceReplacement {boolean} whether an absent
385
+ * pointed generation may be checked against the Space itself, and a
386
+ * fresh Space minted when the Space is gone. False on the run that
387
+ * already works in a Space this visit created
388
+ * @param [options.pointer] {string} the pointed annex DID, when the
389
+ * document names one and this run works in its Space
390
+ * @returns {Promise<ClientAnnexGenerationEnsureOutcome>}
391
+ */
392
+ async function runInAnnexSpace({ annexSpaceId, allowSpaceReplacement, pointer }) {
393
+ // A usable sibling first: absent, targeting a different Space than this
394
+ // run works in, or stale on either of the standing-zcap axes -- expiry
395
+ // (past, or inside the renewal window) and signer death (its proof key
396
+ // no longer under `capabilityDelegation` in the verified account
397
+ // document, the current-key-set rule) -- a fresh sibling is minted
398
+ // (local ladder-VM signing, which verifies because the gate above proved
399
+ // the VM a document verification method) so every annex request below
400
+ // can ride it.
401
+ let sibling = delegatedClients;
402
+ let siblingReminted = false;
403
+ const siblingStale = sibling !== undefined &&
404
+ standingZcapStale({
405
+ zcap: sibling,
406
+ doc: account.doc,
407
+ ...(now !== undefined ? { now } : {})
408
+ });
409
+ if (sibling === undefined ||
410
+ siblingSpaceId !== annexSpaceId ||
411
+ siblingStale) {
412
+ sibling = await mintDelegatedClientsDelegation({
413
+ zcapClient: ladderClient,
414
+ wasServerUrl,
415
+ clientAnnexSpaceId: annexSpaceId,
416
+ controller: standingClient.did,
417
+ ...(now !== undefined ? { now } : {})
418
+ });
419
+ siblingReminted = true;
420
+ }
421
+ const usableSibling = sibling;
422
+ const standingWas = new WasClient({
423
+ serverUrl: wasServerUrl,
424
+ zcapClient: standingClient.zcapClient
425
+ });
426
+ const storeFor = (generationId) => clientAnnexLogStore({
427
+ was: standingWas,
428
+ spaceId: annexSpaceId,
429
+ generationId,
430
+ capability: usableSibling
431
+ });
432
+ const annexPin = (generationId) => pinStore !== undefined
433
+ ? {
434
+ pinStore,
435
+ logId: clientAnnexLogPinId({ spaceId: annexSpaceId, generationId })
436
+ }
437
+ : {};
438
+ // RENEW PRECEDES MINT: a live, verifiable pointed generation is renewed
439
+ // in place; only a rung this generation never committed falls through to
440
+ // the fresh mint (the GC swap's no-committed-survivor escape).
441
+ if (pointer !== undefined) {
442
+ const parts = clientAnnexDidParts({ did: pointer });
443
+ const pointedLog = await readPublishedLog({
444
+ idStore: storeFor(parts.generationId),
445
+ expectedDid: pointer,
446
+ ...annexPin(parts.generationId)
447
+ });
448
+ if (pointedLog !== undefined) {
449
+ try {
450
+ const ensured = await ensureGenerationDelegationCurrent({
451
+ store: storeFor(parts.generationId),
452
+ ladderSeed,
453
+ generationId: parts.generationId,
454
+ mintGenerationDelegation: mintDelegation,
455
+ expectedDid: pointer,
456
+ accountDoc: account.doc,
457
+ // The head just read: on a healthy account this stage spends no
458
+ // second round trip on the same log, and hands its head back for
459
+ // the enrollment to build on.
460
+ published: pointedLog,
461
+ ...annexPin(parts.generationId),
462
+ ...(now !== undefined ? { now } : {})
463
+ });
464
+ const resealed = await resealRecord({
465
+ delegatedClients: usableSibling,
466
+ siblingReminted
467
+ });
468
+ return {
469
+ clientAnnexDid: pointer,
470
+ generationDelegation: ensured.delegation,
471
+ delegation: usableBridge,
472
+ delegatedClients: usableSibling,
473
+ generationMinted: false,
474
+ spaceMinted,
475
+ pointedSpaceMissing,
476
+ delegationRenewed: ensured.renewed,
477
+ siblingReminted,
478
+ bridgeReminted,
479
+ ...(ensured.published !== undefined
480
+ ? { generationLog: ensured.published }
481
+ : {}),
482
+ ...resealed
483
+ };
484
+ }
485
+ catch (err) {
486
+ if (err.name !==
487
+ 'ClientAnnexRungUncommittedError') {
488
+ throw err;
489
+ }
490
+ // This credential's rung was never committed into the pointed
491
+ // generation (bound mid-generation): fall through to the fresh
492
+ // mint, which commits it with the fresh genesis.
493
+ }
494
+ }
495
+ else if (allowSpaceReplacement &&
496
+ (await annexSpaceAbsent({ annexSpaceId, was: standingWas }))) {
497
+ // THE POINTED SPACE IS GONE. A missing generation log inside a live
498
+ // Space is the fresh-generation arm's case; a missing SPACE is not,
499
+ // since every write below would land in a Space that does not exist
500
+ // and the visit would fail on something other than the typed
501
+ // refusal. The Space is re-minted and the run starts over in it,
502
+ // before anything has been written.
503
+ await assertPointerEntryAttributable({ ladderSeed, log: account.log });
504
+ const freshSpaceId = await mintFreshAnnexSpace();
505
+ spaceMinted = true;
506
+ pointedSpaceMissing = true;
507
+ return runInAnnexSpace({
508
+ annexSpaceId: freshSpaceId,
509
+ allowSpaceReplacement: false
510
+ });
511
+ }
512
+ }
513
+ // THE FRESH-GENERATION ARM: pre-flight attribution first (never mint a
514
+ // generation the pointer entry cannot then name), then the GC swap's
515
+ // stage order minus its revoke -- mint, install the delegation,
516
+ // re-point. The fresh genesis commits only the acting credential's annex
517
+ // rung; other standing credentials' per-generation rungs are re-committed
518
+ // only by their own later ceremonies (a property of every generation
519
+ // swap).
520
+ await assertPointerEntryAttributable({ ladderSeed, log: account.log });
521
+ const minted = await mintCredentialClientAnnexGeneration({
522
+ was: standingWas,
523
+ wasServerUrl,
524
+ spaceId: annexSpaceId,
525
+ controller: account.did,
526
+ ladderSeed,
527
+ capability: usableSibling
164
528
  });
165
- // The delegation embeds while the Space still answers to the bootstrap
166
- // did:key; the controller flip follows, then the pointer entry.
167
529
  const ensured = await ensureGenerationDelegationCurrent({
168
- store: clientAnnexLogStore({
169
- was: bootstrapWas,
170
- spaceId: freshSpaceId,
171
- generationId: minted.generationId
172
- }),
530
+ store: storeFor(minted.generationId),
173
531
  ladderSeed,
174
532
  generationId: minted.generationId,
175
533
  mintGenerationDelegation: mintDelegation,
176
534
  expectedDid: minted.did,
177
- ...(pinStore !== undefined
178
- ? {
179
- pinStore,
180
- logId: clientAnnexLogPinId({
181
- spaceId: freshSpaceId,
182
- generationId: minted.generationId
183
- })
184
- }
185
- : {}),
535
+ ...annexPin(minted.generationId),
186
536
  ...(now !== undefined ? { now } : {})
187
537
  });
188
- await bootstrapWas
189
- .space(freshSpaceId)
190
- .configure({ controller: account.did, force: true });
191
- await setDelegatedClientsPointer({
538
+ // No revocation of the superseded generation's delegation is attempted:
539
+ // a transient visit has no reach that could invoke it (the standing
540
+ // client is neither the Space controller nor in that delegation's
541
+ // chain). The pointer move itself retires it on a conforming server --
542
+ // the inspector clause compares the delegation's controller against the
543
+ // document's pointer -- and it otherwise rots on its TTL.
544
+ await movePointerAsLadder({
192
545
  idStore,
193
- updateKeys,
546
+ ladderSeed,
194
547
  clientAnnexDid: minted.did,
195
- expectedDid: account.did,
196
- logOnly: true,
548
+ accountDid: account.did,
197
549
  ...(pinStore !== undefined
198
550
  ? { pinStore, logId: accountLogPinId({ spaceId }) }
199
551
  : {})
200
552
  });
201
- const sibling = await mintDelegatedClientsDelegation({
202
- zcapClient: ladderClient,
203
- wasServerUrl,
204
- clientAnnexSpaceId: freshSpaceId,
205
- controller: standingClient.did,
206
- ...(now !== undefined ? { now } : {})
553
+ const resealed = await resealRecord({
554
+ delegatedClients: usableSibling,
555
+ siblingReminted
207
556
  });
208
- await onRebindRecord({ delegatedClients: sibling });
209
557
  return {
210
558
  clientAnnexDid: minted.did,
211
559
  generationDelegation: ensured.delegation,
212
- delegatedClients: sibling,
560
+ delegation: usableBridge,
561
+ delegatedClients: usableSibling,
213
562
  generationMinted: true,
214
- spaceMinted: true,
563
+ spaceMinted,
564
+ pointedSpaceMissing,
215
565
  delegationRenewed: false,
216
- siblingReminted: true
566
+ siblingReminted,
567
+ bridgeReminted,
568
+ ...resealed
217
569
  };
218
570
  }
219
- // THE EXISTING-SPACE ARMS. A usable sibling first: absent, targeting a
220
- // different Space than the resolved one, or stale on either of the
221
- // standing-zcap axes -- expiry (past, or inside the renewal window) and
222
- // signer death (its proof key no longer under `capabilityDelegation` in
223
- // the verified account document, the current-key-set rule) -- a fresh
224
- // sibling is minted (local ladder-VM signing, which verifies because the
225
- // gate above proved the VM a document verification method) so every annex
226
- // request below can ride it.
227
- let sibling = delegatedClients;
228
- let siblingReminted = false;
229
- const siblingStale = sibling !== undefined &&
230
- (zcapExpiring({
231
- ...(sibling.expires !== undefined
232
- ? { expires: sibling.expires }
233
- : {}),
234
- ...(now !== undefined ? { now } : {})
235
- }) ||
236
- !delegationKeyInDocument({
237
- doc: account.doc,
238
- ...(delegationProofKeyId(sibling) !== undefined
239
- ? { delegationKeyId: delegationProofKeyId(sibling) }
240
- : {})
241
- }));
242
- if (sibling === undefined ||
243
- siblingSpaceId !== annexSpaceId ||
244
- siblingStale) {
245
- sibling = await mintDelegatedClientsDelegation({
246
- zcapClient: ladderClient,
247
- wasServerUrl,
248
- clientAnnexSpaceId: annexSpaceId,
249
- controller: standingClient.did,
250
- ...(now !== undefined ? { now } : {})
251
- });
252
- siblingReminted = true;
253
- }
254
- const usableSibling = sibling;
255
- const standingWas = new WasClient({
256
- serverUrl: wasServerUrl,
257
- zcapClient: standingClient.zcapClient
258
- });
259
- const storeFor = (generationId) => clientAnnexLogStore({
260
- was: standingWas,
261
- spaceId: annexSpaceId,
262
- generationId,
263
- capability: usableSibling
264
- });
265
- const annexPin = (generationId) => pinStore !== undefined
266
- ? {
267
- pinStore,
268
- logId: clientAnnexLogPinId({ spaceId: annexSpaceId, generationId })
269
- }
270
- : {};
271
- // RENEW PRECEDES MINT: a live, verifiable pointed generation is renewed in
272
- // place; only a rung this generation never committed falls through to the
273
- // fresh mint (the GC swap's no-committed-survivor escape).
274
- if (pointer !== undefined) {
275
- const parts = clientAnnexDidParts({ did: pointer });
276
- const pointedLog = await readPublishedLog({
277
- idStore: storeFor(parts.generationId),
278
- expectedDid: pointer,
279
- ...annexPin(parts.generationId)
571
+ if (annexSpaceId === undefined) {
572
+ // NEITHER THE POINTER NOR A SIBLING NAMES A SPACE. The pre-flight
573
+ // attribution runs before any Space or generation is minted (a bridge
574
+ // delegation mint writes nothing durable).
575
+ await assertPointerEntryAttributable({ ladderSeed, log: account.log });
576
+ const freshSpaceId = await mintFreshAnnexSpace();
577
+ spaceMinted = true;
578
+ return runInAnnexSpace({
579
+ annexSpaceId: freshSpaceId,
580
+ allowSpaceReplacement: false
280
581
  });
281
- if (pointedLog !== undefined) {
282
- try {
283
- const ensured = await ensureGenerationDelegationCurrent({
284
- store: storeFor(parts.generationId),
285
- ladderSeed,
286
- generationId: parts.generationId,
287
- mintGenerationDelegation: mintDelegation,
288
- expectedDid: pointer,
289
- accountDoc: account.doc,
290
- ...annexPin(parts.generationId),
291
- ...(now !== undefined ? { now } : {})
292
- });
293
- if (siblingReminted) {
294
- await onRebindRecord({ delegatedClients: usableSibling });
295
- }
296
- return {
297
- clientAnnexDid: pointer,
298
- generationDelegation: ensured.delegation,
299
- delegatedClients: usableSibling,
300
- generationMinted: false,
301
- spaceMinted: false,
302
- delegationRenewed: ensured.renewed,
303
- siblingReminted
304
- };
305
- }
306
- catch (err) {
307
- if (err.name !== 'ClientAnnexRungUncommittedError') {
308
- throw err;
309
- }
310
- // This credential's rung was never committed into the pointed
311
- // generation (bound mid-generation): fall through to the fresh mint,
312
- // which commits it with the fresh genesis.
313
- }
314
- }
315
582
  }
316
- // THE FRESH-GENERATION ARM, existing Space: pre-flight attribution first
317
- // (never mint a generation the pointer entry cannot then name), then the
318
- // GC swap's stage order minus its revoke -- mint, install the delegation,
319
- // re-point. The fresh genesis commits only the acting credential's annex
320
- // rung; other standing credentials' per-generation rungs are re-committed
321
- // only by their own later ceremonies (a property of every generation
322
- // swap).
323
- const updateKeys = await pointerEntryUpdateKeys({
324
- ladderSeed,
325
- log: account.log
326
- });
327
- const minted = await mintCredentialClientAnnexGeneration({
328
- was: standingWas,
329
- wasServerUrl,
330
- spaceId: annexSpaceId,
331
- controller: account.did,
332
- ladderSeed,
333
- capability: usableSibling
334
- });
335
- const ensured = await ensureGenerationDelegationCurrent({
336
- store: storeFor(minted.generationId),
337
- ladderSeed,
338
- generationId: minted.generationId,
339
- mintGenerationDelegation: mintDelegation,
340
- expectedDid: minted.did,
341
- ...annexPin(minted.generationId),
342
- ...(now !== undefined ? { now } : {})
343
- });
344
- // No revocation of the superseded generation's delegation is attempted:
345
- // a transient visit has no reach that could invoke it (the standing client
346
- // is neither the Space controller nor in that delegation's chain). The
347
- // pointer move itself retires it on a conforming server -- the inspector
348
- // clause compares the delegation's controller against the document's
349
- // pointer -- and it otherwise rots on its TTL.
350
- await setDelegatedClientsPointer({
351
- idStore,
352
- updateKeys,
353
- clientAnnexDid: minted.did,
354
- expectedDid: account.did,
355
- logOnly: true,
356
- ...(pinStore !== undefined
357
- ? { pinStore, logId: accountLogPinId({ spaceId }) }
358
- : {})
583
+ return runInAnnexSpace({
584
+ annexSpaceId,
585
+ allowSpaceReplacement: true,
586
+ ...(pointer !== undefined ? { pointer } : {})
359
587
  });
360
- if (siblingReminted) {
361
- await onRebindRecord({ delegatedClients: usableSibling });
362
- }
363
- return {
364
- clientAnnexDid: minted.did,
365
- generationDelegation: ensured.delegation,
366
- delegatedClients: usableSibling,
367
- generationMinted: true,
368
- spaceMinted: false,
369
- delegationRenewed: false,
370
- siblingReminted
371
- };
372
588
  }
373
589
  /**
374
590
  * The annex Space, in the settled resolution order: the account document's
@@ -417,11 +633,7 @@ export function resolveClientAnnexSpaceId({ doc, delegatedClients }) {
417
633
  * @returns {Promise<ClientWebvhUpdateKeys>}
418
634
  */
419
635
  export async function pointerEntryUpdateKeys({ ladderSeed, log }) {
420
- const params = effectiveParameters(log);
421
- const current = params[params.length - 1] ?? {
422
- updateKeys: [],
423
- nextKeyHashes: []
424
- };
636
+ const current = currentLogParameters({ log });
425
637
  const unattributable = () => new ClientAnnexGenerationUnavailableError({
426
638
  reason: 'update-key-not-attributable',
427
639
  message: "No current account-log update key is a rung of this credential's " +
@@ -446,4 +658,139 @@ export async function pointerEntryUpdateKeys({ ladderSeed, log }) {
446
658
  });
447
659
  return { updateSeed: attributed.rung.seed, stagedSeed: staged.seed };
448
660
  }
661
+ /**
662
+ * The pre-flight the pointer-moving arms run before minting anything: this
663
+ * ladder has a rung the pointer entry will be able to sign with, either
664
+ * standing in `updateKeys` already or committed in `nextKeyHashes` and
665
+ * revealable by {@link movePointerAsLadder}. A ladder the log carries no rung
666
+ * of at all, and an ambiguous attribution, refuse here with
667
+ * {@link ClientAnnexGenerationUnavailableError} before a generation or a
668
+ * Space is minted.
669
+ *
670
+ * @param options {object}
671
+ * @param options.ladderSeed {Uint8Array}
672
+ * @param options.log {DIDLog} the VERIFIED account log
673
+ * @returns {Promise<void>}
674
+ */
675
+ async function assertPointerEntryAttributable({ ladderSeed, log }) {
676
+ const current = currentLogParameters({ log });
677
+ try {
678
+ await attributeLadderRung({ ladderSeed, published: current });
679
+ }
680
+ catch (err) {
681
+ if (err.name === 'LadderAttributionError') {
682
+ throw new ClientAnnexGenerationUnavailableError({
683
+ reason: 'update-key-not-attributable',
684
+ message: "The account log carries no rung of this credential's ladder, or " +
685
+ 'the attribution is ambiguous; the pointer entry could not be ' +
686
+ 'signed, so nothing is minted.'
687
+ });
688
+ }
689
+ throw err;
690
+ }
691
+ }
692
+ /**
693
+ * The `#DelegatedClients` pointer move as a credential-only visit makes it:
694
+ * reveal this ladder's rung when only its hash stands committed, re-read, and
695
+ * write the pointer entry signed by the now-revealed rung.
696
+ *
697
+ * A self-enrollment's add entry spends the revealed rung, so on any account
698
+ * that has ever self-enrolled the rung is merely committed and the reveal is
699
+ * what makes the pointer entry signable at all.
700
+ *
701
+ * ACCEPTED CONSEQUENCE (design FW-356, finding R3): the reveal retires
702
+ * nothing, and the pointer entry re-states `updateKeys` verbatim, so the
703
+ * acting rung stands in the account log's `updateKeys` afterwards. The price
704
+ * of a pointer move is therefore a standing account update key in the
705
+ * credential's hand -- direct document-edit authority through the bridge with
706
+ * no further reveal -- retired at that credential's next self-enrollment
707
+ * (whose add entry drops the attributed rung) or at its retirement. This is
708
+ * documented rather than prevented.
709
+ *
710
+ * Both entries run inside ONE conflict retry, and every attempt re-reads the
711
+ * head and re-attributes the rung from it, so a racing ceremony that consumes
712
+ * the rung climbs to the winner's committed rung instead of refusing
713
+ * `update-key-not-attributable` on a rung that is no longer current.
714
+ *
715
+ * Three things make that hold. A rung the winner consumed between this
716
+ * attempt's reveal and its re-read reads back as
717
+ * `update-key-not-attributable`, which inside this loop is staleness rather
718
+ * than a refusal -- the pre-flight already found a rung -- so it is raised as
719
+ * a conflict and the next attempt reveals the winner's committed rung. The pointer entry runs as
720
+ * {@link setDelegatedClientsPointerOnce} rather than through its retrying
721
+ * wrapper: the wrapper's inner loop would re-invoke the attempt with the
722
+ * attribution's stale `updateKeys`, and a rung the racing winner consumed
723
+ * cannot become authorized by re-reading, so the attempt would end on the
724
+ * plain not-authorized refusal, which the outer loop does not retry. And the
725
+ * attempt is handed the very head this attempt attributed against, so a
726
+ * racing entry landing between the attribution and the PUT loses the CAS and
727
+ * surfaces as a `WebvhLogConflictError` -- the refusal the outer loop
728
+ * re-attributes from. The pre-flight guard
729
+ * ({@link assertPointerEntryAttributable}) therefore cannot fire from
730
+ * staleness here: it runs on the caller's snapshot before anything is minted,
731
+ * while every entry this function publishes is built on a head it read
732
+ * itself.
733
+ *
734
+ * @param options {object}
735
+ * @param options.idStore {WebvhIdStore} the account log's store, from the
736
+ * record's bridge delegation
737
+ * @param options.ladderSeed {Uint8Array} the credential's ladder seed
738
+ * @param options.clientAnnexDid {string} the generation to point at
739
+ * @param options.accountDid {string} the account DID the log must resolve to
740
+ * @param [options.pinStore] {ResourceLogPinStore} the visit's chain-head pins
741
+ * @param [options.logId] {string} the account log's pin slot; required
742
+ * whenever a `pinStore` is supplied
743
+ * @returns {Promise<void>}
744
+ */
745
+ async function movePointerAsLadder({ idStore, ladderSeed, clientAnnexDid, accountDid, pinStore, logId }) {
746
+ const pin = pinStore !== undefined && logId !== undefined ? { pinStore, logId } : {};
747
+ await withLogConflictRetry(async () => {
748
+ await revealLadderRungWebvh({
749
+ store: idStore,
750
+ ladderSeed,
751
+ expectedDid: accountDid,
752
+ ...pin
753
+ });
754
+ // The head the reveal entry just published (or the unchanged head, when
755
+ // the rung was revealed already), under the same pin.
756
+ const published = await readPublishedLogOrThrow({
757
+ idStore,
758
+ expectedDid: accountDid,
759
+ ...pin,
760
+ missingMessage: 'did:webvh: did.jsonl is missing; nothing to point at a client annex.'
761
+ });
762
+ // A racing ceremony that consumed the rung between this reveal and this
763
+ // read leaves the ladder committed-only again, which the attribution
764
+ // reports as `update-key-not-attributable`. Inside this loop that is a
765
+ // staleness signal rather than a refusal -- the pre-flight already found
766
+ // a rung, and a fresh reveal is exactly what the next attempt does -- so
767
+ // it becomes a conflict for the retry to re-run.
768
+ let updateKeys;
769
+ try {
770
+ updateKeys = await pointerEntryUpdateKeys({
771
+ ladderSeed,
772
+ log: published.log
773
+ });
774
+ }
775
+ catch (err) {
776
+ if (err.name ===
777
+ 'ClientAnnexGenerationUnavailableError' &&
778
+ err.reason ===
779
+ 'update-key-not-attributable') {
780
+ throw new WebvhLogConflictError('did:webvh: a concurrent ceremony consumed the rung this pointer ' +
781
+ 'move just revealed; the move re-runs from a fresh reveal.', { cause: err });
782
+ }
783
+ throw err;
784
+ }
785
+ await setDelegatedClientsPointerOnce({
786
+ idStore,
787
+ signer: { kind: 'client', updateKeys },
788
+ clientAnnexDid,
789
+ expectedDid: accountDid,
790
+ logOnly: true,
791
+ published,
792
+ ...pin
793
+ });
794
+ });
795
+ }
449
796
  //# sourceMappingURL=heal.js.map