@interop/wallet-core 0.17.1 → 0.18.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 (75) hide show
  1. package/README.md +13 -13
  2. package/dist/clients/index.d.ts +4 -3
  3. package/dist/clients/index.d.ts.map +1 -1
  4. package/dist/clients/index.js +3 -2
  5. package/dist/clients/index.js.map +1 -1
  6. package/dist/clients/policy.d.ts +3 -3
  7. package/dist/clients/policy.d.ts.map +1 -1
  8. package/dist/clients/policy.js +1 -1
  9. package/dist/clients/revocation.d.ts +15 -15
  10. package/dist/clients/revocation.d.ts.map +1 -1
  11. package/dist/clients/revocation.js +19 -19
  12. package/dist/clients/revocation.js.map +1 -1
  13. package/dist/clients/rosterPolicy.d.ts +26 -25
  14. package/dist/clients/rosterPolicy.d.ts.map +1 -1
  15. package/dist/clients/rosterPolicy.js +29 -29
  16. package/dist/clients/rosterPolicy.js.map +1 -1
  17. package/dist/enrollment/enrollment.d.ts +16 -16
  18. package/dist/enrollment/enrollment.d.ts.map +1 -1
  19. package/dist/enrollment/enrollment.js +19 -19
  20. package/dist/enrollment/enrollment.js.map +1 -1
  21. package/dist/enrollment/index.d.ts +1 -1
  22. package/dist/enrollment/index.js +1 -1
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +2 -2
  25. package/dist/keys/clientKeyRecord.d.ts +16 -16
  26. package/dist/keys/clientKeyRecord.d.ts.map +1 -1
  27. package/dist/keys/clientKeyRecord.js +39 -35
  28. package/dist/keys/clientKeyRecord.js.map +1 -1
  29. package/dist/keys/index.d.ts +19 -17
  30. package/dist/keys/index.d.ts.map +1 -1
  31. package/dist/keys/index.js +16 -14
  32. package/dist/keys/index.js.map +1 -1
  33. package/dist/keys/rosterStore.d.ts +3 -3
  34. package/dist/keys/rosterStore.d.ts.map +1 -1
  35. package/dist/keys/rosterStore.js +13 -13
  36. package/dist/keys/rosterStore.js.map +1 -1
  37. package/dist/keys/userKey.d.ts +43 -0
  38. package/dist/keys/userKey.d.ts.map +1 -0
  39. package/dist/keys/{puk.js → userKey.js} +23 -21
  40. package/dist/keys/userKey.js.map +1 -0
  41. package/dist/keys/{pukCascade.d.ts → userKeyCascade.d.ts} +69 -69
  42. package/dist/keys/userKeyCascade.d.ts.map +1 -0
  43. package/dist/keys/{pukCascade.js → userKeyCascade.js} +67 -64
  44. package/dist/keys/userKeyCascade.js.map +1 -0
  45. package/dist/keys/{pukRoster.d.ts → userKeyRoster.d.ts} +73 -74
  46. package/dist/keys/userKeyRoster.d.ts.map +1 -0
  47. package/dist/keys/{pukRoster.js → userKeyRoster.js} +86 -83
  48. package/dist/keys/userKeyRoster.js.map +1 -0
  49. package/dist/recovery/index.d.ts +1 -1
  50. package/dist/recovery/index.js +1 -1
  51. package/dist/recovery/recoveryCode.d.ts +1 -1
  52. package/dist/recovery/recoveryCode.js +6 -6
  53. package/dist/recovery/recoveryRecord.d.ts +1 -1
  54. package/dist/recovery/recoveryWebvh.d.ts +1 -1
  55. package/dist/recovery/recoveryWebvh.js +1 -1
  56. package/dist/space/activity.d.ts +1 -1
  57. package/dist/space/activity.js +1 -1
  58. package/dist/space/collections.d.ts +17 -17
  59. package/dist/space/collections.d.ts.map +1 -1
  60. package/dist/space/collections.js +16 -16
  61. package/dist/space/collections.js.map +1 -1
  62. package/dist/space/index.d.ts +2 -2
  63. package/dist/space/index.d.ts.map +1 -1
  64. package/dist/space/index.js +2 -2
  65. package/dist/space/index.js.map +1 -1
  66. package/dist/webvh/didWebvh.d.ts +1 -1
  67. package/dist/webvh/didWebvh.js +6 -6
  68. package/package.json +1 -1
  69. package/dist/keys/puk.d.ts +0 -42
  70. package/dist/keys/puk.d.ts.map +0 -1
  71. package/dist/keys/puk.js.map +0 -1
  72. package/dist/keys/pukCascade.d.ts.map +0 -1
  73. package/dist/keys/pukCascade.js.map +0 -1
  74. package/dist/keys/pukRoster.d.ts.map +0 -1
  75. package/dist/keys/pukRoster.js.map +0 -1
