@interop/wallet-core 0.49.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 (160) 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/{webvh/companionGc.d.ts → clientAnnex/gc.d.ts} +50 -16
  15. package/dist/clientAnnex/gc.d.ts.map +1 -0
  16. package/dist/{webvh/companionGc.js → clientAnnex/gc.js} +103 -71
  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 +89 -15
  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} +333 -119
  31. package/dist/clientAnnex/log.d.ts.map +1 -0
  32. package/dist/{webvh/companion.js → clientAnnex/log.js} +583 -199
  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 +2 -2
  107. package/dist/space/activity.js +3 -3
  108. package/dist/space/activity.js.map +1 -1
  109. package/dist/unlock/index.d.ts +11 -23
  110. package/dist/unlock/index.d.ts.map +1 -1
  111. package/dist/unlock/index.js +10 -21
  112. package/dist/unlock/index.js.map +1 -1
  113. package/dist/unlock/retire.d.ts +47 -3
  114. package/dist/unlock/retire.d.ts.map +1 -1
  115. package/dist/unlock/retire.js +21 -5
  116. package/dist/unlock/retire.js.map +1 -1
  117. package/dist/unlock/standingWebvh.d.ts +37 -82
  118. package/dist/unlock/standingWebvh.d.ts.map +1 -1
  119. package/dist/unlock/standingWebvh.js +64 -247
  120. package/dist/unlock/standingWebvh.js.map +1 -1
  121. package/dist/unlock/unlockRecord.d.ts +13 -6
  122. package/dist/unlock/unlockRecord.d.ts.map +1 -1
  123. package/dist/unlock/unlockRecord.js +14 -8
  124. package/dist/unlock/unlockRecord.js.map +1 -1
  125. package/dist/webvh/delegatedLogStore.d.ts +5 -5
  126. package/dist/webvh/delegatedLogStore.js +1 -1
  127. package/dist/webvh/didWebvh.d.ts +19 -9
  128. package/dist/webvh/didWebvh.d.ts.map +1 -1
  129. package/dist/webvh/didWebvh.js +16 -16
  130. package/dist/webvh/didWebvh.js.map +1 -1
  131. package/dist/webvh/index.d.ts +9 -25
  132. package/dist/webvh/index.d.ts.map +1 -1
  133. package/dist/webvh/index.js +9 -23
  134. package/dist/webvh/index.js.map +1 -1
  135. package/dist/webvh/revokeClient.d.ts +77 -5
  136. package/dist/webvh/revokeClient.d.ts.map +1 -1
  137. package/dist/webvh/revokeClient.js +154 -61
  138. package/dist/webvh/revokeClient.js.map +1 -1
  139. package/dist/webvh/standingZcap.d.ts +11 -1
  140. package/dist/webvh/standingZcap.d.ts.map +1 -1
  141. package/dist/webvh/standingZcap.js +13 -14
  142. package/dist/webvh/standingZcap.js.map +1 -1
  143. package/dist/webvh/wasIdStore.d.ts +13 -7
  144. package/dist/webvh/wasIdStore.d.ts.map +1 -1
  145. package/dist/webvh/wasIdStore.js +8 -4
  146. package/dist/webvh/wasIdStore.js.map +1 -1
  147. package/dist/webvh/zcap.d.ts +0 -24
  148. package/dist/webvh/zcap.d.ts.map +1 -1
  149. package/dist/webvh/zcap.js +0 -42
  150. package/dist/webvh/zcap.js.map +1 -1
  151. package/package.json +40 -34
  152. package/dist/unlock/ladder.d.ts.map +0 -1
  153. package/dist/unlock/ladder.js +0 -258
  154. package/dist/unlock/ladder.js.map +0 -1
  155. package/dist/unlock/selfEnroll.d.ts.map +0 -1
  156. package/dist/unlock/selfEnroll.js.map +0 -1
  157. package/dist/webvh/companion.d.ts.map +0 -1
  158. package/dist/webvh/companion.js.map +0 -1
  159. package/dist/webvh/companionGc.d.ts.map +0 -1
  160. package/dist/webvh/companionGc.js.map +0 -1
@@ -2,25 +2,25 @@
2
2
  * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
3
  */
4
4
  /**
5
- * The companion did:webvh: the disposable sidecar log holding transient
5
+ * The client annex did:webvh: the disposable sidecar log holding transient
6
6
  * per-visit verification methods, one generation per flat `gen-` collection
7
- * inside the account's stable auxiliary companion Space -- so per-visit facts
7
+ * inside the account's stable auxiliary annex Space -- so per-visit facts
8
8
  * stay out of the account's identity log entirely. This module is the
9
9
  * generation's identity, genesis, and enrollment machinery: the
10
10
  * `gen-<random>` generation id convention, the typed auxiliary Space ensure,
11
- * the genesis parameters, the pin-slot key for companion continuity, the atomic
11
+ * the genesis parameters, the pin-slot key for annex continuity, the atomic
12
12
  * transient-enrollment entry, and the account document's delegated-clients
13
13
  * service entry (the pointer at the current generation).
14
14
  *
15
- * The companion's posture differs from the account log's on purpose:
15
+ * The annex's posture differs from the account log's on purpose:
16
16
  *
17
- * - Update authority is each standing credential's static companion rung 0
17
+ * - Update authority is each standing credential's static annex rung 0
18
18
  * (chain length one, no rung advancement, no attribution scan). Genesis
19
19
  * states the minting credential's rung-0 key in `updateKeys` and commits
20
20
  * every standing credential's rung-0 hash in `nextKeyHashes` -- the minting
21
21
  * key's own carry-over hash included, or no later entry could re-state it.
22
22
  * - Prerotation stays on (the rung-0 hashes are the commitment chain),
23
- * witnesses stay off, and portability is off: a companion is
23
+ * witnesses stay off, and portability is off: an annex is
24
24
  * generation-scoped and host-bound, and replacement is a GC swap, never a
25
25
  * portability move.
26
26
  * - The genesis document is bare -- no verification methods, no service
@@ -32,9 +32,9 @@
32
32
  * - No `did:web` projection exists: the generation collection holds only its
33
33
  * `did.jsonl`, capability-gated rather than world-readable.
34
34
  *
35
- * Ordering rule: the companion log publishes FIRST; only then does the
35
+ * Ordering rule: the annex log publishes FIRST; only then does the
36
36
  * caller re-point the account document's `#DelegatedClients` service entry at
37
- * the new companion DID. A companion nobody points at is authorization-inert
37
+ * the new annex DID. An annex nobody points at is authorization-inert
38
38
  * (no delegation ever names it), so a tear or a double-genesis race leaks
39
39
  * storage, never authority -- and the standing orphan discovery is a plain
40
40
  * `gen-` prefix match over the auxiliary Space's collection listing, with no
@@ -43,9 +43,9 @@
43
43
  * Generation identity is random, never a counter: a reused generation id would
44
44
  * re-derive the same rung-0 update key for a new generation, and no counter
45
45
  * carrier survives GC deleting the old collection. The generation id is also
46
- * the generation-identifying half of the companion rung HKDF labels
46
+ * the generation-identifying half of the annex rung HKDF labels
47
47
  * (`<generationId>/rung/<k>` under the unlock ladder's one salt), so there is
48
- * exactly one spelling of a generation's identity -- the one the companion
48
+ * exactly one spelling of a generation's identity -- the one the annex
49
49
  * DID string already embeds.
50
50
  */
51
51
  import { createDID, deriveNextKeyHash, updateDID } from '@interop/did-method-webvh';
@@ -53,31 +53,32 @@ import { rootCapabilityId, spaceItems, spacePath, toUrl } from '@interop/was-cli
53
53
  import { base64urlnopad } from '@scure/base';
54
54
  import { DID_LOG_RESOURCE } from '../space/collections.js';
55
55
  import { resourceLogPinId } from '../resourceLog/pin.js';
56
- import { companionRung } from '../unlock/ladder.js';
57
- import { assertCarryOverCommitments, concludeWithPublishedLog, didWebvhControllerTemplate, MULTIKEY_VM_TYPE, publishUpdatedLog, putLogResource, readPublishedLog, relationIds, updateKeyMultibase, updateKeySigner, withLogConflictRetry } from './didWebvh.js';
58
- import { STANDING_ZCAP_TTL_MS, zcapExpiring } from './standingZcap.js';
59
- import { wasWebvhLogStore } from './wasIdStore.js';
56
+ import { clientAnnexRung } from './ladder.js';
57
+ import { assertCarryOverCommitments, concludeWithPublishedLog, didWebvhControllerTemplate, MULTIKEY_VM_TYPE, publishUpdatedLog, putLogResource, readPublishedLog, relationIds, updateKeyMultibase, updateKeySigner, withLogConflictRetry } from '../webvh/didWebvh.js';
58
+ import { delegationKeyInDocument } from '../webvh/listClients.js';
59
+ import { delegationProofKeyId, STANDING_ZCAP_TTL_MS, zcapExpiring } from '../webvh/standingZcap.js';
60
+ import { wasWebvhLogStore } from '../webvh/wasIdStore.js';
60
61
  /**
61
- * The Space Description `type` array of the auxiliary companion Space, set at
62
+ * The Space Description `type` array of the auxiliary annex Space, set at
62
63
  * creation (the server treats a Space's `type` as immutable afterwards).
63
64
  * Wire-level and permanent: the server's inspector clause recognizes the
64
65
  * `DelegatedClientsSpace` member, and user-data surfaces exclude auxiliary
65
66
  * Spaces by it.
66
67
  */
