@interop/wallet-core 0.47.0 → 0.49.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.
@@ -0,0 +1,221 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * Companion GC: the quarterly wholesale replacement of the companion
6
+ * generation, and the collection of everything it leaves behind.
7
+ *
8
+ * The ceremony has two halves with different rhythms:
9
+ *
10
+ * - The SWAP runs on the fixed quarterly cadence (90 days, at the first
11
+ * durable login after the period elapses -- coarse on purpose, so the
12
+ * account log's permanent pointer-entry rhythm reveals little about
13
+ * transient-use frequency), and only when the pointed generation is
14
+ * GC-quiet (the 24-hour max-visit quiet bound over its newest entry's
15
+ * `versionTime`, with a skew-margin grace hour -- a deferral policy only,
16
+ * never a session expiry). Its stage order is fixed: mint + genesis,
17
+ * install the fresh generation's delegation service entry, revoke the old
18
+ * generation's delegation, re-point the account document. Revoke lands
19
+ * before the re-point (closing the window where a fail-open non-conforming
20
+ * server still honors the old delegation -- and the revocation POST itself
21
+ * only verifies while the pointer still makes the old chain resolve), and
22
+ * before the delete (the POST needs the capability bytes the delete
23
+ * destroys).
24
+ *
25
+ * - The COLLECT fan-out is predicate-driven and runs at every durable login:
26
+ * every `gen-` collection the pointer does not name -- the generation a
27
+ * swap just superseded, a torn GC's leftover, a torn signup's orphan, a
28
+ * double-genesis loser -- gets identical treatment (revoke its embedded
29
+ * delegation blind, reading the server's 400 already-revoked answer as
30
+ * success; write the digest; delete), so a GC torn anywhere resumes at the
31
+ * next login instead of waiting a quarter. Failures are collected per
32
+ * generation and never abort the fan-out: a partial pass is a resumable
33
+ * success.
34
+ *
35
+ * The completion predicate is durable state alone (exactly one `gen-`
36
+ * collection exists in the auxiliary Space and it is the one the pointer
37
+ * names); no marker resource exists anywhere -- the account document's
38
+ * pointer-update entry is the record, and the cadence is read off its
39
+ * `versionTime`. The digest (`GenerationCollect`, built by the caller's
40
+ * `recordDigest` over `@interop/wallet-core/space`'s builder) is written
41
+ * before the delete: it is the owner's only record of the collected window's
42
+ * visits surviving the delete, and compromise detection ends at digest
43
+ * granularity.
44
+ *
45
+ * Honest limitations: the "no unexpired delegation names a dead generation"
46
+ * conjunct is an ordering obligation, not a check (the revocation protocol
47
+ * exposes no read endpoint, and the dead generation's delegation bytes are
48
+ * destroyed with its collection); orphaned generations are
49
+ * authorization-inert (no delegation ever names an unpointed generation
50
+ * under pointer equality), so what accretes between passes is a storage
51
+ * leak, never an authority leak.
52
+ */
53
+ import type { DIDLog } from '@interop/did-method-webvh';
54
+ import type { ZcapClient } from '@interop/ezcap';
55
+ import type { WasClient } from '@interop/was-client';
56
+ import type { ResourceLogPinStore } from '../resourceLog/pin.js';
57
+ import type { ClientWebvhUpdateKeys, PublishedWebvhLog, WebvhIdStore } from './didWebvh.js';
58
+ /**
59
+ * The fixed GC cadence: a generation is replaced at the first durable login
60
+ * 90 days after the current pointer value was established. Wallet GC policy
61
+ * over the account log's own timestamps, never a stored or wire value.
62
+ */
63
+ export declare const GENERATION_GC_PERIOD_MS: number;
64
+ /**
65
+ * The quiet bound: a generation is GC-quiet when its newest entry's
66
+ * `versionTime` is over 24 hours old. The bound defers the swap only; it
67
+ * never expires a session. A visit outliving it merely loses guaranteed
68
+ * guard protection, and in the rare case a quarterly pass lands on one, the
69
+ * session's next authorization failure maps to the generation-lapse retry
70
+ * state.
71
+ */
72
+ export declare const GENERATION_QUIET_BOUND_MS: number;
73
+ /**
74
+ * The skew-margin grace floor on the quiet bound: three clocks meet at the
75
+ * guard check (the enrolling writer's, any prior entry writer's, and the
76
+ * GC-running client's), and `versionTime` is asserted by the writer's clock,
77
+ * so the guard compares against the bound plus this margin. An hour is
78
+ * generous against real-world skew and defers the swap by at most an hour
79
+ * against a quarterly cadence.
80
+ */
81
+ export declare const GENERATION_QUIET_GRACE_MS: number;
82
+ /**
83
+ * The `versionTime` of the account-log entry that established the CURRENT
84
+ * `#DelegatedClients` pointer value: the newest entry whose state names a
85
+ * different companion DID than the entry before it (or the first entry, when
86
+ * the pointer has been there since genesis). This is the cadence's clock --
87
+ * "the pointer-update entry is the record" -- and it is read off the log a
88
+ * durable login has already verified, so the cadence costs no extra fetch
89
+ * and every enrolled client agrees on it.
90
+ *
91
+ * @param options {object}
92
+ * @param options.log {DIDLog} the VERIFIED account log
93
+ * @returns {string | undefined} the establishing entry's `versionTime`,
94
+ * or undefined when no entry carries a pointer
95
+ */
96
+ export declare function delegatedClientsPointerEstablishedAt({ log }: {
97
+ log: DIDLog;
98
+ }): string | undefined;
99
+ /**
100
+ * Whether the quarterly swap is due: the current pointer value was
101
+ * established {@link GENERATION_GC_PERIOD_MS} or more ago. A log with no
102
+ * pointer is never due (there is no generation to replace).
103
+ *
104
+ * @param options {object}
105
+ * @param options.log {DIDLog} the VERIFIED account log
106
+ * @param [options.now] {number} epoch milliseconds, for tests
107
+ * @returns {boolean}
108
+ */
109
+ export declare function companionGcDue({ log, now }: {
110
+ log: DIDLog;
111
+ now?: number;
112
+ }): boolean;
113
+ /**
114
+ * The live-entry guard: whether a generation is GC-quiet -- its newest
115
+ * entry's `versionTime` is older than the quiet bound plus the skew grace
116
+ * margin. Applies to the POINTED generation only (an unpointed generation
117
+ * authorizes nothing under pointer equality, so nothing inside it ever
118
+ * defers deletion). An unparseable `versionTime` reads as not quiet: the
119
+ * swap defers rather than abandoning a possibly-live visit.
120
+ *
121
+ * @param options {object}
122
+ * @param options.log {DIDLog} the pointed generation's VERIFIED companion
123
+ * log
124
+ * @param [options.now] {number} epoch milliseconds, for tests
125
+ * @returns {boolean}
126
+ */
127
+ export declare function generationQuiet({ log, now }: {
128
+ log: DIDLog;
129
+ now?: number;
130
+ }): boolean;
131
+ /**
132
+ * What the swap half of one GC pass did. `replaced` is the successful swap;
133
+ * `not-due` and `deferred-live` are the two healthy skips (cadence and quiet
134
+ * bound); `no-pointer` means the account has no companion posture (the whole
135
+ * pass no-ops -- without a pointer there is no auxiliary Space to list);
136
+ * `no-ladder-seed` means the swap was due but the login held no ladder seed
137
+ * to mint with (a non-standing record); `failed` means a swap stage threw --
138
+ * reported in `failed` under the pointed generation's id, with the collect
139
+ * fan-out still run.
140
+ */
141
+ export type CompanionGcSwapOutcome = 'replaced' | 'not-due' | 'deferred-live' | 'no-pointer' | 'no-ladder-seed' | 'failed';
142
+ /**
143
+ * One GC pass's report: the swap outcome, the companion DID the account
144
+ * points at after the pass, the generation ids collected (revoked, digested,
145
+ * deleted), and the per-generation failures. A report with `failed` entries
146
+ * is a resumable success -- the next durable login's pass picks up exactly
147
+ * the generations still listed.
148
+ */
149
+ export interface CompanionGcReport {
150
+ swap: CompanionGcSwapOutcome;
151
+ pointedDid?: string;
152
+ collected: string[];
153
+ failed: Array<{
154
+ generationId: string;
155
+ error: unknown;
156
+ }>;
157
+ }
158
+ /**
159
+ * One companion GC pass: the quarterly swap when due and quiet, then the
160
+ * predicate-driven collect fan-out over every non-pointed `gen-` collection.
161
+ * See the module header for the stage order and its load-bearing constraints.
162
+ *
163
+ * Convergence: every stage detects completion from durable state. A re-run
164
+ * after a tear re-POSTs the revocation blind (400 already-revoked reads as
165
+ * success), re-writes the digest (the deterministic payload id collapses the
166
+ * second row at read time), and re-runs the idempotent delete; a swap torn
167
+ * before its re-point leaves an unpointed fresh generation the same fan-out
168
+ * collects.
169
+ *
170
+ * @param options {object}
171
+ * @param options.was {WasClient} the storage client, signing as an
172
+ * enrolled client (root tier on both the account and auxiliary Spaces)
173
+ * @param options.wasServerUrl {string} the account pointer's host
174
+ * @param options.accountSpaceId {string} the ACCOUNT Space's id (the
175
+ * generation delegation's target subtree)
176
+ * @param options.account {object} the VERIFIED account log
177
+ * (`{ did, doc, log }` -- `verifyAccountLog`'s shape)
178
+ * @param options.idStore {WebvhIdStore} the account log's id store, for
179
+ * the re-point
180
+ * @param options.updateKeys {ClientWebvhUpdateKeys} this enrolled client's
181
+ * update keys, signing the re-point entry
182
+ * @param options.zcapClient {ZcapClient} signs the fresh generation
183
+ * delegation (the promoted account keyId)
184
+ * @param [options.ladderSeed] {Uint8Array} the login credential's ladder
185
+ * seed; absent, a due swap reports `no-ladder-seed` and only the collect
186
+ * fan-out runs
187
+ * @param options.recordDigest {Function}
188
+ * `({ generationId, firstEntry, lastEntry, entryCount }) => Promise<void>`
189
+ * -- writes the GenerationCollect wallet-activity row; called before the
190
+ * delete, and a throw keeps the generation for the next pass
191
+ * @param [options.onCollected] {Function}
192
+ * `({ generationId }) => Promise<void>` -- local cleanup after a
193
+ * generation's delete (the caller's companion pin-slot drop); a throw is
194
+ * reported but cannot be retried (the collection is already gone)
195
+ * @param [options.pinStore] {ResourceLogPinStore} chain-head pins for the
196
+ * account-log re-point and the pointed generation's read
197
+ * @param [options.now] {number} epoch milliseconds, for tests
198
+ * @returns {Promise<CompanionGcReport>}
199
+ */
200
+ export declare function runCompanionGc({ was, wasServerUrl, accountSpaceId, account, idStore, updateKeys, zcapClient, ladderSeed, recordDigest, onCollected, pinStore, now }: {
201
+ was: WasClient;
202
+ wasServerUrl: string;
203
+ accountSpaceId: string;
204
+ account: Pick<PublishedWebvhLog, 'did' | 'doc' | 'log'>;
205
+ idStore: WebvhIdStore;
206
+ updateKeys: ClientWebvhUpdateKeys;
207
+ zcapClient: ZcapClient;
208
+ ladderSeed?: Uint8Array;
209
+ recordDigest: (digest: {
210
+ generationId: string;
211
+ firstEntry?: string;
212
+ lastEntry?: string;
213
+ entryCount?: number;
214
+ }) => Promise<void>;
215
+ onCollected?: (options: {
216
+ generationId: string;
217
+ }) => Promise<void>;
218
+ pinStore?: ResourceLogPinStore;
219
+ now?: number;
220
+ }): Promise<CompanionGcReport>;
221
+ //# sourceMappingURL=companionGc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"companionGc.d.ts","sourceRoot":"","sources":["../../src/webvh/companionGc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,413 @@
1
+ import { companionDidParts, companionLogPinId, companionLogStore, delegatedClientsPointer, embeddedGenerationDelegation, ensureGenerationDelegationCurrent, GENERATION_ID_PREFIX, mintCredentialCompanionGeneration, mintGenerationDelegation, setDelegatedClientsPointer } from './companion.js';
2
+ import { readPublishedLog } from './didWebvh.js';
3
+ import { accountLogPinId } from './verifyLog.js';
4
+ /**
5
+ * The fixed GC cadence: a generation is replaced at the first durable login
6
+ * 90 days after the current pointer value was established. Wallet GC policy
7
+ * over the account log's own timestamps, never a stored or wire value.
8
+ */
9
+ export const GENERATION_GC_PERIOD_MS = 90 * 24 * 60 * 60 * 1000;
10
+ /**
11
+ * The quiet bound: a generation is GC-quiet when its newest entry's
12
+ * `versionTime` is over 24 hours old. The bound defers the swap only; it
13
+ * never expires a session. A visit outliving it merely loses guaranteed
14
+ * guard protection, and in the rare case a quarterly pass lands on one, the
15
+ * session's next authorization failure maps to the generation-lapse retry
16
+ * state.
17
+ */
18
+ export const GENERATION_QUIET_BOUND_MS = 24 * 60 * 60 * 1000;
19
+ /**
20
+ * The skew-margin grace floor on the quiet bound: three clocks meet at the
21
+ * guard check (the enrolling writer's, any prior entry writer's, and the
22
+ * GC-running client's), and `versionTime` is asserted by the writer's clock,
23
+ * so the guard compares against the bound plus this margin. An hour is
24
+ * generous against real-world skew and defers the swap by at most an hour
25
+ * against a quarterly cadence.
26
+ */
27
+ export const GENERATION_QUIET_GRACE_MS = 60 * 60 * 1000;
28
+ /**
29
+ * The `versionTime` of the account-log entry that established the CURRENT
30
+ * `#DelegatedClients` pointer value: the newest entry whose state names a
31
+ * different companion DID than the entry before it (or the first entry, when
32
+ * the pointer has been there since genesis). This is the cadence's clock --
33
+ * "the pointer-update entry is the record" -- and it is read off the log a
34
+ * durable login has already verified, so the cadence costs no extra fetch
35
+ * and every enrolled client agrees on it.
36
+ *
37
+ * @param options {object}
38
+ * @param options.log {DIDLog} the VERIFIED account log
39
+ * @returns {string | undefined} the establishing entry's `versionTime`,
40
+ * or undefined when no entry carries a pointer
41
+ */
42
+ export function delegatedClientsPointerEstablishedAt({ log }) {
43
+ let establishedAt;
44
+ let previous;
45
+ for (const entry of log) {
46
+ const pointed = delegatedClientsPointer({ doc: entry.state });
47
+ if (pointed !== undefined && pointed !== previous) {
48
+ establishedAt = entry.versionTime;
49
+ }
50
+ if (pointed === undefined) {
51
+ // A document with no pointer has no companion posture; a later entry
52
+ // restoring one establishes afresh.
53
+ establishedAt = undefined;
54
+ }
55
+ previous = pointed;
56
+ }
57
+ return establishedAt;
58
+ }
59
+ /**
60
+ * Whether the quarterly swap is due: the current pointer value was
61
+ * established {@link GENERATION_GC_PERIOD_MS} or more ago. A log with no
62
+ * pointer is never due (there is no generation to replace).
63
+ *
64
+ * @param options {object}
65
+ * @param options.log {DIDLog} the VERIFIED account log
66
+ * @param [options.now] {number} epoch milliseconds, for tests
67
+ * @returns {boolean}
68
+ */
69
+ export function companionGcDue({ log, now = Date.now() }) {
70
+ const establishedAt = delegatedClientsPointerEstablishedAt({ log });
71
+ if (establishedAt === undefined) {
72
+ return false;
73
+ }
74
+ const establishedMs = Date.parse(establishedAt);
75
+ return (Number.isFinite(establishedMs) &&
76
+ now - establishedMs >= GENERATION_GC_PERIOD_MS);
77
+ }
78
+ /**
79
+ * The live-entry guard: whether a generation is GC-quiet -- its newest
80
+ * entry's `versionTime` is older than the quiet bound plus the skew grace
81
+ * margin. Applies to the POINTED generation only (an unpointed generation
82
+ * authorizes nothing under pointer equality, so nothing inside it ever
83
+ * defers deletion). An unparseable `versionTime` reads as not quiet: the
84
+ * swap defers rather than abandoning a possibly-live visit.
85
+ *
86
+ * @param options {object}
87
+ * @param options.log {DIDLog} the pointed generation's VERIFIED companion
88
+ * log
89
+ * @param [options.now] {number} epoch milliseconds, for tests
90
+ * @returns {boolean}
91
+ */
92
+ export function generationQuiet({ log, now = Date.now() }) {
93
+ const newest = log[log.length - 1]?.versionTime;
94
+ if (newest === undefined) {
95
+ return false;
96
+ }
97
+ const newestMs = Date.parse(newest);
98
+ return (Number.isFinite(newestMs) &&
99
+ now - newestMs >= GENERATION_QUIET_BOUND_MS + GENERATION_QUIET_GRACE_MS);
100
+ }
101
+ /**
102
+ * One companion GC pass: the quarterly swap when due and quiet, then the
103
+ * predicate-driven collect fan-out over every non-pointed `gen-` collection.
104
+ * See the module header for the stage order and its load-bearing constraints.
105
+ *
106
+ * Convergence: every stage detects completion from durable state. A re-run
107
+ * after a tear re-POSTs the revocation blind (400 already-revoked reads as
108
+ * success), re-writes the digest (the deterministic payload id collapses the
109
+ * second row at read time), and re-runs the idempotent delete; a swap torn
110
+ * before its re-point leaves an unpointed fresh generation the same fan-out
111
+ * collects.
112
+ *
113
+ * @param options {object}
114
+ * @param options.was {WasClient} the storage client, signing as an
115
+ * enrolled client (root tier on both the account and auxiliary Spaces)
116
+ * @param options.wasServerUrl {string} the account pointer's host
117
+ * @param options.accountSpaceId {string} the ACCOUNT Space's id (the
118
+ * generation delegation's target subtree)
119
+ * @param options.account {object} the VERIFIED account log
120
+ * (`{ did, doc, log }` -- `verifyAccountLog`'s shape)
121
+ * @param options.idStore {WebvhIdStore} the account log's id store, for
122
+ * the re-point
123
+ * @param options.updateKeys {ClientWebvhUpdateKeys} this enrolled client's
124
+ * update keys, signing the re-point entry
125
+ * @param options.zcapClient {ZcapClient} signs the fresh generation
126
+ * delegation (the promoted account keyId)
127
+ * @param [options.ladderSeed] {Uint8Array} the login credential's ladder
128
+ * seed; absent, a due swap reports `no-ladder-seed` and only the collect
129
+ * fan-out runs
130
+ * @param options.recordDigest {Function}
131
+ * `({ generationId, firstEntry, lastEntry, entryCount }) => Promise<void>`
132
+ * -- writes the GenerationCollect wallet-activity row; called before the
133
+ * delete, and a throw keeps the generation for the next pass
134
+ * @param [options.onCollected] {Function}
135
+ * `({ generationId }) => Promise<void>` -- local cleanup after a
136
+ * generation's delete (the caller's companion pin-slot drop); a throw is
137
+ * reported but cannot be retried (the collection is already gone)
138
+ * @param [options.pinStore] {ResourceLogPinStore} chain-head pins for the
139
+ * account-log re-point and the pointed generation's read
140
+ * @param [options.now] {number} epoch milliseconds, for tests
141
+ * @returns {Promise<CompanionGcReport>}
142
+ */
143
+ export async function runCompanionGc({ was, wasServerUrl, accountSpaceId, account, idStore, updateKeys, zcapClient, ladderSeed, recordDigest, onCollected, pinStore, now = Date.now() }) {
144
+ const pointedDid = delegatedClientsPointer({ doc: account.doc });
145
+ if (pointedDid === undefined) {
146
+ return { swap: 'no-pointer', collected: [], failed: [] };
147
+ }
148
+ const { spaceId } = companionDidParts({ did: pointedDid });
149
+ const failed = [];
150
+ // 1. The swap, when the quarterly cadence is due. Its own failure is
151
+ // collected under the pointed generation's id rather than aborting the
152
+ // pass: the collect fan-out below still cleans what it can, and the next
153
+ // durable login re-attempts the swap from durable state.
154
+ let swap = 'not-due';
155
+ let currentDid = pointedDid;
156
+ if (companionGcDue({ log: account.log, now })) {
157
+ swap =
158
+ ladderSeed === undefined
159
+ ? 'no-ladder-seed'
160
+ : await (async () => {
161
+ const oldParts = companionDidParts({ did: pointedDid });
162
+ try {
163
+ const old = await readCompanionGeneration({
164
+ was,
165
+ spaceId,
166
+ generationId: oldParts.generationId,
167
+ expectedDid: pointedDid,
168
+ pinStore
169
+ });
170
+ if (old === undefined ||
171
+ !generationQuiet({ log: old.log, now })) {
172
+ // A missing pointed log is a broken state the collect
173
+ // fan-out cannot touch (the pointer still names it); defer
174
+ // rather than swap onto a generation this pass could not
175
+ // read as quiet.
176
+ return old === undefined ? 'failed' : 'deferred-live';
177
+ }
178
+ currentDid = await replaceCompanionGeneration({
179
+ was,
180
+ wasServerUrl,
181
+ accountSpaceId,
182
+ account,
183
+ idStore,
184
+ updateKeys,
185
+ zcapClient,
186
+ ladderSeed,
187
+ companionSpaceId: spaceId,
188
+ oldGeneration: old,
189
+ pinStore
190
+ });
191
+ return 'replaced';
192
+ }
193
+ catch (err) {
194
+ failed.push({ generationId: oldParts.generationId, error: err });
195
+ return 'failed';
196
+ }
197
+ })();
198
+ }
199
+ // 2. The collect fan-out: every `gen-` collection the (possibly fresh)
200
+ // pointer does not name. Orphan discovery is a plain prefix match over the
201
+ // auxiliary Space's collection listing -- no registry of generations
202
+ // exists anywhere -- and a torn GC's old generation, a torn signup's
203
+ // orphan, and a double-genesis loser get identical treatment.
204
+ const currentGenerationId = companionDidParts({
205
+ did: currentDid
206
+ }).generationId;
207
+ const space = was.space(spaceId);
208
+ const stale = [];
209
+ for await (const page of space.collectionsPages()) {
210
+ for (const item of page.items) {
211
+ if (item.id.startsWith(GENERATION_ID_PREFIX) &&
212
+ item.id !== currentGenerationId) {
213
+ stale.push(item.id);
214
+ }
215
+ }
216
+ }
217
+ const collected = [];
218
+ await Promise.all(stale.map(async (generationId) => {
219
+ try {
220
+ await collectOneGeneration({
221
+ was,
222
+ spaceId,
223
+ generationId,
224
+ recordDigest,
225
+ onCollected
226
+ });
227
+ collected.push(generationId);
228
+ }
229
+ catch (err) {
230
+ failed.push({ generationId, error: err });
231
+ }
232
+ }));
233
+ return { swap, pointedDid: currentDid, collected, failed };
234
+ }
235
+ /**
236
+ * Reads and verifies one generation's published companion log, or resolves
237
+ * undefined when its `did.jsonl` does not exist (a generation that never
238
+ * finished minting, or whose collection outlived a torn delete).
239
+ *
240
+ * @param options {object}
241
+ * @param options.was {WasClient}
242
+ * @param options.spaceId {string} the auxiliary companion Space's id
243
+ * @param options.generationId {string}
244
+ * @param [options.expectedDid] {string}
245
+ * @param [options.pinStore] {ResourceLogPinStore}
246
+ * @returns {Promise<PublishedWebvhLog | undefined>}
247
+ */
248
+ async function readCompanionGeneration({ was, spaceId, generationId, expectedDid, pinStore }) {
249
+ const store = companionLogStore({ was, spaceId, generationId });
250
+ return readPublishedLog({
251
+ idStore: store,
252
+ ...(expectedDid !== undefined ? { expectedDid } : {}),
253
+ ...(pinStore !== undefined
254
+ ? { pinStore, logId: companionLogPinId({ spaceId, generationId }) }
255
+ : {})
256
+ });
257
+ }
258
+ /**
259
+ * The swap's four stages, in the fixed order: mint + genesis; install the
260
+ * fresh generation's delegation service entry; revoke the old generation's
261
+ * delegation; re-point the account document. Returns the fresh companion
262
+ * DID. The digest and the delete are deliberately NOT here -- once the
263
+ * re-point lands, the old generation is an ordinary non-pointed `gen-`
264
+ * collection and the standing collect fan-out handles it, which is also
265
+ * what makes a swap torn after its re-point resume for free.
266
+ *
267
+ * @param options {object} see {@link runCompanionGc}, plus:
268
+ * @param options.companionSpaceId {string} the auxiliary Space's id
269
+ * @param options.oldGeneration {PublishedWebvhLog} the pointed
270
+ * generation's verified log, read by the quiet check
271
+ * @returns {Promise<string>} the fresh companion DID
272
+ */
273
+ async function replaceCompanionGeneration({ was, wasServerUrl, accountSpaceId, account, idStore, updateKeys, zcapClient, ladderSeed, companionSpaceId, oldGeneration, pinStore }) {
274
+ // 1. Mint + genesis: a fresh generation in the existing auxiliary Space
275
+ // (the typed-Space ensure no-ops on it; the controller argument is only
276
+ // read when the Space does not exist). The genesis commits the minting
277
+ // credential's rung-0 hash for the fresh generation id.
278
+ const minted = await mintCredentialCompanionGeneration({
279
+ was,
280
+ wasServerUrl,
281
+ spaceId: companionSpaceId,
282
+ controller: account.did,
283
+ ladderSeed
284
+ });
285
+ // 2. Install the fresh generation's delegation service entry (the
286
+ // install-when-absent half of the renew-precedes-mint helper), the
287
+ // delegation signed by this enrolled client's promoted account key and the
288
+ // installing companion entry by the credential's rung 0.
289
+ await ensureGenerationDelegationCurrent({
290
+ store: companionLogStore({
291
+ was,
292
+ spaceId: companionSpaceId,
293
+ generationId: minted.generationId
294
+ }),
295
+ ladderSeed,
296
+ generationId: minted.generationId,
297
+ mintGenerationDelegation: async ({ companionDid }) => mintGenerationDelegation({
298
+ zcapClient,
299
+ wasServerUrl,
300
+ spaceId: accountSpaceId,
301
+ companionDid
302
+ }),
303
+ expectedDid: minted.did,
304
+ ...(pinStore !== undefined
305
+ ? {
306
+ pinStore,
307
+ logId: companionLogPinId({
308
+ spaceId: companionSpaceId,
309
+ generationId: minted.generationId
310
+ })
311
+ }
312
+ : {})
313
+ });
314
+ // 3. Revoke the old generation's delegation, before the re-point (the
315
+ // revocation POST verifies against the currently resolved document, so it
316
+ // only chains while the pointer still names the old generation) and
317
+ // before the delete (the POST needs bytes the delete destroys).
318
+ const oldDelegation = embeddedGenerationDelegation({ doc: oldGeneration.doc });
319
+ if (oldDelegation !== undefined) {
320
+ await revokeTreatingAlreadyRevokedAsSuccess({
321
+ was,
322
+ delegation: oldDelegation
323
+ });
324
+ }
325
+ // 4. Re-point the account document at the fresh generation. On a
326
+ // conforming server the pointer equality itself kills the old
327
+ // generation's delegations; the explicit revoke above covered the
328
+ // fail-open case.
329
+ await setDelegatedClientsPointer({
330
+ idStore,
331
+ updateKeys,
332
+ companionDid: minted.did,
333
+ expectedDid: account.did,
334
+ ...(pinStore !== undefined
335
+ ? { pinStore, logId: accountLogPinId({ spaceId: accountSpaceId }) }
336
+ : {})
337
+ });
338
+ return minted.did;
339
+ }
340
+ /**
341
+ * Collects one non-pointed generation: revoke its embedded delegation
342
+ * (blind; the server's 400 already-revoked answer reads as success), write
343
+ * the digest from its verified log, delete the collection, then the
344
+ * caller's local cleanup. A generation whose `did.jsonl` does not exist
345
+ * held no visits and is deleted without a digest row; one whose log exists
346
+ * but fails verification is kept and reported (deleting it would destroy
347
+ * the evidence of tampering).
348
+ *
349
+ * @param options {object}
350
+ * @param options.was {WasClient}
351
+ * @param options.spaceId {string}
352
+ * @param options.generationId {string}
353
+ * @param options.recordDigest {Function} see {@link runCompanionGc}
354
+ * @param [options.onCollected] {Function} see {@link runCompanionGc}
355
+ * @returns {Promise<void>}
356
+ */
357
+ async function collectOneGeneration({ was, spaceId, generationId, recordDigest, onCollected }) {
358
+ // No pin, no expectedDid: an orphan was possibly never pointed from this
359
+ // client, and its chain-head slot is about to be dropped either way. The
360
+ // log still fully verifies (hash chain, prerotation, rung signatures) --
361
+ // the digest quotes only a log that resolves.
362
+ const published = await readCompanionGeneration({
363
+ was,
364
+ spaceId,
365
+ generationId
366
+ });
367
+ if (published !== undefined) {
368
+ const oldDelegation = embeddedGenerationDelegation({ doc: published.doc });
369
+ if (oldDelegation !== undefined) {
370
+ await revokeTreatingAlreadyRevokedAsSuccess({
371
+ was,
372
+ delegation: oldDelegation
373
+ });
374
+ }
375
+ // The digest, strictly before the delete: the delete destroys the
376
+ // owner's only per-entry record of the window's visits, so a digest
377
+ // that cannot be written keeps the generation for the next pass.
378
+ await recordDigest({
379
+ generationId,
380
+ firstEntry: published.log[0]?.versionTime,
381
+ lastEntry: published.log[published.log.length - 1]?.versionTime,
382
+ entryCount: published.log.length
383
+ });
384
+ }
385
+ // Idempotent: a re-run's delete of an already-deleted collection resolves.
386
+ await was.space(spaceId).collection(generationId).delete();
387
+ await onCollected?.({ generationId });
388
+ }
389
+ /**
390
+ * Submits the revocation of a generation delegation, reading the server's
391
+ * 400 answer as success: an already-revoked chain (a resumed GC's blind
392
+ * re-POST) and an expired delegation (which no longer needs revoking) both
393
+ * land there, and the revocation protocol exposes no read endpoint to
394
+ * distinguish them beforehand. Matched on `err.name` -- error classes do not
395
+ * survive crossing package copies.
396
+ *
397
+ * @param options {object}
398
+ * @param options.was {WasClient}
399
+ * @param options.delegation {IZcap}
400
+ * @returns {Promise<void>}
401
+ */
402
+ async function revokeTreatingAlreadyRevokedAsSuccess({ was, delegation }) {
403
+ try {
404
+ await was.revoke(delegation);
405
+ }
406
+ catch (err) {
407
+ if (err.name === 'ValidationError') {
408
+ return;
409
+ }
410
+ throw err;
411
+ }
412
+ }
413
+ //# sourceMappingURL=companionGc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"companionGc.js","sourceRoot":"","sources":["../../src/webvh/companionGc.ts"],"names":[],"mappings":"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"}
@@ -288,7 +288,7 @@ export type DidWebKeyMapV2 = DidWebKeyMap & {
288
288
  * creation. The host segment percent-encodes a port (`localhost:8080` becomes
289
289
  * `localhost%3A8080`), matching the library's `toDidDomainComponent`. The
290
290
  * collection defaults to the account log's `id` collection; a companion
291
- * generation's log passes its own `gen-` collection segment.
291
+ * generation's log passes its own `gen-` generation id.
292
292
  *
293
293
  * @param options {object}
294
294
  * @param options.wasServerUrl {string}