@@ -2,92 +2,92 @@
2
2
  * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
3
  */
4
4
  /**
5
- * The per-collection half of the PUK rotation cascade: once the roster's
6
- * current epoch has moved to a fresh PUK (a client revoked, a recovery code
7
- * spent or revoked), every encrypted collection must take a new epoch naming
8
- * the fresh PUK and retire the old one -- otherwise writes keep landing under
9
- * an epoch key the revoked party still holds.
5
+ * The per-collection half of the user key rotation cascade: once the roster's
6
+ * current epoch has moved to a fresh user key (a client revoked, a recovery
7
+ * code spent or revoked), every encrypted collection must take a new epoch
8
+ * naming the fresh user key and retire the old one -- otherwise writes keep
9
+ * landing under an epoch key the revoked party still holds.
10
10
  *
11
11
  * The staleness rule is the one the revocation spike verified: **a collection
12
- * is stale exactly when its current epoch names a PUK KAK other than the
12
+ * is stale exactly when its current epoch names a user key KAK other than the
13
13
  * roster's current** -- detectable from durable data alone, no checkpoint
14
- * resource anywhere, which is what makes a crashed cascade resumable by a
15
- * naive full re-run (and what the login-time completion sweep re-checks).
16
- * Which KAKs are "PUK generations" is read from the roster itself: its epochs
17
- * ARE the PUK generations, each escrow-wrapped to every enrolled client, so
18
- * any enrolled client can recover any generation's key
19
- * ({@link unwrapPukGenerations}) -- needed both to recognize a stale epoch
20
- * and to escrow the fresh PUK into a stranded collection's history.
14
+ * resource anywhere, which is what makes a crashed cascade resumable by a naive
15
+ * full re-run (and what the login-time completion sweep re-checks). Which KAKs
16
+ * are "user key generations" is read from the roster itself: its epochs ARE the
17
+ * user key generations, each escrow-wrapped to every enrolled client, so any
18
+ * enrolled client can recover any generation's key ({@link
19
+ * unwrapUserKeyGenerations}) -- needed both to recognize a stale epoch and to
20
+ * escrow the fresh user key into a stranded collection's history.
21
21
  *
22
22
  * One residue, accepted: a collection with NO epochs gets the newest prior
23
23
  * generation installed as its first epoch (pre-epoch envelopes are sealed to
24
- * the era's PUK KAK, and the PUK IS the epoch construction, so they ARE
25
- * epoch-of-that-generation envelopes) -- envelopes older than that
26
- * generation, on a collection every prior cascade missed, stay readable only
27
- * to sessions that held the older key.
24
+ * the era's user key KAK, and the user key IS the epoch construction, so they
25
+ * ARE epoch-of-that-generation envelopes) -- envelopes older than that
26
+ * generation, on a collection every prior cascade missed, stay readable only to
27
+ * sessions that held the older key.
28
28
  */
29
29
  import type { IKeyAgreementKey } from '@interop/data-integrity-core';
30
30
  import type { CollectionEncryption } from '@interop/was-client';
31
31
  import { type EncryptionDescriptorStore, type RecipientPublicKey } from '@interop/was-client/edv';
32
- import { type Puk } from './puk.js';
32
+ import { type UserKey } from './userKey.js';
33
33
  /**
34
- * A PUK presented as an epoch-roster recipient: the kid is the
34
+ * A user key presented as an epoch-roster recipient: the kid is the
35
35
  * self-describing `<did:key>#<fingerprint>` form every collection epoch names
36
36
  * the user under, and the public key is the did:key's own multibase.
37
37
  *
38
38
  * @param options {object}
39
- * @param options.puk {Puk}
39
+ * @param options.userKey {UserKey}
40
40
  * @returns {RecipientPublicKey}
41
41
  */