67
- export const COMPANION_SPACE_TYPE = [
68
+ export const CLIENT_ANNEX_SPACE_TYPE = [
68
69
  'Space',
69
70
  'AuxiliarySpace',
70
71
  'DelegatedClientsSpace'
71
72
  ];
72
73
  /**
73
74
  * The `type` member that marks a Space as the delegated-clients auxiliary
74
- * Space (the last entry of {@link COMPANION_SPACE_TYPE}).
75
+ * Space (the last entry of {@link CLIENT_ANNEX_SPACE_TYPE}).
75
76
  */
76
77
  const DELEGATED_CLIENTS_SPACE_TYPE = 'DelegatedClientsSpace';
77
78
  /**
78
79
  * The literal prefix of every generation collection's name. Wire-level and
79
80
  * permanent: orphan discovery is a plain prefix match over the auxiliary
80
- * Space's collection listing, and the generation id embeds in every companion
81
+ * Space's collection listing, and the generation id embeds in every annex
81
82
  * DID string ever published.
82
83
  */
83
84
  export const GENERATION_ID_PREFIX = 'gen-';
@@ -107,7 +108,7 @@ export function mintGenerationId() {
107
108
  }
108
109
  /**
109
110
  * Refuses anything that is not a well-formed generation id. Run by every
110
- * companion builder that takes a generation id, so a malformed one is refused
111
+ * annex builder that takes a generation id, so a malformed one is refused
111
112
  * before it can reach a DID string, an HKDF label, or a collection id.
112
113
  *
113
114
  * @param generationId {string}
@@ -119,19 +120,19 @@ export function assertGenerationId(generationId) {
119
120
  }
120
121
  }
121
122
  /**
122
- * The pin-slot key for one companion generation's log -- host-free like every
123
+ * The pin-slot key for one annex generation's log -- host-free like every
123
124
  * pin-slot key, keyed by the auxiliary Space id and the generation id.
124
125
  * A transient session keeps this slot in an in-memory pin store (a durable
125
126
  * pin is the wrong lifetime for a disposable log, and a transient session
126
127
  * must not durably create the pin store on a read); a durable client's store
127
- * clears companion slots when the generation is collected.
128
+ * clears annex slots when the generation is collected.
128
129
  *
129
130
  * @param options {object}
130
- * @param options.spaceId {string} the auxiliary companion Space's id
131
+ * @param options.spaceId {string} the auxiliary annex Space's id
131
132
  * @param options.generationId {string} the generation collection's name
132
133
  * @returns {string}
133
134
  */
134
- export function companionLogPinId({ spaceId, generationId }) {
135
+ export function clientAnnexLogPinId({ spaceId, generationId }) {
135
136
  return resourceLogPinId({
136
137
  spaceId,
137
138
  collectionId: generationId,
@@ -139,7 +140,7 @@ export function companionLogPinId({ spaceId, generationId }) {
139
140
  });
140
141
  }
141
142
  /**
142
- * The WAS-backed store a companion generation's ceremonies read and publish
143
+ * The WAS-backed store an annex generation's ceremonies read and publish
143
144
  * through with controller-tier signing (an enrolled client). A transient
144
145
  * session writes through the delegated store instead
145
146
  * (`delegatedWebvhLogStore`, invoking the credential's sibling delegation);
@@ -147,16 +148,24 @@ export function companionLogPinId({ spaceId, generationId }) {
147
148
  *
148
149
  * @param options {object}
149
150
  * @param options.was {WasClient}
150
- * @param options.spaceId {string} the auxiliary companion Space's id
151
+ * @param options.spaceId {string} the auxiliary annex Space's id
151
152
  * @param options.generationId {string} the generation collection's name
153
+ * @param [options.capability] {IZcap} an invocation capability every request
154
+ * rides (the sibling delegation, where the caller is not an enrolled
155
+ * invoker); absent, requests invoke the root capability
152
156
  * @returns {WebvhLogResourceStore}
153
157
  */
154
- export function companionLogStore({ was, spaceId, generationId }) {
158
+ export function clientAnnexLogStore({ was, spaceId, generationId, capability }) {
155
159
  assertGenerationId(generationId);
156
- return wasWebvhLogStore({ was, spaceId, collectionId: generationId });
160
+ return wasWebvhLogStore({
161
+ was,
162
+ spaceId,
163
+ collectionId: generationId,
164
+ ...(capability !== undefined ? { capability } : {})
165
+ });
157
166
  }
158
167
  /**
159
- * The DID core context -- the companion genesis document's whole `@context`.
168
+ * The DID core context -- the annex genesis document's whole `@context`.
160
169
  * The document carries no verification methods and no service entries at
161
170
  * genesis, so no other vocabulary is in scope; the entry that first publishes
162
171
  * a typed member extends the context then (a did:webvh entry replaces the
@@ -164,23 +173,23 @@ export function companionLogStore({ was, spaceId, generationId }) {
164
173
  */
165
174
  const DID_CORE_CONTEXT = 'https://www.w3.org/ns/did/v1';
166
175
  /**
167
- * The Multikey context, appended to the companion document's `@context` by
176
+ * The Multikey context, appended to the annex document's `@context` by
168
177
  * the entry that first publishes a transient verification method (genesis
169
178
  * carries the DID core context only, having no typed members to define).
170
179
  */
171
180
  const MULTIKEY_CONTEXT_URL = 'https://w3id.org/security/multikey/v1';
172
181
  /**
173
- * Creates the one-entry companion generation log. The genesis parameters are
174
- * the companion posture (see the module doc): prerotation on via the rung-0
182
+ * Creates the one-entry annex generation log. The genesis parameters are
183
+ * the annex posture (see the module doc): prerotation on via the rung-0
175
184
  * hash commitments, no witnesses, portability off (the library's default,
176
185
  * stated explicitly in the emitted entry), and a bare document -- id and the
177
186
  * DID core context, nothing else.
178
187
  *
179
188
  * The caller supplies the update authority: the minting credential's
180
- * companion rung-0 key as the sole `updateKeys` member, `nextKeyHashes` as
189
+ * annex rung-0 key as the sole `updateKeys` member, `nextKeyHashes` as
181
190
  * every standing credential's rung-0 hash (restated explicitly on every later
182
191
  * entry, never inherited), and rung 0's signer. The minting key's own
183
- * carry-over hash MUST be among the commitments -- every companion entry
192
+ * carry-over hash MUST be among the commitments -- every annex entry
184
193
  * re-states `updateKeys` containing the revealed rung-0 keys, and the
185
194
  * resolver checks the re-statement against the previous entry's commitments
186
195
  * -- so a `nextKeyHashes` that omits it is refused here rather than
@@ -188,20 +197,20 @@ const MULTIKEY_CONTEXT_URL = 'https://w3id.org/security/multikey/v1';
188
197
  *
189
198
  * @param options {object}
190
199
  * @param options.wasServerUrl {string}
191
- * @param options.spaceId {string} the auxiliary companion Space's id
200
+ * @param options.spaceId {string} the auxiliary annex Space's id
192
201
  * @param options.generationId {string} the generation collection's name
193
202
  * @param options.updateKeyPublicKeyMultibase {string} the minting
194
- * credential's companion rung-0 key
203
+ * credential's annex rung-0 key
195
204
  * @param options.nextKeyHashes {string[]} every standing credential's
196
205
  * rung-0 hash, the minting credential's included
197
206
  * @param options.signer {Signer} the minting credential's rung-0 signer
198
207
  * @returns {Promise<{ log: DIDLog; did: string; doc: DIDDoc }>}
199
208
  */
200
- export async function createCompanionLog({ wasServerUrl, spaceId, generationId, updateKeyPublicKeyMultibase, nextKeyHashes, signer }) {
209
+ export async function createClientAnnexLog({ wasServerUrl, spaceId, generationId, updateKeyPublicKeyMultibase, nextKeyHashes, signer }) {
201
210
  assertGenerationId(generationId);
202
211
  const carryOverHash = await deriveNextKeyHash(updateKeyPublicKeyMultibase);
203
212
  if (!nextKeyHashes.includes(carryOverHash)) {
204
- throw new Error('companion genesis: `nextKeyHashes` must include the minting ' +
213
+ throw new Error('client annex genesis: `nextKeyHashes` must include the minting ' +
205
214
  "credential's own rung-0 hash (the carry-over commitment), or no " +
206
215
  'later entry could ever re-state the revealed key.');
207
216
  }
@@ -220,13 +229,13 @@ export async function createCompanionLog({ wasServerUrl, spaceId, generationId,
220
229
  didDocument: { '@context': [DID_CORE_CONTEXT], id: controllerTemplate }
221
230
  });
222
231
  if (!result.did || !result.doc) {
223
- throw new Error('companion genesis: createDID returned no DID document.');
232
+ throw new Error('client annex genesis: createDID returned no DID document.');
224
233
  }
225
234
  return { log: result.log, did: result.did, doc: result.doc };
226
235
  }
227
236
  /**
228
- * Ensures the auxiliary companion Space exists: created with the typed
229
- * Description ({@link COMPANION_SPACE_TYPE}) under the given controller when
237
+ * Ensures the auxiliary annex Space exists: created with the typed
238
+ * Description ({@link CLIENT_ANNEX_SPACE_TYPE}) under the given controller when
230
239
  * absent, verified when present. The `type` array must ride the create -- the
231
240
  * server accepts it at creation only and treats it as immutable afterwards --
232
241
  * which is also why an existing Space at this id that is NOT typed as the
@@ -242,19 +251,19 @@ export async function createCompanionLog({ wasServerUrl, spaceId, generationId,
242
251
  *
243
252
  * @param options {object}
244
253
  * @param options.was {WasClient}
245
- * @param options.spaceId {string} the auxiliary companion Space's id
254
+ * @param options.spaceId {string} the auxiliary annex Space's id
246
255
  * @param options.controller {string} the Space controller (the account
247
- * did:webvh where it exists; a bootstrap did:key on a client-less signup,
256
+ * did:webvh where it exists; a bootstrap did:key on a ladder-anchored signup,
248
257
  * promoted the same way the account Space's controller is)
249
258
  * @returns {Promise<void>}
250
259
  */
251
- export async function ensureCompanionSpace({ was, spaceId, controller }) {
260
+ export async function ensureClientAnnexSpace({ was, spaceId, controller }) {
252
261
  const space = was.space(spaceId);
253
262
  const current = await space.describe();
254
263
  if (current === null) {
255
264
  await space.configure({
256
265
  controller,
257
- type: COMPANION_SPACE_TYPE,
266
+ type: CLIENT_ANNEX_SPACE_TYPE,
258
267
  force: true
259
268
  });
260
269
  return;
@@ -262,11 +271,11 @@ export async function ensureCompanionSpace({ was, spaceId, controller }) {
262
271
  if (!current.type?.includes(DELEGATED_CLIENTS_SPACE_TYPE)) {
263
272
  throw new Error(`The Space "${spaceId}" exists but is not typed as the ` +
264
273
  'delegated-clients auxiliary Space; its type is immutable, so it ' +
265
- 'cannot hold companion generations.');
274
+ 'cannot hold client-annex generations.');
266
275
  }
267
276
  }
268
277
  /**
269
- * Mints a fresh companion generation with controller-tier signing: ensures
278
+ * Mints a fresh annex generation with controller-tier signing: ensures
270
279
  * the typed auxiliary Space, mints a fresh random generation id, creates the
271
280
  * generation collection, and publishes the genesis `did.jsonl` as a
272
281
  * create-if-absent -- the same conditional-publish discipline as every log
@@ -274,7 +283,7 @@ export async function ensureCompanionSpace({ was, spaceId, controller }) {
274
283
  * negligible.
275
284
  *
276
285
  * The account document's `#DelegatedClients` service entry is deliberately
277
- * NOT written here: the companion log publishes first, and the caller
286
+ * NOT written here: the annex log publishes first, and the caller
278
287
  * re-points the account document at the returned DID afterwards. A run torn
279
288
  * between the two leaves an unpointed generation -- authorization-inert (no
280
289
  * delegation names it), collected by the standing `gen-` prefix orphan
@@ -287,23 +296,23 @@ export async function ensureCompanionSpace({ was, spaceId, controller }) {
287
296
  *
288
297
  * @param options {object}
289
298
  * @param options.was {WasClient} the storage client, signing as an enrolled
290
- * client (or the bootstrap controller on a client-less signup)
299
+ * client (or the bootstrap controller on a ladder-anchored signup)
291
300
  * @param options.wasServerUrl {string}
292
- * @param options.spaceId {string} the auxiliary companion Space's id
301
+ * @param options.spaceId {string} the auxiliary annex Space's id
293
302
  * @param options.controller {string} the auxiliary Space's controller, used
294
303
  * only when the Space does not exist yet
295
304
  * @param options.updateKeyPublicKeyMultibase {string} the minting
296
- * credential's companion rung-0 key
305
+ * credential's annex rung-0 key
297
306
  * @param options.nextKeyHashes {string[]} every standing credential's
298
307
  * rung-0 hash, the minting credential's included
299
308
  * @param options.signer {Signer} the minting credential's rung-0 signer
300
309
  * @returns {Promise<{ did: string; generationId: string; log: DIDLog;
301
310
  * doc: DIDDoc }>}
302
311
  */
303
- export async function mintCompanionGeneration({ was, wasServerUrl, spaceId, controller, updateKeyPublicKeyMultibase, nextKeyHashes, signer }) {
304
- await ensureCompanionSpace({ was, spaceId, controller });
312
+ export async function mintClientAnnexGeneration({ was, wasServerUrl, spaceId, controller, updateKeyPublicKeyMultibase, nextKeyHashes, signer }) {
313
+ await ensureClientAnnexSpace({ was, spaceId, controller });
305
314
  const generationId = mintGenerationId();
306
- return publishCompanionGenesis({
315
+ return publishClientAnnexGenesis({
307
316
  was,
308
317
  wasServerUrl,
309
318
  spaceId,
@@ -321,25 +330,27 @@ export async function mintCompanionGeneration({ was, wasServerUrl, spaceId, cont
321
330
  * @param options {object}
322
331
  * @param options.was {WasClient}
323
332
  * @param options.wasServerUrl {string}
324
- * @param options.spaceId {string} the auxiliary companion Space's id
333
+ * @param options.spaceId {string} the auxiliary annex Space's id
325
334
  * @param options.generationId {string} the freshly minted generation id
326
335
  * @param options.updateKeyPublicKeyMultibase {string}
327
336
  * @param options.nextKeyHashes {string[]}
328
337
  * @param options.signer {Signer}
338
+ * @param [options.capability] {IZcap} an invocation capability the
339
+ * collection create and the genesis publish ride (a delegated minter)
329
340
  * @returns {Promise<{ did: string; generationId: string; log: DIDLog;
330
341
  * doc: DIDDoc }>}
331
342
  */
332
- async function publishCompanionGenesis({ was, wasServerUrl, spaceId, generationId, updateKeyPublicKeyMultibase, nextKeyHashes, signer }) {
343
+ async function publishClientAnnexGenesis({ was, wasServerUrl, spaceId, generationId, updateKeyPublicKeyMultibase, nextKeyHashes, signer, capability }) {
333
344
  // The generation collection must exist before its first resource PUT; a
334
345
  // fresh random generation id means this is always a create. Plaintext on
335
346
  // purpose:
336
- // the server resolves the companion DID out of its own storage, and the
347
+ // the server resolves the annex DID out of its own storage, and the
337
348
  // collection is capability-gated rather than encrypted.
338
349
  await was
339
- .space(spaceId)
350
+ .space(spaceId, capability !== undefined ? { capability } : {})
340
351
  .collection(generationId, { encryption: 'plaintext' })
341
352
  .configure({ name: generationId, force: true });
342
- const created = await createCompanionLog({
353
+ const created = await createClientAnnexLog({
343
354
  wasServerUrl,
344
355
  spaceId,
345
356
  generationId,
@@ -348,30 +359,35 @@ async function publishCompanionGenesis({ was, wasServerUrl, spaceId, generationI
348
359
  signer
349
360
  });
350
361
  await putLogResource({
351
- store: companionLogStore({ was, spaceId, generationId }),
362
+ store: clientAnnexLogStore({
363
+ was,
364
+ spaceId,
365
+ generationId,
366
+ ...(capability !== undefined ? { capability } : {})
367
+ }),
352
368
  log: created.log,
353
369
  ifNoneMatch: true
354
370
  });
355
371
  return { ...created, generationId };
356
372
  }
357
373
  /**
358
- * Mints a fresh companion generation signed by a standing CREDENTIAL's
359
- * companion rung 0 -- the mint a ladder-seed holder runs (a credential-in-hand
374
+ * Mints a fresh annex generation signed by a standing CREDENTIAL's
375
+ * annex rung 0 -- the mint a ladder-seed holder runs (a credential-in-hand
360
376
  * login, or a test harness standing in for one). The generation id must exist
361
377
  * before the update authority can: the rung-0 key derives from the ladder
362
- * seed AND the generation id (`companionRung`), so this helper mints the
378
+ * seed AND the generation id (`clientAnnexRung`), so this helper mints the
363
379
  * generation id
364
380
  * first, derives the rung, and states its own carry-over hash in
365
- * `nextKeyHashes` -- {@link mintCompanionGeneration}'s caller-supplied-key
381
+ * `nextKeyHashes` -- {@link mintClientAnnexGeneration}'s caller-supplied-key
366
382
  * shape cannot express that ordering. Everything else matches it: the typed
367
383
  * Space ensure, the collection create, the create-if-absent genesis publish,
368
384
  * and the pointer deliberately left to the caller.
369
385
  *
370
386
  * @param options {object}
371
387
  * @param options.was {WasClient} the storage client, signing as an enrolled
372
- * client (or the bootstrap controller on a client-less signup)
388
+ * client (or the bootstrap controller on a ladder-anchored signup)
373
389
  * @param options.wasServerUrl {string}
374
- * @param options.spaceId {string} the auxiliary companion Space's id
390
+ * @param options.spaceId {string} the auxiliary annex Space's id
375
391
  * @param options.controller {string} the auxiliary Space's controller, used
376
392
  * only when the Space does not exist yet
377
393
  * @param options.ladderSeed {Uint8Array} the minting credential's ladder
@@ -380,14 +396,22 @@ async function publishCompanionGenesis({ was, wasServerUrl, spaceId, generationI
380
396
  * credentials' rung-0 hashes for this generation id, when the account has
381
397
  * more
382
398
  * than one; the minting credential's own carry-over hash is always included
399
+ * @param [options.capability] {IZcap} an invocation capability the mint
400
+ * rides -- the transient-recovery continuation minting its fresh generation
401
+ * through the credential's sibling delegation (the auxiliary Space's items
402
+ * subtree). The typed-Space ensure is then skipped: the delegation's target
403
+ * covers the collections beneath the Space, never the Space Description,
404
+ * and a standing sibling delegation presupposes the auxiliary Space
383
405
  * @returns {Promise<{ did: string; generationId: string; log: DIDLog;
384
406
  * doc: DIDDoc }>}
385
407
  */
386
- export async function mintCredentialCompanionGeneration({ was, wasServerUrl, spaceId, controller, ladderSeed, extraNextKeyHashes = [] }) {
387
- await ensureCompanionSpace({ was, spaceId, controller });
408
+ export async function mintCredentialClientAnnexGeneration({ was, wasServerUrl, spaceId, controller, ladderSeed, extraNextKeyHashes = [], capability }) {
409
+ if (capability === undefined) {
410
+ await ensureClientAnnexSpace({ was, spaceId, controller });
411
+ }
388
412
  const generationId = mintGenerationId();
389
- const rung = await companionRung({ ladderSeed, generationId });
390
- return publishCompanionGenesis({
413
+ const rung = await clientAnnexRung({ ladderSeed, generationId });
414
+ return publishClientAnnexGenesis({
391
415
  was,
392
416
  wasServerUrl,
393
417
  spaceId,
@@ -397,14 +421,15 @@ export async function mintCredentialCompanionGeneration({ was, wasServerUrl, spa
397
421
  await deriveNextKeyHash(rung.keyMultibase),
398
422
  ...extraNextKeyHashes
399
423
  ],
400
- signer: await updateKeySigner({ seed: rung.seed })
424
+ signer: await updateKeySigner({ seed: rung.seed }),
425
+ ...(capability !== undefined ? { capability } : {})
401
426
  });
402
427
  }
403
428
  /**
404
429
  * The type IRI of the account document's delegated-clients service entry --
405
- * the pointer at the current companion generation's DID. Wire-level and
430
+ * the pointer at the current annex generation's DID. Wire-level and
406
431
  * permanent: readers (this module's {@link delegatedClientsPointer}, the
407
- * server's companion-chain inspector clause) dispatch on this IRI, never on
432
+ * server's annex-chain inspector clause) dispatch on this IRI, never on
408
433
  * the entry's fragment id, which is non-semantic by convention.
409
434
  */
410
435
  export const DELEGATED_CLIENTS_SERVICE_TYPE = 'https://w3id.org/byoe#DelegatedClients';
@@ -417,25 +442,25 @@ export const DELEGATED_CLIENTS_SERVICE_TYPE = 'https://w3id.org/byoe#DelegatedCl
417
442
  const DELEGATED_CLIENTS_SERVICE_FRAGMENT = 'delegated-clients';
418
443
  /**
419
444
  * Builds a fresh delegated-clients service entry for the account document.
420
- * The `serviceEndpoint` is the companion DID STRING, deliberately not a URL:
445
+ * The `serviceEndpoint` is the annex DID STRING, deliberately not a URL:
421
446
  * the DID is self-certifying and host-independent, and the account pointer
422
447
  * already carries the host.
423
448
  *
424
449
  * @param options {object}
425
450
  * @param options.accountDid {string} the account did:webvh
426
- * @param options.companionDid {string} the current generation's companion
451
+ * @param options.clientAnnexDid {string} the current generation's annex
427
452
  * DID
428
453
  * @returns {ServiceEndpoint}
429
454
  */
430
- export function delegatedClientsServiceEntry({ accountDid, companionDid }) {
455
+ export function delegatedClientsServiceEntry({ accountDid, clientAnnexDid }) {
431
456
  return {
432
457
  id: `${accountDid}#${DELEGATED_CLIENTS_SERVICE_FRAGMENT}`,
433
458
  type: DELEGATED_CLIENTS_SERVICE_TYPE,
434
- serviceEndpoint: companionDid
459
+ serviceEndpoint: clientAnnexDid
435
460
  };
436
461
  }
437
462
  /**
438
- * The companion DID the account document currently points at: the
463
+ * The annex DID the account document currently points at: the
439
464
  * `serviceEndpoint` of the service entry whose `type` names (or includes)
440
465
  * {@link DELEGATED_CLIENTS_SERVICE_TYPE}. Only a bare DID-string endpoint
441
466
  * counts -- the same predicate the server's inspector clause evaluates, so
@@ -455,9 +480,42 @@ export function delegatedClientsPointer({ doc }) {
455
480
  }
456
481
  return undefined;
457
482
  }
483
+ /**
484
+ * The account document's `service` array with the delegated-clients pointer
485
+ * set to `clientAnnexDid`. An existing pointer entry is re-pointed in place,
486
+ * its fragment id preserved verbatim (the id is non-semantic and stable);
487
+ * absent one, a fresh entry is appended. Every other service entry is carried
488
+ * through untouched.
489
+ *
490
+ * Shared by the two writers of the pointer: the standalone
491
+ * {@link setDelegatedClientsPointer} entry, and the transient-recovery
492
+ * continuation, which folds the pointer into its own add-and-retire entry so
493
+ * the pointer can never lag the entry that retires the standing ladder VMs.
494
+ *
495
+ * @param options {object}
496
+ * @param options.doc {DIDDoc} the current account document
497
+ * @param options.accountDid {string} the account did:webvh
498
+ * @param options.clientAnnexDid {string} the generation to point at
499
+ * @returns {ServiceEndpoint[]}
500
+ */
501
+ export function servicesPointedAtClientAnnex({ doc, accountDid, clientAnnexDid }) {
502
+ const existing = (doc.service ?? []);
503
+ const isPointerEntry = (entry) => {
504
+ const types = Array.isArray(entry.type) ? entry.type : [entry.type];
505
+ return types.includes(DELEGATED_CLIENTS_SERVICE_TYPE);
506
+ };
507
+ return existing.some(isPointerEntry)
508
+ ? existing.map(entry => isPointerEntry(entry)
509
+ ? { ...entry, serviceEndpoint: clientAnnexDid }
510
+ : entry)
511
+ : [
512
+ ...existing,
513
+ delegatedClientsServiceEntry({ accountDid, clientAnnexDid })
514
+ ];
515
+ }
458
516
  /**
459
517
  * The unlock-record sibling delegation's `allowedAction` set: GET beside PUT,
460
- * so an enrolling transient client can read the companion head it appends to.
518
+ * so an enrolling transient client can read the annex head it appends to.
461
519
  * Wire-level and permanent (wallet-core decision 0005): the server's
462
520
  * inspector clause admits a delegated-clients delegation with `allowedAction`
463
521
  * a subset of exactly this pair.
@@ -471,13 +529,13 @@ export const DELEGATED_CLIENTS_DELEGATION_ACTIONS = ['GET', 'PUT'];
471
529
  */
472
530
  export const DELEGATED_CLIENTS_DELEGATION_TTL_MS = STANDING_ZCAP_TTL_MS;
473
531
  /**
474
- * Mints one delegated-clients (companion-Space) delegation: the pre-minted
532
+ * Mints one delegated-clients (annex Space) delegation: the pre-minted
475
533
  * zcap sealed into a standing credential's unlock record beside the account
476
- * bridge, which is what lets a transient login reach the companion log with
534
+ * bridge, which is what lets a transient login reach the annex log with
477
535
  * nothing but the credential. The shape is a permanent wire artifact
478
536
  * (wallet-core decision 0005):
479
537
  *
480
- * - `invocationTarget` is the AUXILIARY companion Space's items subtree --
538
+ * - `invocationTarget` is the AUXILIARY annex Space's items subtree --
481
539
  * the Space URL with a trailing slash, built with was-client's paths
482
540
  * helpers so the bytes match the server's target check on a sub-path
483
541
  * deployment. Generation coverage comes from generation-id-bounded
@@ -495,22 +553,22 @@ export const DELEGATED_CLIENTS_DELEGATION_TTL_MS = STANDING_ZCAP_TTL_MS;
495
553
  * enrolled client's promoted signer, or the account ladder VM)
496
554
  * @param options.wasServerUrl {string} the auxiliary Space's storage
497
555
  * server (the account pointer's host)
498
- * @param options.companionSpaceId {string} the auxiliary companion
556
+ * @param options.clientAnnexSpaceId {string} the auxiliary annex
499
557
  * Space's id
500
558
  * @param options.controller {string} the credential-derived signing DID
501
559
  * @param [options.now] {number} epoch milliseconds, for tests
502
560
  * @returns {Promise<IZcap>}
503
561
  */
504
- export async function mintDelegatedClientsDelegation({ zcapClient, wasServerUrl, companionSpaceId, controller, now = Date.now() }) {
562
+ export async function mintDelegatedClientsDelegation({ zcapClient, wasServerUrl, clientAnnexSpaceId, controller, now = Date.now() }) {
505
563
  const spaceUrl = toUrl({
506
564
  serverUrl: wasServerUrl,
507
- path: spacePath(companionSpaceId)
565
+ path: spacePath(clientAnnexSpaceId)
508
566
  });
509
567
  return (await zcapClient.delegate({
510
568
  capability: rootCapabilityId(spaceUrl),
511
569
  invocationTarget: toUrl({
512
570
  serverUrl: wasServerUrl,
513
- path: spaceItems(companionSpaceId)
571
+ path: spaceItems(clientAnnexSpaceId)
514
572
  }),
515
573
  controller,
516
574
  allowedActions: [...DELEGATED_CLIENTS_DELEGATION_ACTIONS],
@@ -518,9 +576,51 @@ export async function mintDelegatedClientsDelegation({ zcapClient, wasServerUrl,
518
576
  }));
519
577
  }
520
578
  /**
521
- * The auxiliary companion Space id a delegated-clients delegation targets,
579
+ * Builds the annex-side sibling-delegation minter the durable record re-mint
580
+ * orchestrator (`recovery/remintRecoveryDelegations`) takes as an injected
581
+ * closure -- the boundary keeping that base orchestrator free of annex
582
+ * imports. The returned closure reads the auxiliary annex Space id off the
583
+ * verified document's delegated-clients service entry (the annex DID string
584
+ * embeds it) and mints a fresh {@link mintDelegatedClientsDelegation} to the
585
+ * named controller; it resolves `undefined` while the document points at no
586
+ * generation, which the orchestrator reads as "carry the old sealed member
587
+ * verbatim".
588
+ *
589
+ * @param options {object}
590
+ * @param options.doc {object} the locally verified account document
591
+ * @param options.zcapClient {ZcapClient} the acting client's promoted
592
+ * signer, which mints the fresh delegations
593
+ * @param options.wasServerUrl {string} the auxiliary Space's storage
594
+ * server (the account pointer's host)
595
+ * @returns {Function} `({ controller }) => Promise<IZcap | undefined>`
596
+ */
597
+ export function delegatedClientsDelegationMinter({ doc, zcapClient, wasServerUrl }) {
598
+ return async ({ controller }) => {
599
+ const clientAnnexDid = delegatedClientsPointer({
600
+ doc: doc
601
+ });
602
+ if (!clientAnnexDid) {
603
+ return undefined;
604
+ }
605
+ let clientAnnexSpaceId;
606
+ try {
607
+ clientAnnexSpaceId = clientAnnexDidParts({ did: clientAnnexDid }).spaceId;
608
+ }
609
+ catch {
610
+ return undefined;
611
+ }
612
+ return mintDelegatedClientsDelegation({
613
+ zcapClient,
614
+ wasServerUrl,
615
+ clientAnnexSpaceId,
616
+ controller
617
+ });
618
+ };
619
+ }
620
+ /**
621
+ * The auxiliary annex Space id a delegated-clients delegation targets,
522
622
  * read out of its `invocationTarget` (the items-subtree URL,
523
- * `.../space/<companionSpaceId>/`). The id has no other home -- a transient
623
+ * `.../space/<clientAnnexSpaceId>/`). The id has no other home -- a transient
524
624
  * login learns the Space from the delegation it unwraps, and a refresh pass
525
625
  * that holds the old delegation rebuilds the target from it -- so this parse
526
626
  * is the one reader. Returns `undefined` on anything that is not an
@@ -554,7 +654,7 @@ export function delegatedClientsDelegationSpaceId({ delegation }) {
554
654
  return spaceId ? decodeURIComponent(spaceId) : undefined;
555
655
  }
556
656
  /**
557
- * The type IRI of the companion document's generation-delegation service
657
+ * The type IRI of the annex document's generation-delegation service
558
658
  * entry -- the generation's standing Space-scoped zcap, embedded where an
559
659
  * enrolling transient client can reach it before it holds any other
560
660
  * authority. Wire-level and permanent: readers (this module's
@@ -607,8 +707,8 @@ export const GENERATION_DELEGATION_TTL_MS = STANDING_ZCAP_TTL_MS;
607
707
  * bytes match the server's target check on a sub-path deployment. The
608
708
  * bare Space URL sits outside the capability bytes (see
609
709
  * {@link GENERATION_DELEGATION_ACTIONS} for what that excludes).
610
- * - `controller` is the bare companion DID string. Transient keys invoke as
611
- * `<companionDid>#<vm>`, and the server's inspector clause compares this
710
+ * - `controller` is the bare annex DID string. Transient keys invoke as
711
+ * `<clientAnnexDid>#<vm>`, and the server's inspector clause compares this
612
712
  * string against the account document's delegated-clients pointer.
613
713
  * - The chain is rooted directly in the account Space's root zcap, so an
614
714
  * App Connect grant delegated under it forms the depth-3 chain
@@ -624,12 +724,12 @@ export const GENERATION_DELEGATION_TTL_MS = STANDING_ZCAP_TTL_MS;
624
724
  * or a durable client's promoted signer)
625
725
  * @param options.wasServerUrl {string} the ACCOUNT Space's storage server
626
726
  * @param options.spaceId {string} the ACCOUNT Space's id
627
- * @param options.companionDid {string} the generation's companion DID
727
+ * @param options.clientAnnexDid {string} the generation's annex DID
628
728
  * @param [options.now] {number} epoch milliseconds, for tests
629
729
  * @returns {Promise<IZcap>}
630
730
  */
631
- export async function mintGenerationDelegation({ zcapClient, wasServerUrl, spaceId, companionDid, now = Date.now() }) {
632
- companionDidParts({ did: companionDid });
731
+ export async function mintGenerationDelegation({ zcapClient, wasServerUrl, spaceId, clientAnnexDid, now = Date.now() }) {
732
+ clientAnnexDidParts({ did: clientAnnexDid });
633
733
  const spaceUrl = toUrl({ serverUrl: wasServerUrl, path: spacePath(spaceId) });
634
734
  return (await zcapClient.delegate({
635
735
  capability: rootCapabilityId(spaceUrl),
@@ -637,7 +737,7 @@ export async function mintGenerationDelegation({ zcapClient, wasServerUrl, space
637
737
  serverUrl: wasServerUrl,
638
738
  path: spaceItems(spaceId)
639
739
  }),
640
- controller: companionDid,
740
+ controller: clientAnnexDid,
641
741
  allowedActions: [...GENERATION_DELEGATION_ACTIONS],
642
742
  expires: new Date(now + GENERATION_DELEGATION_TTL_MS)
643
743
  }));
@@ -667,33 +767,33 @@ export function clampGrantExpires({ ttlMs, delegation, now = Date.now() }) {
667
767
  return new Date(Math.min(now + ttlMs, parentExpires));
668
768
  }
669
769
  /**
670
- * Builds a fresh generation-delegation service entry for a companion
770
+ * Builds a fresh generation-delegation service entry for an annex
671
771
  * document. The `serviceEndpoint` is the full delegated-zcap JSON as a
672
772
  * single map, byte-identical to what `zcapClient.delegate` produced -- the
673
- * companion entry proof (JCS canonicalization) then covers it byte for byte,
773
+ * annex entry proof (JCS canonicalization) then covers it byte for byte,
674
774
  * so host tampering with the stored delegation is client-visible.
675
775
  *
676
776
  * @param options {object}
677
- * @param options.companionDid {string} the generation's companion DID
777
+ * @param options.clientAnnexDid {string} the generation's annex DID
678
778
  * @param options.delegation {IZcap} the minted generation delegation
679
779
  * @returns {ServiceEndpoint}
680
780
  */
681
- export function generationDelegationServiceEntry({ companionDid, delegation }) {
781
+ export function generationDelegationServiceEntry({ clientAnnexDid, delegation }) {
682
782
  return {
683
- id: `${companionDid}#${GENERATION_DELEGATION_SERVICE_FRAGMENT}`,
783
+ id: `${clientAnnexDid}#${GENERATION_DELEGATION_SERVICE_FRAGMENT}`,
684
784
  type: GENERATION_DELEGATION_SERVICE_TYPE,
685
785
  serviceEndpoint: delegation
686
786
  };
687
787
  }
688
788
  /**
689
- * The generation delegation a companion document carries: the
789
+ * The generation delegation an annex document carries: the
690
790
  * `serviceEndpoint` map of the service entry whose `type` names (or
691
791
  * includes) {@link GENERATION_DELEGATION_SERVICE_TYPE}. Only a map-form
692
792
  * endpoint counts (the delegation is embedded as the zcap JSON itself,
693
793
  * never as a URL or an encoded string).
694
794
  *
695
795
  * @param options {object}
696
- * @param options.doc {DIDDoc} the resolved (and verified) companion
796
+ * @param options.doc {DIDDoc} the resolved (and verified) annex
697
797
  * document
698
798
  * @returns {IZcap | undefined}
699
799
  */
@@ -712,18 +812,77 @@ export function embeddedGenerationDelegation({ doc }) {
712
812
  return undefined;
713
813
  }
714
814
  /**
715
- * The companion document's service list with the generation delegation
815
+ * Every generation delegation a generation's log has ever embedded, in log
816
+ * order and deduplicated by zcap id -- the annex-log HISTORY WALK the
817
+ * last-durable-client forget revokes from (decision 0004's 2026-08-19
818
+ * amendment): a renewal replaces the head service entry's endpoint in place,
819
+ * so a superseded delegation's bytes survive only in earlier entries'
820
+ * re-stated full state, and a renewal inside the 30-day window can leave TWO
821
+ * still-unexpired ladder-signed delegations. The caller filters (signer,
822
+ * expiry) and revokes; this walk only recovers the bytes.
823
+ *
824
+ * @param options {object}
825
+ * @param options.log {DIDLog} the generation's VERIFIED log
826
+ * @returns {IZcap[]}
827
+ */
828
+ export function generationDelegationHistory({ log }) {
829
+ const seen = new Set();
830
+ const delegations = [];
831
+ for (const entry of log) {
832
+ const embedded = embeddedGenerationDelegation({
833
+ doc: entry.state
834
+ });
835
+ if (embedded === undefined) {
836
+ continue;
837
+ }
838
+ const id = embedded.id;
839
+ if (typeof id !== 'string' || seen.has(id)) {
840
+ continue;
841
+ }
842
+ seen.add(id);
843
+ delegations.push(embedded);
844
+ }
845
+ return delegations;
846
+ }
847
+ /**
848
+ * Submits the revocation of a generation delegation, reading the server's
849
+ * 400 answer as success: an already-revoked chain (a resumed ceremony's
850
+ * blind re-POST) and an expired delegation (which no longer needs revoking)
851
+ * both land there, and the revocation protocol exposes no read endpoint to
852
+ * distinguish them beforehand. Matched on `err.name` -- error classes do not
853
+ * survive crossing package copies. The `revoke` seam is was-client's
854
+ * `WasClient#revoke`, bound by the caller.
855
+ *
856
+ * @param options {object}
857
+ * @param options.revoke {Function} `(delegation) => Promise<void>` --
858
+ * POSTs the revocation (`was.revoke`)
859
+ * @param options.delegation {IZcap}
860
+ * @returns {Promise<void>}
861
+ */
862
+ export async function revokeTreatingAlreadyRevokedAsSuccess({ revoke, delegation }) {
863
+ try {
864
+ await revoke(delegation);
865
+ }
866
+ catch (err) {
867
+ if (err.name === 'ValidationError') {
868
+ return;
869
+ }
870
+ throw err;
871
+ }
872
+ }
873
+ /**
874
+ * The annex document's service list with the generation delegation
716
875
  * installed: an existing entry's endpoint is replaced in place, its fragment
717
876
  * id preserved verbatim (the id is non-semantic and stable); absent one, a
718
877
  * fresh entry is appended. Every other service entry is preserved untouched.
719
878
  *
720
879
  * @param options {object}
721
- * @param options.doc {DIDDoc} the companion document as published
722
- * @param options.companionDid {string}
880
+ * @param options.doc {DIDDoc} the annex document as published
881
+ * @param options.clientAnnexDid {string}
723
882
  * @param options.delegation {IZcap}
724
883
  * @returns {ServiceEndpoint[]}
725
884
  */
726
- function withGenerationDelegationEntry({ doc, companionDid, delegation }) {
885
+ function withGenerationDelegationEntry({ doc, clientAnnexDid, delegation }) {
727
886
  const existing = (doc.service ?? []);
728
887
  const isDelegationEntry = (entry) => {
729
888
  const types = Array.isArray(entry.type) ? entry.type : [entry.type];
@@ -738,23 +897,23 @@ function withGenerationDelegationEntry({ doc, companionDid, delegation }) {
738
897
  : entry)
739
898
  : [
740
899
  ...existing,
741
- generationDelegationServiceEntry({ companionDid, delegation })
900
+ generationDelegationServiceEntry({ clientAnnexDid, delegation })
742
901
  ];
743
902
  }
744
903
  /**
745
- * Parses the auxiliary Space id and generation id out of a companion DID
746
- * string. Both are permanent substrings of every companion DID by
747
- * construction: the generation id is the final path segment of the companion
904
+ * Parses the auxiliary Space id and generation id out of an annex DID
905
+ * string. Both are permanent substrings of every annex DID by
906
+ * construction: the generation id is the final path segment of the annex
748
907
  * DID (`did:webvh:<scid>:<host>:...:space:<spaceId>:<generationId>`), and it
749
- * is the generation-identifying half of the companion rung HKDF
908
+ * is the generation-identifying half of the annex rung HKDF
750
909
  * labels, so this parse is what lets an enrollee derive its writing key from
751
910
  * the pointer alone -- no log read, no registry.
752
911
  *
753
912
  * @param options {object}
754
- * @param options.did {string} a companion did:webvh string
913
+ * @param options.did {string} an annex did:webvh string
755
914
  * @returns {{ spaceId: string, generationId: string }}
756
915
  */
757
- export function companionDidParts({ did }) {
916
+ export function clientAnnexDidParts({ did }) {
758
917
  const parts = did.split(':');
759
918
  const generationId = parts[parts.length - 1];
760
919
  const spaceId = parts[parts.length - 2];
@@ -765,39 +924,39 @@ export function companionDidParts({ did }) {
765
924
  generationId === undefined ||
766
925
  spaceId === undefined ||
767
926
  spaceId.length === 0) {
768
- throw new Error(`Not a companion did:webvh: "${did}".`);
927
+ throw new Error(`Not a client annex did:webvh: "${did}".`);
769
928
  }
770
929
  assertGenerationId(generationId);
771
930
  return { spaceId, generationId };
772
931
  }
773
932
  /**
774
- * Thrown when the published companion log commits neither the writing
933
+ * Thrown when the published annex log commits neither the writing
775
934
  * credential's rung-0 key nor its hash -- the mid-generation lockout: a
776
- * credential bound after the generation's genesis cannot write the companion
935
+ * credential bound after the generation's genesis cannot write the annex
777
936
  * until an existing writer commits its rung-0 hash or the next GC swap's
778
937
  * genesis does. Typed so callers can map it to the fresh-generation path
779
938
  * where one is licensed (the transient-recovery continuation) or to honest
780
939
  * copy where none is.
781
940
  */
782
- export class CompanionRungUncommittedError extends Error {
941
+ export class ClientAnnexRungUncommittedError extends Error {
783
942
  constructor(message) {
784
943
  super(message);
785
- this.name = 'CompanionRungUncommittedError';
944
+ this.name = 'ClientAnnexRungUncommittedError';
786
945
  }
787
946
  }
788
947
  /**
789
- * Reads and resolves the published companion log through the narrow seam, or
948
+ * Reads and resolves the published annex log through the narrow seam, or
790
949
  * throws when the generation's `did.jsonl` is missing (an unpointed or
791
950
  * deleted generation -- nothing to enroll into).
792
951
  *
793
952
  * @param options {object}
794
- * @param options.store {CompanionWriteStore}
953
+ * @param options.store {ClientAnnexWriteStore}
795
954
  * @param [options.expectedDid] {string}
796
955
  * @param [options.pinStore] {ResourceLogPinStore}
797
956
  * @param [options.logId] {string}
798
957
  * @returns {Promise<PublishedWebvhLog>}
799
958
  */
800
- async function readCompanionLogOrThrow({ store, expectedDid, pinStore, logId }) {
959
+ async function readClientAnnexLogOrThrow({ store, expectedDid, pinStore, logId }) {
801
960
  // readPublishedLog only calls getIdResourceRaw, so the narrow seam is safe.
802
961
  const published = await readPublishedLog({
803
962
  idStore: store,
@@ -806,19 +965,19 @@ async function readCompanionLogOrThrow({ store, expectedDid, pinStore, logId })
806
965
  ...(logId !== undefined ? { logId } : {})
807
966
  });
808
967
  if (!published) {
809
- throw new Error('companion: did.jsonl is missing; the generation was never minted or ' +
968
+ throw new Error('client annex: did.jsonl is missing; the generation was never minted or ' +
810
969
  'has been collected.');
811
970
  }
812
971
  return published;
813
972
  }
814
973
  /**
815
974
  * TRANSIENT ENROLLMENT: publishes one per-visit verification method into a
816
- * companion generation's log -- one atomic entry, signed by the writing
975
+ * annex generation's log -- one atomic entry, signed by the writing
817
976
  * credential's static rung 0 (derived from the ladder seed and the generation
818
977
  * id;
819
- * see `companionRung`). The entry:
978
+ * see `clientAnnexRung`). The entry:
820
979
  *
821
- * - reveals the writer's rung-0 key into `updateKeys` at its first companion
980
+ * - reveals the writer's rung-0 key into `updateKeys` at its first annex
822
981
  * write (later writes re-state it unchanged);
823
982
  * - re-states `nextKeyHashes` verbatim -- every standing credential's rung-0
824
983
  * hash, the writer's own carry-over hash included -- explicitly on the
@@ -827,7 +986,7 @@ async function readCompanionLogOrThrow({ store, expectedDid, pinStore, logId })
827
986
  * relationship arrays stated explicitly (no `authentication`, no
828
987
  * `assertionMethod`, no `keyAgreement` twin -- the DIDAuth path signs as
829
988
  * the bare did:key, and the controller-marker convention does not arise in
830
- * the companion at all).
989
+ * the annex at all).
831
990
  *
832
991
  * The transient key set carries no update key, and nothing here touches the
833
992
  * ACCOUNT log's `updateKeys` or `nextKeyHashes`. There is no two-entry
@@ -836,12 +995,12 @@ async function readCompanionLogOrThrow({ store, expectedDid, pinStore, logId })
836
995
  * published document's own state -- a VM already present is a no-op.
837
996
  *
838
997
  * A writer whose rung-0 key is neither revealed nor committed is refused
839
- * ({@link CompanionRungUncommittedError}): companion entries verify against
998
+ * ({@link ClientAnnexRungUncommittedError}): annex entries verify against
840
999
  * the log's own hash-commitment chain, so no admission rule can make an
841
1000
  * uncommitted key verify mid-log.
842
1001
  *
843
1002
  * @param options {object}
844
- * @param options.store {CompanionWriteStore} the generation's log store
1003
+ * @param options.store {ClientAnnexWriteStore} the generation's log store
845
1004
  * (delegated through the credential's sibling delegation, or
846
1005
  * controller-tier)
847
1006
  * @param options.ladderSeed {Uint8Array} the credential's ladder seed, from
@@ -849,41 +1008,41 @@ async function readCompanionLogOrThrow({ store, expectedDid, pinStore, logId })
849
1008
  * @param options.generationId {string} the generation collection's name
850
1009
  * @param options.transientKeyMultibase {string} the visit's in-memory
851
1010
  * Ed25519 signing key, public multibase
852
- * @param [options.services] {ServiceEndpoint[]} the companion document's
1011
+ * @param [options.services] {ServiceEndpoint[]} the annex document's
853
1012
  * full service-entry list, replacing the published one wholesale; omitted,
854
1013
  * the prior entries are preserved verbatim (or extended by
855
1014
  * `mintGenerationDelegation` below). Supplying both is refused in favor of
856
1015
  * the explicit list
857
1016
  * @param [options.mintGenerationDelegation] {Function}
858
- * `({ companionDid }) => Promise<IZcap>` -- mints the generation
1017
+ * `({ clientAnnexDid }) => Promise<IZcap>` -- mints the generation
859
1018
  * delegation this entry installs when it publishes the generation's FIRST
860
1019
  * transient verification method (and the document carries no delegation
861
1020
  * entry yet). Never invoked otherwise: the delegation is installed with
862
1021
  * the first transient VM or by the GC ceremony's own install stage, never
863
1022
  * by genesis (a genesis-embedded signed zcap can never verify -- its
864
1023
  * `controller` embeds the SCID the genesis hash derives from)
865
- * @param [options.expectedDid] {string} the companion DID the log must
1024
+ * @param [options.expectedDid] {string} the annex DID the log must
866
1025
  * resolve to, from the account document's pointer
867
1026
  * @param [options.pinStore] {ResourceLogPinStore} chain-head pins (a
868
1027
  * transient session passes an in-memory store)
869
1028
  * @param [options.logId] {string} the generation's pin-slot key, from
870
- * {@link companionLogPinId}; required whenever a `pinStore` is supplied
1029
+ * {@link clientAnnexLogPinId}; required whenever a `pinStore` is supplied
871
1030
  * @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog }>}
872
1031
  */
873
- export async function enrollCompanionTransientClient(options) {
874
- return withLogConflictRetry(() => enrollCompanionTransientClientOnce(options));
1032
+ export async function enrollClientAnnexTransientClient(options) {
1033
+ return withLogConflictRetry(() => enrollClientAnnexTransientClientOnce(options));
875
1034
  }
876
1035
  /**
877
- * One attempt of {@link enrollCompanionTransientClient}, re-invoked by the
1036
+ * One attempt of {@link enrollClientAnnexTransientClient}, re-invoked by the
878
1037
  * conflict retry (with the same signing key -- static rung 0 has no
879
1038
  * advanced-rung retry shape).
880
1039
  *
881
- * @param options {object} see {@link enrollCompanionTransientClient}
1040
+ * @param options {object} see {@link enrollClientAnnexTransientClient}
882
1041
  * @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog }>}
883
1042
  */
884
- async function enrollCompanionTransientClientOnce({ store, ladderSeed, generationId, transientKeyMultibase, services, mintGenerationDelegation: mintDelegation, expectedDid, pinStore, logId }) {
1043
+ async function enrollClientAnnexTransientClientOnce({ store, ladderSeed, generationId, transientKeyMultibase, services, mintGenerationDelegation: mintDelegation, expectedDid, pinStore, logId }) {
885
1044
  assertGenerationId(generationId);
886
- const published = await readCompanionLogOrThrow({
1045
+ const published = await readClientAnnexLogOrThrow({
887
1046
  store,
888
1047
  ...(expectedDid !== undefined ? { expectedDid } : {}),
889
1048
  ...(pinStore !== undefined ? { pinStore } : {}),
@@ -898,13 +1057,13 @@ async function enrollCompanionTransientClientOnce({ store, ladderSeed, generatio
898
1057
  if (existingMethods.some(method => method.id === vmId)) {
899
1058
  return { did, doc, log: published.log };
900
1059
  }
901
- const rung = await companionRung({ ladderSeed, generationId });
1060
+ const rung = await clientAnnexRung({ ladderSeed, generationId });
902
1061
  const rungHash = await deriveNextKeyHash(rung.keyMultibase);
903
1062
  const revealed = published.updateKeys.includes(rung.keyMultibase);
904
1063
  if (!revealed && !published.nextKeyHashes.includes(rungHash)) {
905
- throw new CompanionRungUncommittedError("companion: the log commits neither this credential's rung-0 key nor " +
1064
+ throw new ClientAnnexRungUncommittedError("client annex: the log commits neither this credential's rung-0 key nor " +
906
1065
  'its hash; a credential bound mid-generation cannot write the ' +
907
- 'companion until a writer commits its hash or the next GC swap does.');
1066
+ 'annex until a writer commits its hash or the next GC swap does.');
908
1067
  }
909
1068
  // A non-rotating entry re-states `updateKeys`, which the resolver checks
910
1069
  // against the previous entry's commitments -- genesis enforces the
@@ -918,10 +1077,10 @@ async function enrollCompanionTransientClientOnce({ store, ladderSeed, generatio
918
1077
  mintDelegation !== undefined &&
919
1078
  existingMethods.length === 0 &&
920
1079
  embeddedGenerationDelegation({ doc }) === undefined) {
921
- const delegation = await mintDelegation({ companionDid: did });
1080
+ const delegation = await mintDelegation({ clientAnnexDid: did });
922
1081
  services = withGenerationDelegationEntry({
923
1082
  doc,
924
- companionDid: did,
1083
+ clientAnnexDid: did,
925
1084
  delegation
926
1085
  });
927
1086
  }
@@ -955,17 +1114,17 @@ async function enrollCompanionTransientClientOnce({ store, ladderSeed, generatio
955
1114
  capabilityDelegation: relationIds(doc.capabilityDelegation),
956
1115
  ...(services !== undefined ? { services } : {})
957
1116
  });
958
- // The log only -- a companion has no did:web projection -- conditional on
1117
+ // The log only -- an annex has no did:web projection -- conditional on
959
1118
  // the read this entry was built on.
960
1119
  await putLogResource({ store, log: updated.log, ifMatch: published.etag });
961
1120
  return { did: updated.did, doc: updated.doc, log: updated.log };
962
1121
  }
963
1122
  /**
964
1123
  * Points the account document's delegated-clients service entry at a
965
- * companion DID -- the first install after a generation's genesis, and the GC
1124
+ * annex DID -- the first install after a generation's genesis, and the GC
966
1125
  * swap's re-point alike. One ordinary document-update entry, signed by an
967
- * enrolled durable client's active update key; the companion log always
968
- * publishes FIRST (see {@link mintCompanionGeneration}), so a tear leaves an
1126
+ * enrolled durable client's active update key; the annex log always
1127
+ * publishes FIRST (see {@link mintClientAnnexGeneration}), so a tear leaves an
969
1128
  * unpointed, authorization-inert generation, never a dangling pointer.
970
1129
  *
971
1130
  * An existing delegated-clients entry is re-pointed in place, its fragment id
@@ -976,10 +1135,14 @@ async function enrollCompanionTransientClientOnce({ store, ladderSeed, generatio
976
1135
  * a no-op on the log (it still heals a lagging `did.json`).
977
1136
  *
978
1137
  * @param options {object}
979
- * @param options.idStore {WebvhIdStore} the ACCOUNT log's store
1138
+ * @param options.idStore {WebvhIdStore} the ACCOUNT log's store; with
1139
+ * `logOnly`, only its log read and `did.jsonl` PUT are used, so the narrow
1140
+ * delegated seam satisfies it
980
1141
  * @param options.updateKeys {ClientWebvhUpdateKeys} this durable client's
981
- * update-key seeds
982
- * @param options.companionDid {string} the generation to point at
1142
+ * update-key seeds -- or the ladder-rung idiom on a ladder-anchored
1143
+ * account (`{ updateSeed: rung0.seed, stagedSeed: rung1.seed }`), as the
1144
+ * credential-anchored genesis and the transient-recovery continuation pass
1145
+ * @param options.clientAnnexDid {string} the generation to point at
983
1146
  * @param [options.expectedDid] {string} the account DID the log must
984
1147
  * resolve to, from the account pointer
985
1148
  * @param [options.pinStore] {ResourceLogPinStore} this client's chain-head
@@ -987,6 +1150,11 @@ async function enrollCompanionTransientClientOnce({ store, ladderSeed, generatio
987
1150
  * @param [options.logId] {string} the account log's pin-slot key, from
988
1151
  * `accountLogPinId({ spaceId })`; required whenever a `pinStore` is
989
1152
  * supplied
1153
+ * @param [options.logOnly] {boolean} publish `did.jsonl` only, never the
1154
+ * `did.json` projection -- the transient-recovery continuation writing
1155
+ * through the record's bridge delegation, whose narrow scope covers nothing
1156
+ * but the log. The projection heals at the next authorized write (the log
1157
+ * is the source of truth)
990
1158
  * @returns {Promise<{ did: string, doc: DIDDoc }>}
991
1159
  */
992
1160
  export async function setDelegatedClientsPointer(options) {
@@ -999,9 +1167,9 @@ export async function setDelegatedClientsPointer(options) {
999
1167
  * @param options {object} see {@link setDelegatedClientsPointer}
1000
1168
  * @returns {Promise<{ did: string, doc: DIDDoc }>}
1001
1169
  */
1002
- async function setDelegatedClientsPointerOnce({ idStore, updateKeys, companionDid, expectedDid, pinStore, logId }) {
1170
+ async function setDelegatedClientsPointerOnce({ idStore, updateKeys, clientAnnexDid, expectedDid, pinStore, logId, logOnly = false }) {
1003
1171
  // Refuses a malformed target before anything is read or written.
1004
- companionDidParts({ did: companionDid });
1172
+ clientAnnexDidParts({ did: clientAnnexDid });
1005
1173
  const published = await readPublishedLog({
1006
1174
  idStore,
1007
1175
  ...(expectedDid !== undefined ? { expectedDid } : {}),
@@ -1009,11 +1177,13 @@ async function setDelegatedClientsPointerOnce({ idStore, updateKeys, companionDi
1009
1177
  ...(logId !== undefined ? { logId } : {})
1010
1178
  });
1011
1179
  if (!published) {
1012
- throw new Error('did:webvh: did.jsonl is missing; nothing to point at a companion.');
1180
+ throw new Error('did:webvh: did.jsonl is missing; nothing to point at a client annex.');
1013
1181
  }
1014
1182
  const { did, doc } = published;
1015
- if (delegatedClientsPointer({ doc }) === companionDid) {
1016
- await concludeWithPublishedLog({ idStore, published });
1183
+ if (delegatedClientsPointer({ doc }) === clientAnnexDid) {
1184
+ if (!logOnly) {
1185
+ await concludeWithPublishedLog({ idStore, published });
1186
+ }
1017
1187
  return { did, doc };
1018
1188
  }
1019
1189
  // The entry is signed by this client's active update key; a log that does
@@ -1025,19 +1195,11 @@ async function setDelegatedClientsPointerOnce({ idStore, updateKeys, companionDi
1025
1195
  'delegated-clients entry.');
1026
1196
  }
1027
1197
  await assertCarryOverCommitments({ published });
1028
- const existing = (doc.service ?? []);
1029
- const isPointerEntry = (entry) => {
1030
- const types = Array.isArray(entry.type) ? entry.type : [entry.type];
1031
- return types.includes(DELEGATED_CLIENTS_SERVICE_TYPE);
1032
- };
1033
- const services = existing.some(isPointerEntry)
1034
- ? existing.map(entry => isPointerEntry(entry)
1035
- ? { ...entry, serviceEndpoint: companionDid }
1036
- : entry)
1037
- : [
1038
- ...existing,
1039
- delegatedClientsServiceEntry({ accountDid: did, companionDid })
1040
- ];
1198
+ const services = servicesPointedAtClientAnnex({
1199
+ doc,
1200
+ accountDid: did,
1201
+ clientAnnexDid
1202
+ });
1041
1203
  const signer = await updateKeySigner({ seed: updateKeys.updateSeed });
1042
1204
  const updated = await updateDID({
1043
1205
  log: published.log,
@@ -1050,7 +1212,16 @@ async function setDelegatedClientsPointerOnce({ idStore, updateKeys, companionDi
1050
1212
  nextKeyHashes: published.nextKeyHashes,
1051
1213
  services
1052
1214
  });
1053
- await publishUpdatedLog({ idStore, updated, ifMatch: published.etag });
1215
+ if (logOnly) {
1216
+ await putLogResource({
1217
+ store: idStore,
1218
+ log: updated.log,
1219
+ ifMatch: published.etag
1220
+ });
1221
+ }
1222
+ else {
1223
+ await publishUpdatedLog({ idStore, updated, ifMatch: published.etag });
1224
+ }
1054
1225
  return { did: updated.did, doc: updated.doc };
1055
1226
  }
1056
1227
  /**
@@ -1076,69 +1247,80 @@ async function setDelegatedClientsPointerOnce({ idStore, updateKeys, companionDi
1076
1247
  * @param options.transientKeyMultibase {string} the visit's in-memory
1077
1248
  * signing key, public multibase
1078
1249
  * @param [options.mintGenerationDelegation] {Function}
1079
- * `({ companionDid }) => Promise<IZcap>` -- forwarded to the enrollment
1250
+ * `({ clientAnnexDid }) => Promise<IZcap>` -- forwarded to the enrollment
1080
1251
  * entry, which installs the minted delegation when it publishes the
1081
1252
  * generation's first transient VM (see
1082
- * {@link enrollCompanionTransientClient}). The closure receives whichever
1083
- * companion DID the round enrolls into, so a GC-race re-enroll mints for
1253
+ * {@link enrollClientAnnexTransientClient}). The closure receives whichever
1254
+ * annex DID the round enrolls into, so a GC-race re-enroll mints for
1084
1255
  * the fresh generation
1085
1256
  * @param [options.pinStore] {ResourceLogPinStore} chain-head pins for the
1086
1257
  * generation logs (a transient session passes an in-memory store); slot
1087
- * keys are derived per generation with {@link companionLogPinId}
1258
+ * keys are derived per generation with {@link clientAnnexLogPinId}
1088
1259
  * @param [options.maxRounds] {number} how many pointer moves to chase
1089
1260
  * before giving up (a GC pass is quarterly, so more than one mid-ceremony
1090
1261
  * move means something else is wrong)
1091
- * @returns {Promise<{ companionDid: string, doc: DIDDoc, log: DIDLog }>}
1262
+ * @returns {Promise<{ clientAnnexDid: string, doc: DIDDoc, log: DIDLog }>}
1092
1263
  */
1093
1264
  export async function enrollTransientClient({ readAccountDocument, storeForGenerationId, ladderSeed, transientKeyMultibase, mintGenerationDelegation: mintDelegation, pinStore, maxRounds = 3 }) {
1094
1265
  let accountDoc = await readAccountDocument();
1095
1266
  for (let round = 0; round < maxRounds; round++) {
1096
- const companionDid = delegatedClientsPointer({ doc: accountDoc });
1097
- if (companionDid === undefined) {
1098
- throw new Error('companion: the account document carries no delegated-clients ' +
1267
+ const clientAnnexDid = delegatedClientsPointer({ doc: accountDoc });
1268
+ if (clientAnnexDid === undefined) {
1269
+ throw new Error('client annex: the account document carries no delegated-clients ' +
1099
1270
  'service entry; no generation exists to enroll into.');
1100
1271
  }
1101
- const { spaceId, generationId } = companionDidParts({ did: companionDid });
1102
- const enrolled = await enrollCompanionTransientClient({
1272
+ const { spaceId, generationId } = clientAnnexDidParts({
1273
+ did: clientAnnexDid
1274
+ });
1275
+ const enrolled = await enrollClientAnnexTransientClient({
1103
1276
  store: storeForGenerationId(generationId),
1104
1277
  ladderSeed,
1105
1278
  generationId,
1106
1279
  transientKeyMultibase,
1107
- expectedDid: companionDid,
1280
+ expectedDid: clientAnnexDid,
1108
1281
  ...(mintDelegation !== undefined
1109
1282
  ? { mintGenerationDelegation: mintDelegation }
1110
1283
  : {}),
1111
1284
  ...(pinStore !== undefined
1112
- ? { pinStore, logId: companionLogPinId({ spaceId, generationId }) }
1285
+ ? { pinStore, logId: clientAnnexLogPinId({ spaceId, generationId }) }
1113
1286
  : {})
1114
1287
  });
1115
1288
  // The GC-race re-read: an unchanged pointer means the enrollment stands
1116
1289
  // in the pointed generation; a moved one means a concurrent GC abandoned
1117
1290
  // it, and the next round enrolls into the fresh generation.
1118
1291
  accountDoc = await readAccountDocument();
1119
- if (delegatedClientsPointer({ doc: accountDoc }) === companionDid) {
1120
- return { companionDid, doc: enrolled.doc, log: enrolled.log };
1292
+ if (delegatedClientsPointer({ doc: accountDoc }) === clientAnnexDid) {
1293
+ return { clientAnnexDid, doc: enrolled.doc, log: enrolled.log };
1121
1294
  }
1122
1295
  }
1123
- throw new Error('companion: the delegated-clients pointer kept moving across ' +
1296
+ throw new Error('client annex: the delegated-clients pointer kept moving across ' +
1124
1297
  `${String(maxRounds)} enrollment rounds; giving up.`);
1125
1298
  }
1126
1299
  /**
1127
1300
  * RENEW PRECEDES MINT: the blocking pre-mint stage a transient App Connect
1128
- * approval runs before delegating any grant. Reads the companion document
1301
+ * approval runs before delegating any grant. Reads the annex document
1129
1302
  * and hands back its embedded generation delegation -- renewing it first
1130
1303
  * when it is expired or inside the 30-day renewal window ({@link
1131
1304
  * zcapExpiring}): a fresh delegation is minted through the caller's closure
1132
1305
  * (ladder-signed -- the renewal must not depend on the very delegation it
1133
1306
  * replaces; published through the store, which in a transient session is
1134
1307
  * the credential's sibling delegation, so even a hard-expired delegation is
1135
- * recoverable), and one companion entry replaces the service entry's
1308
+ * recoverable), and one annex entry replaces the service entry's
1136
1309
  * endpoint in place, signed by the writing credential's static rung 0.
1137
1310
  *
1138
- * A companion document carrying no delegation entry at all installs one the
1311
+ * An annex document carrying no delegation entry at all installs one the
1139
1312
  * same way (the GC ceremony's own install stage and the first-VM install
1140
1313
  * make this rare; a heal, not a policy).
1141
1314
  *
1315
+ * Beside the expiry axis, an `accountDoc` adds the SIGNER-DEATH axis: a
1316
+ * standing delegation whose proof key is no longer in the supplied verified
1317
+ * account document has rotted under the current-key-set rule (the durable
1318
+ * client that minted it was revoked, or the ladder VM that signed it left
1319
+ * with the first durable self-enrollment) and is replaced the same way. No
1320
+ * revocation POST accompanies the replacement: a rotted chain no longer
1321
+ * verifies at the revocation endpoint, and the expiry-renewal path never
1322
+ * revoked either.
1323
+ *
1142
1324
  * Failure is the caller's failure: a renewal that cannot complete throws,
1143
1325
  * and the App Connect approval fails with the standard retryable-ceremony
1144
1326
  * posture -- deliberately no clamp-on-failure fallback, which would deliver
@@ -1148,21 +1330,28 @@ export async function enrollTransientClient({ readAccountDocument, storeForGener
1148
1330
  * 30 or more days remaining ({@link clampGrantExpires}).
1149
1331
  *
1150
1332
  * @param options {object}
1151
- * @param options.store {CompanionWriteStore} the generation's log store
1333
+ * @param options.store {ClientAnnexWriteStore} the generation's log store
1152
1334
  * (delegated through the credential's sibling delegation, or
1153
1335
  * controller-tier)
1154
1336
  * @param options.ladderSeed {Uint8Array} the credential's ladder seed, from
1155
1337
  * its unlock record
1156
1338
  * @param options.generationId {string} the generation collection's name
1157
1339
  * @param options.mintGenerationDelegation {Function}
1158
- * `({ companionDid }) => Promise<IZcap>` -- mints the replacement
1340
+ * `({ clientAnnexDid }) => Promise<IZcap>` -- mints the replacement
1159
1341
  * delegation (ladder-signed in a transient session)
1160
- * @param [options.expectedDid] {string} the companion DID the log must
1342
+ * @param [options.expectedDid] {string} the annex DID the log must
1161
1343
  * resolve to, from the account document's pointer
1162
1344
  * @param [options.pinStore] {ResourceLogPinStore} chain-head pins (a
1163
1345
  * transient session passes an in-memory store)
1164
1346
  * @param [options.logId] {string} the generation's pin-slot key, from
1165
- * {@link companionLogPinId}; required whenever a `pinStore` is supplied
1347
+ * {@link clientAnnexLogPinId}; required whenever a `pinStore` is supplied
1348
+ * @param [options.accountDoc] {PublishedKeyDocument} the locally VERIFIED
1349
+ * account document; supplied, a standing delegation whose proof key it no
1350
+ * longer lists is replaced (the signer-death axis above)
1351
+ * @param [options.force] {boolean} replace the embedded delegation
1352
+ * unconditionally, however healthy it looks -- the last-durable-client
1353
+ * forget's replacement stage, where the standing delegation has just been
1354
+ * revoked server-side (a state no client-side predicate can read)
1166
1355
  * @param [options.now] {number} epoch milliseconds, for tests
1167
1356
  * @returns {Promise<{ delegation: IZcap, renewed: boolean }>}
1168
1357
  */
@@ -1177,9 +1366,9 @@ export async function ensureGenerationDelegationCurrent(options) {
1177
1366
  * @param options {object} see {@link ensureGenerationDelegationCurrent}
1178
1367
  * @returns {Promise<{ delegation: IZcap, renewed: boolean }>}
1179
1368
  */
1180
- async function ensureGenerationDelegationCurrentOnce({ store, ladderSeed, generationId, mintGenerationDelegation: mintDelegation, expectedDid, pinStore, logId, now }) {
1369
+ async function ensureGenerationDelegationCurrentOnce({ store, ladderSeed, generationId, mintGenerationDelegation: mintDelegation, expectedDid, pinStore, logId, accountDoc, force = false, now }) {
1181
1370
  assertGenerationId(generationId);
1182
- const published = await readCompanionLogOrThrow({
1371
+ const published = await readClientAnnexLogOrThrow({
1183
1372
  store,
1184
1373
  ...(expectedDid !== undefined ? { expectedDid } : {}),
1185
1374
  ...(pinStore !== undefined ? { pinStore } : {}),
@@ -1187,7 +1376,17 @@ async function ensureGenerationDelegationCurrentOnce({ store, ladderSeed, genera
1187
1376
  });
1188
1377
  const { did, doc } = published;
1189
1378
  const standing = embeddedGenerationDelegation({ doc });
1190
- if (standing !== undefined &&
1379
+ const signerRotted = standing !== undefined &&
1380
+ accountDoc !== undefined &&
1381
+ !delegationKeyInDocument({
1382
+ doc: accountDoc,
1383
+ ...(delegationProofKeyId(standing) !== undefined
1384
+ ? { delegationKeyId: delegationProofKeyId(standing) }
1385
+ : {})
1386
+ });
1387
+ if (!force &&
1388
+ standing !== undefined &&
1389
+ !signerRotted &&
1191
1390
  !zcapExpiring({
1192
1391
  ...(standing.expires !== undefined
1193
1392
  ? { expires: standing.expires }
@@ -1198,22 +1397,22 @@ async function ensureGenerationDelegationCurrentOnce({ store, ladderSeed, genera
1198
1397
  }
1199
1398
  // The rung refusal precedes the mint: nothing is delegated for a writer
1200
1399
  // who cannot publish the entry that would carry it.
1201
- const rung = await companionRung({ ladderSeed, generationId });
1400
+ const rung = await clientAnnexRung({ ladderSeed, generationId });
1202
1401
  const rungHash = await deriveNextKeyHash(rung.keyMultibase);
1203
1402
  const revealed = published.updateKeys.includes(rung.keyMultibase);
1204
1403
  if (!revealed && !published.nextKeyHashes.includes(rungHash)) {
1205
- throw new CompanionRungUncommittedError("companion: the log commits neither this credential's rung-0 key nor " +
1404
+ throw new ClientAnnexRungUncommittedError("client annex: the log commits neither this credential's rung-0 key nor " +
1206
1405
  'its hash; a credential bound mid-generation cannot renew the ' +
1207
1406
  'generation delegation until a writer commits its hash or the next ' +
1208
1407
  'GC swap does.');
1209
1408
  }
1210
1409
  await assertCarryOverCommitments({ published });
1211
- const fresh = await mintDelegation({ companionDid: did });
1410
+ const fresh = await mintDelegation({ clientAnnexDid: did });
1212
1411
  const signer = await updateKeySigner({ seed: rung.seed });
1213
1412
  const updated = await updateDID({
1214
1413
  log: published.log,
1215
1414
  signer,
1216
- // The writer's rung-0 key reveals at its first companion write, exactly
1415
+ // The writer's rung-0 key reveals at its first annex write, exactly
1217
1416
  // as the enrollment entry does; `nextKeyHashes` is re-stated verbatim,
1218
1417
  // never inherited. Verification methods, relationship arrays, and every
1219
1418
  // other service entry ride the library's prior-state clone untouched.
@@ -1221,11 +1420,196 @@ async function ensureGenerationDelegationCurrentOnce({ store, ladderSeed, genera
1221
1420
  nextKeyHashes: [...published.nextKeyHashes],
1222
1421
  services: withGenerationDelegationEntry({
1223
1422
  doc,
1224
- companionDid: did,
1423
+ clientAnnexDid: did,
1225
1424
  delegation: fresh
1226
1425
  })
1227
1426
  });
1228
1427
  await putLogResource({ store, log: updated.log, ifMatch: published.etag });
1229
1428
  return { delegation: fresh, renewed: true };
1230
1429
  }
1231
- //# sourceMappingURL=companion.js.map
1430
+ /**
1431
+ * THE CLIENT-ANNEX RUNG STRIKE: drops a retired credential's annex posture
1432
+ * from a generation's log -- its revealed rung-0 key out of `updateKeys` and
1433
+ * its standing rung-0 hash out of `nextKeyHashes` -- in one atomic entry
1434
+ * signed by ANOTHER credential's committed rung 0 (an annex entry cannot
1435
+ * remove its own signing key: the entry verifies against its own re-stated
1436
+ * `updateKeys`). The credential-rotation ceremony's annex reach.
1437
+ *
1438
+ * A log committing neither the retired rung's key nor its hash is already
1439
+ * clean and the strike no-ops (`struck: false`) -- the resumable shape, and
1440
+ * the common one: a credential that never minted or wrote this generation
1441
+ * has no posture in it. An acting rung the log does not commit (after the
1442
+ * retired members are excluded -- so the retired credential can never sign
1443
+ * its own strike) is refused with {@link ClientAnnexRungUncommittedError},
1444
+ * which the caller maps to the generation-swap fallback: a fresh generation
1445
+ * minted from a surviving credential's seed retires the rung with the whole
1446
+ * generation.
1447
+ *
1448
+ * @param options {object}
1449
+ * @param options.store {ClientAnnexWriteStore} the pointed generation's log
1450
+ * store (controller-tier, or delegated through a sibling delegation)
1451
+ * @param options.retiredLadderSeed {Uint8Array} the RETIRED credential's
1452
+ * ladder seed (its rung is derived per generation, so the seed is the only
1453
+ * way to name what to strike)
1454
+ * @param options.actingLadderSeed {Uint8Array} a surviving credential's
1455
+ * ladder seed, whose committed rung 0 signs the strike entry
1456
+ * @param options.generationId {string} the generation collection's name
1457
+ * @param [options.expectedDid] {string} the annex DID the log must
1458
+ * resolve to, from the account document's pointer
1459
+ * @param [options.pinStore] {ResourceLogPinStore}
1460
+ * @param [options.logId] {string} the generation's pin-slot key, from
1461
+ * {@link clientAnnexLogPinId}; required whenever a `pinStore` is supplied
1462
+ * @returns {Promise<{ struck: boolean }>}
1463
+ */
1464
+ export async function retireClientAnnexRung(options) {
1465
+ return withLogConflictRetry(() => retireClientAnnexRungOnce(options));
1466
+ }
1467
+ /**
1468
+ * One attempt of {@link retireClientAnnexRung}, re-invoked by the conflict
1469
+ * retry (with the same signing key -- static rung 0 has no advanced-rung
1470
+ * retry shape).
1471
+ *
1472
+ * @param options {object} see {@link retireClientAnnexRung}
1473
+ * @returns {Promise<{ struck: boolean }>}
1474
+ */
1475
+ async function retireClientAnnexRungOnce({ store, retiredLadderSeed, actingLadderSeed, generationId, expectedDid, pinStore, logId }) {
1476
+ assertGenerationId(generationId);
1477
+ const published = await readClientAnnexLogOrThrow({
1478
+ store,
1479
+ ...(expectedDid !== undefined ? { expectedDid } : {}),
1480
+ ...(pinStore !== undefined ? { pinStore } : {}),
1481
+ ...(logId !== undefined ? { logId } : {})
1482
+ });
1483
+ const retired = await clientAnnexRung({
1484
+ ladderSeed: retiredLadderSeed,
1485
+ generationId
1486
+ });
1487
+ const retiredHash = await deriveNextKeyHash(retired.keyMultibase);
1488
+ const remainingKeys = published.updateKeys.filter(key => key !== retired.keyMultibase);
1489
+ const remainingHashes = published.nextKeyHashes.filter(hash => hash !== retiredHash);
1490
+ if (remainingKeys.length === published.updateKeys.length &&
1491
+ remainingHashes.length === published.nextKeyHashes.length) {
1492
+ // Already clean: the retired credential holds no posture in this
1493
+ // generation (never committed, or a completed earlier strike).
1494
+ return { struck: false };
1495
+ }
1496
+ // The acting rung must be committed AFTER the retired members are
1497
+ // excluded, so the retired credential can never sign its own strike.
1498
+ const acting = await clientAnnexRung({
1499
+ ladderSeed: actingLadderSeed,
1500
+ generationId
1501
+ });
1502
+ const actingHash = await deriveNextKeyHash(acting.keyMultibase);
1503
+ const revealed = remainingKeys.includes(acting.keyMultibase);
1504
+ if (!revealed && !remainingHashes.includes(actingHash)) {
1505
+ throw new ClientAnnexRungUncommittedError("client annex: the log commits neither the acting credential's rung-0 " +
1506
+ 'key nor its hash (or it is the retired rung itself); the strike ' +
1507
+ 'needs a distinct committed writer -- swap the generation instead.');
1508
+ }
1509
+ await assertCarryOverCommitments({ published });
1510
+ const signer = await updateKeySigner({ seed: acting.seed });
1511
+ const updated = await updateDID({
1512
+ log: published.log,
1513
+ signer,
1514
+ // The acting rung reveals at its first annex write, exactly as the
1515
+ // enrollment entry does; the retired rung's key and hash are dropped by
1516
+ // explicit re-statement (never parameter inheritance). Verification
1517
+ // methods, relationship arrays, and the service entries ride the
1518
+ // library's prior-state clone untouched.
1519
+ updateKeys: [...new Set([...remainingKeys, acting.keyMultibase])],
1520
+ nextKeyHashes: [...remainingHashes]
1521
+ });
1522
+ await putLogResource({ store, log: updated.log, ifMatch: published.etag });
1523
+ return { struck: true };
1524
+ }
1525
+ /**
1526
+ * THE CLIENT-ANNEX RUNG COMMIT: adds a freshly bound credential's rung-0
1527
+ * hash to a generation's `nextKeyHashes` -- one atomic hash-restating entry
1528
+ * signed by an already-committed credential's rung 0. The bind ceremonies'
1529
+ * annex reach (passkey add, passphrase change): a bind runs from a logged-in
1530
+ * session whose own login credential's rung 0 is committed, so committing the
1531
+ * new credential's hash here is what keeps it out of the mid-generation
1532
+ * lockout ({@link ClientAnnexRungUncommittedError} at its first transient
1533
+ * login, otherwise standing until the next GC swap's genesis).
1534
+ *
1535
+ * A log already committing the bound rung's hash (or carrying its revealed
1536
+ * key) is a no-op (`committed: false`) -- the resumable shape. An acting rung
1537
+ * the log does not commit is refused with
1538
+ * {@link ClientAnnexRungUncommittedError}: the bind ceremony maps that to an
1539
+ * honest skip (nothing licenses it to mint a generation), and the lockout
1540
+ * consequence stands as documented.
1541
+ *
1542
+ * @param options {object}
1543
+ * @param options.store {ClientAnnexWriteStore} the pointed generation's log
1544
+ * store (controller-tier, or delegated through a sibling delegation)
1545
+ * @param options.boundLadderSeed {Uint8Array} the freshly bound
1546
+ * credential's ladder seed (its rung is derived per generation, so the seed
1547
+ * is the only way to name what to commit)
1548
+ * @param options.actingLadderSeed {Uint8Array} the logged-in session's
1549
+ * login credential's ladder seed, whose committed rung 0 signs the entry
1550
+ * @param options.generationId {string} the generation collection's name
1551
+ * @param [options.expectedDid] {string} the annex DID the log must
1552
+ * resolve to, from the account document's pointer
1553
+ * @param [options.pinStore] {ResourceLogPinStore}
1554
+ * @param [options.logId] {string} the generation's pin-slot key, from
1555
+ * {@link clientAnnexLogPinId}; required whenever a `pinStore` is supplied
1556
+ * @returns {Promise<{ committed: boolean }>}
1557
+ */
1558
+ export async function commitClientAnnexRung(options) {
1559
+ return withLogConflictRetry(() => commitClientAnnexRungOnce(options));
1560
+ }
1561
+ /**
1562
+ * One attempt of {@link commitClientAnnexRung}, re-invoked by the conflict
1563
+ * retry (with the same signing key -- static rung 0 has no advanced-rung
1564
+ * retry shape).
1565
+ *
1566
+ * @param options {object} see {@link commitClientAnnexRung}
1567
+ * @returns {Promise<{ committed: boolean }>}
1568
+ */
1569
+ async function commitClientAnnexRungOnce({ store, boundLadderSeed, actingLadderSeed, generationId, expectedDid, pinStore, logId }) {
1570
+ assertGenerationId(generationId);
1571
+ const published = await readClientAnnexLogOrThrow({
1572
+ store,
1573
+ ...(expectedDid !== undefined ? { expectedDid } : {}),
1574
+ ...(pinStore !== undefined ? { pinStore } : {}),
1575
+ ...(logId !== undefined ? { logId } : {})
1576
+ });
1577
+ const bound = await clientAnnexRung({
1578
+ ladderSeed: boundLadderSeed,
1579
+ generationId
1580
+ });
1581
+ const boundHash = await deriveNextKeyHash(bound.keyMultibase);
1582
+ if (published.updateKeys.includes(bound.keyMultibase) ||
1583
+ published.nextKeyHashes.includes(boundHash)) {
1584
+ // Already committed (or even revealed): a completed earlier run, or a
1585
+ // generation the bound credential itself minted.
1586
+ return { committed: false };
1587
+ }
1588
+ const acting = await clientAnnexRung({
1589
+ ladderSeed: actingLadderSeed,
1590
+ generationId
1591
+ });
1592
+ const actingHash = await deriveNextKeyHash(acting.keyMultibase);
1593
+ const revealed = published.updateKeys.includes(acting.keyMultibase);
1594
+ if (!revealed && !published.nextKeyHashes.includes(actingHash)) {
1595
+ throw new ClientAnnexRungUncommittedError("client annex: the log commits neither the acting credential's rung-0 " +
1596
+ 'key nor its hash; a commit entry needs a committed writer -- the ' +
1597
+ 'bound credential stays locked out until the next GC swap.');
1598
+ }
1599
+ await assertCarryOverCommitments({ published });
1600
+ const signer = await updateKeySigner({ seed: acting.seed });
1601
+ const updated = await updateDID({
1602
+ log: published.log,
1603
+ signer,
1604
+ // The acting rung reveals at its first annex write, exactly as the
1605
+ // enrollment entry does; the bound rung's hash is added by explicit
1606
+ // re-statement (never parameter inheritance). Verification methods,
1607
+ // relationship arrays, and the service entries ride the library's
1608
+ // prior-state clone untouched.
1609
+ updateKeys: [...new Set([...published.updateKeys, acting.keyMultibase])],
1610
+ nextKeyHashes: [...published.nextKeyHashes, boundHash]
1611
+ });
1612
+ await putLogResource({ store, log: updated.log, ifMatch: published.etag });
1613
+ return { committed: true };
1614
+ }
1615
+ //# sourceMappingURL=log.js.map