42
- export declare function pukAsRecipient({ puk }: {
43
- puk: Puk;
42
+ export declare function userKeyAsRecipient({ userKey }: {
43
+ userKey: UserKey;
44
44
  }): RecipientPublicKey;
45
45
  /**
46
- * Recovers every PUK generation from the roster descriptor: each roster epoch
47
- * is one generation (its id the generation's did:key, its wrapped secret the
48
- * generation's raw key), escrow-wrapped to every enrolled client -- so this
49
- * client's key-agreement key unwraps them all, in roster (chronological)
50
- * order. A generation whose wrap is missing or fails to unwrap is skipped
51
- * rather than fatal (the cascade then simply cannot recognize or escrow that
52
- * generation; the current epoch always unwraps or the roster read itself
53
- * would have refused).
46
+ * Recovers every user key generation from the roster descriptor: each roster
47
+ * epoch is one generation (its id the generation's did:key, its wrapped secret
48
+ * the generation's raw key), escrow-wrapped to every enrolled client -- so this
49
+ * client's key-agreement key unwraps them all, in roster (chronological) order.
50
+ * A generation whose wrap is missing or fails to unwrap is skipped rather than
51
+ * fatal (the cascade then simply cannot recognize or escrow that generation;
52
+ * the current epoch always unwraps or the roster read itself would have
53
+ * refused).
54
54
  *
55
55
  * @param options {object}
56
56
  * @param options.descriptor {CollectionEncryption} the roster descriptor
57
57
  * @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
58
58
  * (identity) key-agreement key
59
- * @returns {Promise<Puk[]>} the generations, oldest first
59
+ * @returns {Promise<UserKey[]>} the generations, oldest first
60
60
  */
61
- export declare function unwrapPukGenerations({ descriptor, clientKeyAgreementKey }: {
61
+ export declare function unwrapUserKeyGenerations({ descriptor, clientKeyAgreementKey }: {
62
62
  descriptor: CollectionEncryption;
63
63
  clientKeyAgreementKey: IKeyAgreementKey;
64
- }): Promise<Puk[]>;
64
+ }): Promise<UserKey[]>;
65
65
  /**
66
- * What one collection's cascade step did: `noop` (already on the current
67
- * PUK), `installed` (a pre-epoch collection on a first-generation account
68
- * gained its first epoch; nothing to retire), `escrowed` (the current PUK's
69
- * wrap was completed into history with no stale epoch to rotate), or
70
- * `rotated` (a fresh epoch sealed to the current PUK, the stale generations
71
- * retired -- the pre-epoch install falls through to this).
66
+ * What one collection's cascade step did: `noop` (already on the current user
67
+ * key), `installed` (a pre-epoch collection on a first-generation account
68
+ * gained its first epoch; nothing to retire), `escrowed` (the current user
69
+ * key's wrap was completed into history with no stale epoch to rotate), or
70
+ * `rotated` (a fresh epoch sealed to the current user key, the stale
71
+ * generations retired -- the pre-epoch install falls through to this).
72
72
  */
73
- export type CollectionPukRotationOutcome = 'noop' | 'installed' | 'escrowed' | 'rotated';
73
+ export type CollectionUserKeyRotationOutcome = 'noop' | 'installed' | 'escrowed' | 'rotated';
74
74
  /**
75
- * Brings ONE encrypted collection's epoch roster onto the current PUK -- the
76
- * per-collection op of the revocation cascade and of the completion sweep:
75
+ * Brings ONE encrypted collection's epoch roster onto the current user key --
76
+ * the per-collection op of the revocation cascade and of the completion sweep:
77
77
  *
78
78
  * - **No epochs yet**: install the newest PRIOR generation as the first epoch
79
- * (see the module doc), wrapped to that generation and the current PUK,
79
+ * (see the module doc), wrapped to that generation and the current user key,
80
80
  * then fall through to the rotation. A first-generation account (nothing
81
- * prior) installs the current PUK alone and is done.
81
+ * prior) installs the current user key alone and is done.
82
82
  * - **Stale current epoch** (names a non-current generation): one
83
- * `replaceRecipient` write -- the current PUK escrowed into every epoch,
83
+ * `replaceRecipient` write -- the current user key escrowed into every epoch,
84
84
  * a fresh epoch minted without the stale generations. Two requests per
85
85
  * collection; app recipients and other readers ride through untouched (the
86
86
  * default did:key resolver re-wraps them).
87
87
  * - **Current already** and fully escrowed: no write at all, so a naive
88
88
  * re-run after a mid-cascade crash converges with zero redundant epochs.
89
89
  *
90
- * The pull axis is deliberately a no-op here: a PUK rotation follows a
90
+ * The pull axis is deliberately a no-op here: a user key rotation follows a
91
91
  * document edit (client revocation, code retirement) that already killed the
92
92
  * revoked party's server-side access everywhere under the current-key-set
93
93
  * rule -- there is no per-collection revoke.
@@ -95,35 +95,35 @@ export type CollectionPukRotationOutcome = 'noop' | 'installed' | 'escrowed' | '
95
95
  * @param options {object}
96
96
  * @param options.store {EncryptionDescriptorStore} the collection's
97
97
  * descriptor store
98
- * @param options.puk {Puk} the roster's CURRENT PUK
99
- * @param options.generations {Puk[]} every roster generation this client
100
- * could unwrap ({@link unwrapPukGenerations}), oldest first
101
- * @returns {Promise<CollectionPukRotationOutcome>}
98
+ * @param options.userKey {UserKey} the roster's CURRENT user key
99
+ * @param options.generations {UserKey[]} every roster generation this client
100
+ * could unwrap ({@link unwrapUserKeyGenerations}), oldest first
101
+ * @returns {Promise<CollectionUserKeyRotationOutcome>}
102
102
  */
103
- export declare function rotateCollectionEpochsToPuk({ store, puk, generations }: {
103
+ export declare function rotateCollectionEpochsToUserKey({ store, userKey, generations }: {
104
104
  store: EncryptionDescriptorStore;
105
- puk: Puk;
106
- generations: Puk[];
107
- }): Promise<CollectionPukRotationOutcome>;
105
+ userKey: UserKey;
106
+ generations: UserKey[];
107
+ }): Promise<CollectionUserKeyRotationOutcome>;
108
108
  /**
109
109
  * What the collection fan-out did, per collection id: the outcomes for every
110
110
  * collection that needed (or took) work, and the per-collection failures the
111
111
  * caller surfaces -- the cascade never aborts on one stuck collection.
112
112
  */
113
- export interface PukCascadeResult {
114
- outcomes: Record<string, CollectionPukRotationOutcome>;
113
+ export interface UserKeyCascadeResult {
114
+ outcomes: Record<string, CollectionUserKeyRotationOutcome>;
115
115
  failed: Array<{
116
116
  collectionId: string;
117
117
  error: unknown;
118
118
  }>;
119
119
  }
120
120
  /**
121
- * The collection fan-out of the PUK rotation cascade: re-epochs every named
122
- * collection onto the roster's current PUK, in parallel, unwrapping the PUK
123
- * generations from the roster once. The wallet supplies what only it knows --
124
- * which collections exist (`collectionIds`) and how to reach each one's
125
- * descriptor (`storeFor`); the per-collection staleness rule and re-epoch live
126
- * in {@link rotateCollectionEpochsToPuk}.
121
+ * The collection fan-out of the user key rotation cascade: re-epochs every
122
+ * named collection onto the roster's current user key, in parallel, unwrapping
123
+ * the user key generations from the roster once. The wallet supplies what only
124
+ * it knows -- which collections exist (`collectionIds`) and how to reach each
125
+ * one's descriptor (`storeFor`); the per-collection staleness rule and re-epoch
126
+ * live in {@link rotateCollectionEpochsToUserKey}.
127
127
  *
128
128
  * A collection that fails is reported in `failed` and the rest proceed; the
129
129
  * caller decides what a failure means (the login-time completion sweep is the
@@ -140,18 +140,18 @@ export interface PukCascadeResult {
140
140
  * own task so a throwing check lands in that collection's `failed` entry
141
141
  * (e.g. a standard collection an account never provisioned server-side)
142
142
  * @param options.rosterDescriptor {CollectionEncryption} the freshly read
143
- * `key-map/puk.json` roster (the source of the PUK generations)
143
+ * `key-map/user-key.json` roster (the source of the user key generations)
144
144
  * @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
145
145
  * (identity) key-agreement key, unwrapping the generations
146
- * @param options.puk {Puk} the roster's current PUK
147
- * @returns {Promise<PukCascadeResult>}
146
+ * @param options.userKey {UserKey} the roster's current user key
147
+ * @returns {Promise<UserKeyCascadeResult>}
148
148
  */
149
- export declare function cascadeCollectionsToPuk({ collectionIds, storeFor, isEncrypted, rosterDescriptor, clientKeyAgreementKey, puk }: {
149
+ export declare function cascadeCollectionsToUserKey({ collectionIds, storeFor, isEncrypted, rosterDescriptor, clientKeyAgreementKey, userKey }: {
150
150
  collectionIds: string[];
151
151
  storeFor: (collectionId: string) => EncryptionDescriptorStore;
152
152
  isEncrypted?: (collectionId: string) => Promise<boolean>;
153
153
  rosterDescriptor: CollectionEncryption;
154
154
  clientKeyAgreementKey: IKeyAgreementKey;
155
- puk: Puk;
156
- }): Promise<PukCascadeResult>;
157
- //# sourceMappingURL=pukCascade.d.ts.map
155
+ userKey: UserKey;
156
+ }): Promise<UserKeyCascadeResult>;
157
+ //# sourceMappingURL=userKeyCascade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userKeyCascade.d.ts","sourceRoot":"","sources":["../../src/keys/userKeyCascade.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAML,KAAK,yBAAyB,EAC9B,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAAoB,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAE7D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACR,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;CACjB,GAAG,kBAAkB,CAKrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,wBAAwB,CAAC,EAC7C,UAAU,EACV,qBAAqB,EACtB,EAAE;IACD,UAAU,EAAE,oBAAoB,CAAA;IAChC,qBAAqB,EAAE,gBAAgB,CAAA;CACxC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAkBrB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,gCAAgC,GAC1C,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAA;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,+BAA+B,CAAC,EACpD,KAAK,EACL,OAAO,EACP,WAAW,EACZ,EAAE;IACD,KAAK,EAAE,yBAAyB,CAAA;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,OAAO,EAAE,CAAA;CACvB,GAAG,OAAO,CAAC,gCAAgC,CAAC,CA6H5C;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gCAAgC,CAAC,CAAA;IAC1D,MAAM,EAAE,KAAK,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CACxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,2BAA2B,CAAC,EAChD,aAAa,EACb,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,OAAO,EACR,EAAE;IACD,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,yBAAyB,CAAA;IAC7D,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACxD,gBAAgB,EAAE,oBAAoB,CAAA;IACtC,qBAAqB,EAAE,gBAAgB,CAAA;IACvC,OAAO,EAAE,OAAO,CAAA;CACjB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwBhC"}
@@ -1,38 +1,38 @@
1
1
  import { addRecipient, epochKeyIdFor, initRecipients, replaceRecipient, unwrapEpochSecret } from '@interop/was-client/edv';
2
2
  import { ValidationError } from '@interop/was-client';
3
- import { pukVaultKeys } from './puk.js';
3
+ import { userKeyVaultKeys } from './userKey.js';
4
4
  /**
5
- * A PUK presented as an epoch-roster recipient: the kid is the
5
+ * A user key presented as an epoch-roster recipient: the kid is the
6
6
  * self-describing `<did:key>#<fingerprint>` form every collection epoch names
7
7
  * the user under, and the public key is the did:key's own multibase.
8
8
  *
9
9
  * @param options {object}
10
- * @param options.puk {Puk}
10
+ * @param options.userKey {UserKey}
11
11
  * @returns {RecipientPublicKey}
12
12
  */
13
- export function pukAsRecipient({ puk }) {
13
+ export function userKeyAsRecipient({ userKey }) {
14
14
  return {
15
- id: epochKeyIdFor(puk.id),
16
- publicKeyMultibase: puk.id.split(':')[2]
15
+ id: epochKeyIdFor(userKey.id),
16
+ publicKeyMultibase: userKey.id.split(':')[2]
17
17
  };
18
18
  }
19
19
  /**
20
- * Recovers every PUK generation from the roster descriptor: each roster epoch
21
- * is one generation (its id the generation's did:key, its wrapped secret the
22
- * generation's raw key), escrow-wrapped to every enrolled client -- so this
23
- * client's key-agreement key unwraps them all, in roster (chronological)
24
- * order. A generation whose wrap is missing or fails to unwrap is skipped
25
- * rather than fatal (the cascade then simply cannot recognize or escrow that
26
- * generation; the current epoch always unwraps or the roster read itself
27
- * would have refused).
20
+ * Recovers every user key generation from the roster descriptor: each roster
21
+ * epoch is one generation (its id the generation's did:key, its wrapped secret
22
+ * the generation's raw key), escrow-wrapped to every enrolled client -- so this
23
+ * client's key-agreement key unwraps them all, in roster (chronological) order.
24
+ * A generation whose wrap is missing or fails to unwrap is skipped rather than
25
+ * fatal (the cascade then simply cannot recognize or escrow that generation;
26
+ * the current epoch always unwraps or the roster read itself would have
27
+ * refused).
28
28
  *
29
29
  * @param options {object}
30
30
  * @param options.descriptor {CollectionEncryption} the roster descriptor
31
31
  * @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
32
32
  * (identity) key-agreement key
33
- * @returns {Promise<Puk[]>} the generations, oldest first
33
+ * @returns {Promise<UserKey[]>} the generations, oldest first
34
34
  */
35
- export async function unwrapPukGenerations({ descriptor, clientKeyAgreementKey }) {
35
+ export async function unwrapUserKeyGenerations({ descriptor, clientKeyAgreementKey }) {
36
36
  const generations = [];
37
37
  for (const epoch of descriptor.epochs ?? []) {
38
38
  const entry = epoch.recipients.find(recipient => recipient.header.kid === clientKeyAgreementKey.id);
@@ -50,22 +50,22 @@ export async function unwrapPukGenerations({ descriptor, clientKeyAgreementKey }
50
50
  return generations;
51
51
  }
52
52
  /**
53
- * Brings ONE encrypted collection's epoch roster onto the current PUK -- the
54
- * per-collection op of the revocation cascade and of the completion sweep:
53
+ * Brings ONE encrypted collection's epoch roster onto the current user key --
54
+ * the per-collection op of the revocation cascade and of the completion sweep:
55
55
  *
56
56
  * - **No epochs yet**: install the newest PRIOR generation as the first epoch
57
- * (see the module doc), wrapped to that generation and the current PUK,
57
+ * (see the module doc), wrapped to that generation and the current user key,
58
58
  * then fall through to the rotation. A first-generation account (nothing
59
- * prior) installs the current PUK alone and is done.
59
+ * prior) installs the current user key alone and is done.
60
60
  * - **Stale current epoch** (names a non-current generation): one
61
- * `replaceRecipient` write -- the current PUK escrowed into every epoch,
61
+ * `replaceRecipient` write -- the current user key escrowed into every epoch,
62
62
  * a fresh epoch minted without the stale generations. Two requests per
63
63
  * collection; app recipients and other readers ride through untouched (the
64
64
  * default did:key resolver re-wraps them).
65
65
  * - **Current already** and fully escrowed: no write at all, so a naive
66
66
  * re-run after a mid-cascade crash converges with zero redundant epochs.
67
67
  *
68
- * The pull axis is deliberately a no-op here: a PUK rotation follows a
68
+ * The pull axis is deliberately a no-op here: a user key rotation follows a
69
69
  * document edit (client revocation, code retirement) that already killed the
70
70
  * revoked party's server-side access everywhere under the current-key-set
71
71
  * rule -- there is no per-collection revoke.
@@ -73,32 +73,35 @@ export async function unwrapPukGenerations({ descriptor, clientKeyAgreementKey }
73
73
  * @param options {object}
74
74
  * @param options.store {EncryptionDescriptorStore} the collection's
75
75
  * descriptor store
76
- * @param options.puk {Puk} the roster's CURRENT PUK
77
- * @param options.generations {Puk[]} every roster generation this client
78
- * could unwrap ({@link unwrapPukGenerations}), oldest first
79
- * @returns {Promise<CollectionPukRotationOutcome>}
76
+ * @param options.userKey {UserKey} the roster's CURRENT user key
77
+ * @param options.generations {UserKey[]} every roster generation this client
78
+ * could unwrap ({@link unwrapUserKeyGenerations}), oldest first
79
+ * @returns {Promise<CollectionUserKeyRotationOutcome>}
80
80
  */
81
- export async function rotateCollectionEpochsToPuk({ store, puk, generations }) {
81
+ export async function rotateCollectionEpochsToUserKey({ store, userKey, generations }) {
82
82
  const current = await store.read();
83
83
  if (current === null) {
84
84
  return 'noop';
85
85
  }
86
86
  let descriptor = current.descriptor;
87
- const staleGenerations = generations.filter(generation => generation.id !== puk.id);
87
+ const staleGenerations = generations.filter(generation => generation.id !== userKey.id);
88
88
  if (!descriptor.epochs?.length || !descriptor.currentEpoch) {
89
- // Pre-epoch: the collection's envelopes are sealed to the era's PUK KAK,
90
- // which -- the PUK being the epoch construction -- makes them epoch-of-
89
+ // Pre-epoch: the collection's envelopes are sealed to the era's user key KAK,
90
+ // which -- the user key being the epoch construction -- makes them epoch-of-
91
91
  // that-generation envelopes. Install that generation as the first epoch.
92
92
  const previous = staleGenerations[staleGenerations.length - 1];
93
93
  try {
94
94
  const installed = await initRecipients({
95
95
  store,
96
96
  recipients: previous
97
- ? [pukAsRecipient({ puk: previous }), pukAsRecipient({ puk })]
98
- : [pukAsRecipient({ puk })],
97
+ ? [
98
+ userKeyAsRecipient({ userKey: previous }),
99
+ userKeyAsRecipient({ userKey })
100
+ ]
101
+ : [userKeyAsRecipient({ userKey })],
99
102
  epoch: previous
100
103
  ? { epochId: previous.id, secret: previous.secret }
101
- : { epochId: puk.id, secret: puk.secret }
104
+ : { epochId: userKey.id, secret: userKey.secret }
102
105
  });
103
106
  if (!previous) {
104
107
  // A first-generation account: nothing prior to escrow, nothing to
@@ -126,65 +129,65 @@ export async function rotateCollectionEpochsToPuk({ store, puk, generations }) {
126
129
  const staleKids = staleGenerations
127
130
  .map(generation => epochKeyIdFor(generation.id))
128
131
  .filter(kid => currentKids.has(kid));
129
- const pukKid = epochKeyIdFor(puk.id);
132
+ const userKeyKid = epochKeyIdFor(userKey.id);
130
133
  if (staleKids.length === 0) {
131
- const escrowComplete = (descriptor.epochs ?? []).every(epoch => epoch.recipients.some(entry => entry.header.kid === pukKid));
132
- if (currentKids.has(pukKid) && escrowComplete) {
134
+ const escrowComplete = (descriptor.epochs ?? []).every(epoch => epoch.recipients.some(entry => entry.header.kid === userKeyKid));
135
+ if (currentKids.has(userKeyKid) && escrowComplete) {
133
136
  return 'noop';
134
137
  }
135
138
  }
136
139
  if (staleKids.length > 0) {
137
140
  // The escrow owner: the newest stale generation still named by the
138
141
  // current epoch, which the cascade invariant escrowed into every prior
139
- // epoch -- so it unwraps the whole history for the fresh PUK's escrow.
142
+ // epoch -- so it unwraps the whole history for the fresh user key's escrow.
140
143
  const ownerGeneration = [...staleGenerations]
141
144
  .reverse()
142
145
  .find(generation => currentKids.has(epochKeyIdFor(generation.id)));
143
- const owner = pukVaultKeys({ puk: ownerGeneration });
146
+ const owner = userKeyVaultKeys({ userKey: ownerGeneration });
144
147
  await replaceRecipient({
145
148
  store,
146
149
  retire: staleKids,
147
- recipient: pukAsRecipient({ puk }),
150
+ recipient: userKeyAsRecipient({ userKey }),
148
151
  owner: { keyAgreementKey: owner.keyAgreementKey },
149
152
  pull: async () => { }
150
153
  });
151
154
  return 'rotated';
152
155
  }
153
- if (!currentKids.has(pukKid)) {
154
- // No stale generation to retire, but the current PUK is not a recipient
155
- // at all: this collection's roster is not PUK-owned in a shape this
156
+ if (!currentKids.has(userKeyKid)) {
157
+ // No stale generation to retire, but the current user key is not a recipient
158
+ // at all: this collection's roster is not user-key-owned in a shape this
156
159
  // cascade can heal -- surface it rather than silently minting an epoch
157
160
  // whose history the account cannot read.
158
- throw new Error('The collection current epoch names no PUK generation this client ' +
159
- 'can recognize; its roster cannot be rotated to the current PUK.');
161
+ throw new Error('The collection current epoch names no user key generation this client ' +
162
+ 'can recognize; its roster cannot be rotated to the current user key.');
160
163
  }
161
- // Escrow completion only: the current epoch is already on the current PUK,
164
+ // Escrow completion only: the current epoch is already on the current user key,
162
165
  // but some historical epoch is missing its wrap. The owner must unwrap
163
166
  // exactly those epochs -- the newest generation named by every incomplete
164
167
  // epoch.
165
- const incomplete = (descriptor.epochs ?? []).filter(epoch => !epoch.recipients.some(entry => entry.header.kid === pukKid));
168
+ const incomplete = (descriptor.epochs ?? []).filter(epoch => !epoch.recipients.some(entry => entry.header.kid === userKeyKid));
166
169
  const escrowOwner = [...generations]
167
170
  .reverse()
168
171
  .find(generation => incomplete.every(epoch => epoch.recipients.some(entry => entry.header.kid === epochKeyIdFor(generation.id))));
169
172
  if (!escrowOwner) {
170
- throw new Error("The current PUK cannot be escrowed into this collection's history: " +
171
- 'no held PUK generation is a recipient of every incomplete epoch.');
173
+ throw new Error("The current user key cannot be escrowed into this collection's history: " +
174
+ 'no held user key generation is a recipient of every incomplete epoch.');
172
175
  }
173
- const owner = pukVaultKeys({ puk: escrowOwner });
176
+ const owner = userKeyVaultKeys({ userKey: escrowOwner });
174
177
  await addRecipient({
175
178
  store,
176
- recipient: pukAsRecipient({ puk }),
179
+ recipient: userKeyAsRecipient({ userKey }),
177
180
  owner: { keyAgreementKey: owner.keyAgreementKey }
178
181
  });
179
182
  return 'escrowed';
180
183
  }
181
184
  /**
182
- * The collection fan-out of the PUK rotation cascade: re-epochs every named
183
- * collection onto the roster's current PUK, in parallel, unwrapping the PUK
184
- * generations from the roster once. The wallet supplies what only it knows --
185
- * which collections exist (`collectionIds`) and how to reach each one's
186
- * descriptor (`storeFor`); the per-collection staleness rule and re-epoch live
187
- * in {@link rotateCollectionEpochsToPuk}.
185
+ * The collection fan-out of the user key rotation cascade: re-epochs every
186
+ * named collection onto the roster's current user key, in parallel, unwrapping
187
+ * the user key generations from the roster once. The wallet supplies what only
188
+ * it knows -- which collections exist (`collectionIds`) and how to reach each
189
+ * one's descriptor (`storeFor`); the per-collection staleness rule and re-epoch
190
+ * live in {@link rotateCollectionEpochsToUserKey}.
188
191
  *
189
192
  * A collection that fails is reported in `failed` and the rest proceed; the
190
193
  * caller decides what a failure means (the login-time completion sweep is the
@@ -201,14 +204,14 @@ export async function rotateCollectionEpochsToPuk({ store, puk, generations }) {
201
204
  * own task so a throwing check lands in that collection's `failed` entry
202
205
  * (e.g. a standard collection an account never provisioned server-side)
203
206
  * @param options.rosterDescriptor {CollectionEncryption} the freshly read
204
- * `key-map/puk.json` roster (the source of the PUK generations)
207
+ * `key-map/user-key.json` roster (the source of the user key generations)
205
208
  * @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
206
209
  * (identity) key-agreement key, unwrapping the generations
207
- * @param options.puk {Puk} the roster's current PUK
208
- * @returns {Promise<PukCascadeResult>}
210
+ * @param options.userKey {UserKey} the roster's current user key
211
+ * @returns {Promise<UserKeyCascadeResult>}
209
212
  */
210
- export async function cascadeCollectionsToPuk({ collectionIds, storeFor, isEncrypted, rosterDescriptor, clientKeyAgreementKey, puk }) {
211
- const generations = await unwrapPukGenerations({
213
+ export async function cascadeCollectionsToUserKey({ collectionIds, storeFor, isEncrypted, rosterDescriptor, clientKeyAgreementKey, userKey }) {
214
+ const generations = await unwrapUserKeyGenerations({
212
215
  descriptor: rosterDescriptor,
213
216
  clientKeyAgreementKey
214
217
  });
@@ -219,9 +222,9 @@ export async function cascadeCollectionsToPuk({ collectionIds, storeFor, isEncry
219
222
  if (isEncrypted && !(await isEncrypted(collectionId))) {
220
223
  return;
221
224
  }
222
- outcomes[collectionId] = await rotateCollectionEpochsToPuk({
225
+ outcomes[collectionId] = await rotateCollectionEpochsToUserKey({
223
226
  store: storeFor(collectionId),
224
- puk,
227
+ userKey,
225
228
  generations
226
229
  });
227
230
  }
@@ -231,4 +234,4 @@ export async function cascadeCollectionsToPuk({ collectionIds, storeFor, isEncry
231
234
  }));
232
235
  return { outcomes, failed };
233
236
  }
234
- //# sourceMappingURL=pukCascade.js.map
237
+ //# sourceMappingURL=userKeyCascade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userKeyCascade.js","sourceRoot":"","sources":["../../src/keys/userKeyCascade.ts"],"names":[],"mappings":"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"}