@interop/wallet-core 0.20.0 → 0.22.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 (66) hide show
  1. package/README.md +5 -2
  2. package/dist/descriptors/cipher.d.ts +4 -1
  3. package/dist/descriptors/cipher.d.ts.map +1 -1
  4. package/dist/descriptors/cipher.js +16 -8
  5. package/dist/descriptors/cipher.js.map +1 -1
  6. package/dist/keyring/record.d.ts +69 -18
  7. package/dist/keyring/record.d.ts.map +1 -1
  8. package/dist/keyring/record.js +90 -23
  9. package/dist/keyring/record.js.map +1 -1
  10. package/dist/keys/index.d.ts +4 -0
  11. package/dist/keys/index.d.ts.map +1 -1
  12. package/dist/keys/index.js +4 -0
  13. package/dist/keys/index.js.map +1 -1
  14. package/dist/keys/spaceEpochs.d.ts +56 -0
  15. package/dist/keys/spaceEpochs.d.ts.map +1 -0
  16. package/dist/keys/spaceEpochs.js +55 -0
  17. package/dist/keys/spaceEpochs.js.map +1 -0
  18. package/dist/keys/userKeyCascade.d.ts +14 -16
  19. package/dist/keys/userKeyCascade.d.ts.map +1 -1
  20. package/dist/keys/userKeyCascade.js +9 -43
  21. package/dist/keys/userKeyCascade.js.map +1 -1
  22. package/dist/recovery/recoveryRecord.d.ts +16 -13
  23. package/dist/recovery/recoveryRecord.d.ts.map +1 -1
  24. package/dist/recovery/recoveryRecord.js +14 -17
  25. package/dist/recovery/recoveryRecord.js.map +1 -1
  26. package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
  27. package/dist/recovery/recoveryWebvh.js +4 -0
  28. package/dist/recovery/recoveryWebvh.js.map +1 -1
  29. package/dist/space/collections.d.ts +66 -32
  30. package/dist/space/collections.d.ts.map +1 -1
  31. package/dist/space/collections.js +88 -12
  32. package/dist/space/collections.js.map +1 -1
  33. package/dist/space/index.d.ts +13 -5
  34. package/dist/space/index.d.ts.map +1 -1
  35. package/dist/space/index.js +12 -4
  36. package/dist/space/index.js.map +1 -1
  37. package/dist/space/provisioning.d.ts +46 -0
  38. package/dist/space/provisioning.d.ts.map +1 -0
  39. package/dist/space/provisioning.js +52 -0
  40. package/dist/space/provisioning.js.map +1 -0
  41. package/dist/sync/engine.d.ts +17 -1
  42. package/dist/sync/engine.d.ts.map +1 -1
  43. package/dist/sync/engine.js +5 -0
  44. package/dist/sync/engine.js.map +1 -1
  45. package/dist/sync/index.d.ts +8 -3
  46. package/dist/sync/index.d.ts.map +1 -1
  47. package/dist/sync/index.js +8 -3
  48. package/dist/sync/index.js.map +1 -1
  49. package/dist/sync/push.d.ts.map +1 -1
  50. package/dist/sync/push.js +3 -2
  51. package/dist/sync/push.js.map +1 -1
  52. package/dist/sync/remint.d.ts +78 -0
  53. package/dist/sync/remint.d.ts.map +1 -0
  54. package/dist/sync/remint.js +73 -0
  55. package/dist/sync/remint.js.map +1 -0
  56. package/dist/sync/types.d.ts +26 -4
  57. package/dist/sync/types.d.ts.map +1 -1
  58. package/dist/sync/types.js +4 -4
  59. package/dist/sync/types.js.map +1 -1
  60. package/dist/webvh/didWebvh.d.ts.map +1 -1
  61. package/dist/webvh/didWebvh.js +3 -0
  62. package/dist/webvh/didWebvh.js.map +1 -1
  63. package/dist/webvh/revokeClient.d.ts.map +1 -1
  64. package/dist/webvh/revokeClient.js +1 -0
  65. package/dist/webvh/revokeClient.js.map +1 -1
  66. package/package.json +5 -5
package/README.md CHANGED
@@ -81,8 +81,11 @@ The subpaths:
81
81
  staleness detected from durable state alone, history escrowed -- also the
82
82
  completion sweep's building block), plus the detector that converges a roster
83
83
  left wrapping the current key to a recipient the account document no longer
84
- keys. Also the enrolled-client display labels (`key-map/client-labels.json`)
85
- and their WAS-backed store. Also the client-key record codec: the contents and
84
+ keys. Also `ensureWalletSpaceEpochs`, the provision-time install of each
85
+ encrypted wallet collection's key epoch[0] (a fresh random epoch key wrapped
86
+ to the user key) -- the EDV-bearing second step of `provisionWalletSpace`.
87
+ Also the enrolled-client display labels (`key-map/client-labels.json`) and
88
+ their WAS-backed store. Also the client-key record codec: the contents and
86
89
  strict validation of the local record each wallet client keeps its own key
87
90
  material in (storage and wrapping stay app-side).
88
91
 
@@ -10,7 +10,10 @@
10
10
  * Built, the cipher acquires the collection's descriptor (fetch, cache the
11
11
  * success, cached fallback on failure -- see `acquire.ts`) and constructs the
12
12
  * underlying EDV cipher from it; with no descriptor, or a descriptor with no
13
- * epochs, that is the single-key path, unchanged. When a decrypt throws
13
+ * epochs, the build refuses fail-closed (every encrypted collection's
14
+ * descriptor carries an epoch roster from provisioning, so the absence can
15
+ * only mean the install has not landed or the host is lying). When a decrypt
16
+ * throws
14
17
  * `UnknownEpochError`, the cipher re-acquires the descriptor, rebuilds itself,
15
18
  * and retries that decrypt exactly once -- and only once per cipher instance,
16
19
  * which the host scopes to one `(profile, collection)` session by dropping
@@ -1 +1 @@
1
- {"version":3,"file":"cipher.d.ts","sourceRoot":"","sources":["../../src/descriptors/cipher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAChC,MAAM,cAAc,CAAA;AAErB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,4BAA4B,CAAC,EACjD,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,YAAY,EACb,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACnC,MAAM,CAAC,EAAE,0BAA0B,CAAA;IACnC,KAAK,EAAE,yBAAyB,CAAA;IAChC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE,GAAG,OAAO,CAAC,SAAS,CAAC,CAqDrB"}
1
+ {"version":3,"file":"cipher.d.ts","sourceRoot":"","sources":["../../src/descriptors/cipher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAChC,MAAM,cAAc,CAAA;AAErB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,4BAA4B,CAAC,EACjD,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,YAAY,EACb,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACnC,MAAM,CAAC,EAAE,0BAA0B,CAAA;IACnC,KAAK,EAAE,yBAAyB,CAAA;IAChC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE,GAAG,OAAO,CAAC,SAAS,CAAC,CA8DrB"}
@@ -23,18 +23,26 @@ import { acquireDescriptor } from './acquire.js';
23
23
  * @returns {Promise<DocCipher>}
24
24
  */
25
25
  export async function createRefreshingEdvDocCipher({ keyAgreementKey, keyResolver, collectionId, idDerivation, source, cache, onFetchError }) {
26
- const build = async () => createEdvDocCipher({
27
- keyAgreementKey,
28
- keyResolver,
29
- collectionId,
30
- idDerivation,
31
- encryption: await acquireDescriptor({
26
+ const build = async () => {
27
+ const encryption = await acquireDescriptor({
32
28
  source,
33
29
  cache,
34
30
  collectionId,
35
31
  onFetchError
36
- })
37
- });
32
+ });
33
+ if (!encryption) {
34
+ throw new Error(`Collection "${collectionId}" has no encryption descriptor available ` +
35
+ '(fetched or cached). Every encrypted collection carries its key ' +
36
+ 'epochs from provisioning; refusing to build a cipher without them.');
37
+ }
38
+ return createEdvDocCipher({
39
+ keyAgreementKey,
40
+ keyResolver,
41
+ collectionId,
42
+ idDerivation,
43
+ encryption
44
+ });
45
+ };
38
46
  let inner = await build();
39
47
  // The one descriptor refresh this cipher instance (= this collection this
40
48
  // session) may spend, shared so concurrent unknown-epoch decrypts ride a
@@ -1 +1 @@
1
- {"version":3,"file":"cipher.js","sourceRoot":"","sources":["../../src/descriptors/cipher.ts"],"names":[],"mappings":"AAuBA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,iBAAiB,EAGlB,MAAM,cAAc,CAAA;AAErB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,EACjD,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,YAAY,EASb;IACC,MAAM,KAAK,GAAG,KAAK,IAAwB,EAAE,CAC3C,kBAAkB,CAAC;QACjB,eAAe;QACf,WAAW;QACX,YAAY;QACZ,YAAY;QACZ,UAAU,EAAE,MAAM,iBAAiB,CAAC;YAClC,MAAM;YACN,KAAK;YACL,YAAY;YACZ,YAAY;SACb,CAAC;KACH,CAAC,CAAA;IAEJ,IAAI,KAAK,GAAG,MAAM,KAAK,EAAE,CAAA;IACzB,0EAA0E;IAC1E,yEAAyE;IACzE,+CAA+C;IAC/C,IAAI,SAAS,GAAyB,IAAI,CAAA;IAE1C,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAE1C,aAAa,EAAE,OAAO,CAAC,EAAE;YACvB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CACb,eAAe,YAAY,kCAAkC,CAC9D,CAAA;YACH,CAAC;YACD,OAAO,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;QACrC,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE;YACxB,IAAI,CAAC;gBACH,OAAO,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC1C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CAAC,GAAG,YAAY,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACnD,MAAM,GAAG,CAAA;gBACX,CAAC;gBACD,SAAS,KAAK,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;oBAClC,KAAK,GAAG,MAAM,CAAA;gBAChB,CAAC,CAAC,CAAA;gBACF,MAAM,SAAS,CAAA;gBACf,iEAAiE;gBACjE,8DAA8D;gBAC9D,+DAA+D;gBAC/D,iEAAiE;gBACjE,2BAA2B;gBAC3B,OAAO,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;YACpC,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"cipher.js","sourceRoot":"","sources":["../../src/descriptors/cipher.ts"],"names":[],"mappings":"AA0BA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,iBAAiB,EAGlB,MAAM,cAAc,CAAA;AAErB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,EACjD,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,YAAY,EASb;IACC,MAAM,KAAK,GAAG,KAAK,IAAwB,EAAE;QAC3C,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC;YACzC,MAAM;YACN,KAAK;YACL,YAAY;YACZ,YAAY;SACb,CAAC,CAAA;QACF,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,eAAe,YAAY,2CAA2C;gBACpE,kEAAkE;gBAClE,oEAAoE,CACvE,CAAA;QACH,CAAC;QACD,OAAO,kBAAkB,CAAC;YACxB,eAAe;YACf,WAAW;YACX,YAAY;YACZ,YAAY;YACZ,UAAU;SACX,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,IAAI,KAAK,GAAG,MAAM,KAAK,EAAE,CAAA;IACzB,0EAA0E;IAC1E,yEAAyE;IACzE,+CAA+C;IAC/C,IAAI,SAAS,GAAyB,IAAI,CAAA;IAE1C,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAE1C,aAAa,EAAE,OAAO,CAAC,EAAE;YACvB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CACb,eAAe,YAAY,kCAAkC,CAC9D,CAAA;YACH,CAAC;YACD,OAAO,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;QACrC,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE;YACxB,IAAI,CAAC;gBACH,OAAO,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC1C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CAAC,GAAG,YAAY,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACnD,MAAM,GAAG,CAAA;gBACX,CAAC;gBACD,SAAS,KAAK,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;oBAClC,KAAK,GAAG,MAAM,CAAA;gBAChB,CAAC,CAAC,CAAA;gBACF,MAAM,SAAS,CAAA;gBACf,iEAAiE;gBACjE,8DAA8D;gBAC9D,+DAA+D;gBAC/D,iEAAiE;gBACjE,2BAA2B;gBAC3B,OAAO,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;YACpC,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -2,24 +2,74 @@
2
2
  * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
3
  */
4
4
  /**
5
- * The keyring record codec: the `{ version, wrapped }` envelope stored as the
6
- * one resource of an account's unlock Space. Its plaintext carries the account
7
- * controller, the email captured at bind time, and the account pointer -- and
8
- * deliberately no key material of any kind, so the record locates an account
9
- * without authorizing anything against it.
5
+ * The keyring record codec: the `{ version, encryption, wrapped }` envelope
6
+ * stored as the one resource of an account's unlock Space. Its plaintext
7
+ * carries the account controller, the email captured at bind time, and the
8
+ * account pointer -- and deliberately no key material of any kind, so the
9
+ * record locates an account without authorizing anything against it.
10
10
  *
11
- * The wrap is an EDV document envelope under the unlock key-agreement key,
12
- * bound to the `keyring` cipher context, so a keyring envelope can never be
13
- * mistaken for (or swapped with) any other wrapped record.
11
+ * The wrap is an EDV document envelope bound to the `keyring` cipher context
12
+ * (so a keyring envelope can never be mistaken for, or swapped with, any other
13
+ * wrapped record), sealed under the record's own key epoch: every EDV envelope
14
+ * seals to an epoch key, so the record carries its one-epoch descriptor in its
15
+ * `encryption` member, epoch[0] wrapped to the unlock key-agreement key. The
16
+ * record stays self-contained -- unlock KAK in, contents out.
14
17
  */
15
18
  import type { IKeyAgreementKey, IKeyResolver } from '@interop/data-integrity-core';
19
+ import type { CollectionEncryption } from '@interop/was-client';
20
+ import { type DocCipher } from '@interop/was-client/edv';
16
21
  /**
17
- * The version stamped on the stored `{ version, wrapped }` keyring envelope.
18
- * Version 2 is the account-pointer record; version-1 records (which carried a
19
- * wrapped account-wide data seed) are refused as unusable -- such accounts are
20
- * re-provisioned, not migrated.
22
+ * The version stamped on the stored `{ version, encryption, wrapped }` keyring
23
+ * envelope: the record whose envelope seals under the record's own key epoch
24
+ * (the `encryption` member). Any other version is refused as unusable -- such
25
+ * accounts are re-provisioned, not migrated.
21
26
  */
22
- export declare const KEYRING_RECORD_VERSION = 2;
27
+ export declare const KEYRING_RECORD_VERSION = 1;
28
+ /**
29
+ * Mints the one-epoch descriptor a fresh record is sealed under: epoch[0]
30
+ * wrapped to the unlock KAK alone, built through `initRecipients` against a
31
+ * throwaway in-memory store (the descriptor's home is the record itself).
32
+ *
33
+ * @param options {object}
34
+ * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
35
+ * @returns {Promise<CollectionEncryption>}
36
+ */
37
+ export declare function mintRecordEncryption({ keyAgreementKey }: {
38
+ keyAgreementKey: IKeyAgreementKey;
39
+ }): Promise<CollectionEncryption>;
40
+ /**
41
+ * Builds the record cipher: the keyring-context EDV cipher over the record's
42
+ * own descriptor. Shared by the wrap and unwrap paths (and by the recovery
43
+ * record, which reuses the keyring cipher context verbatim).
44
+ *
45
+ * @param options {object}
46
+ * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
47
+ * @param options.keyResolver {IKeyResolver}
48
+ * @param options.encryption {CollectionEncryption} the record's descriptor
49
+ * @returns {Promise<DocCipher>}
50
+ */
51
+ export declare function recordCipher({ keyAgreementKey, keyResolver, encryption }: {
52
+ keyAgreementKey: IKeyAgreementKey;
53
+ keyResolver: IKeyResolver;
54
+ encryption: CollectionEncryption;
55
+ }): Promise<DocCipher>;
56
+ /**
57
+ * Validates the common `{ version, encryption, wrapped }` frame of a stored
58
+ * record (keyring or recovery -- `label` names the refusals) and returns its
59
+ * members.
60
+ *
61
+ * @param options {object}
62
+ * @param options.record {unknown}
63
+ * @param options.label {string} `'keyring'` or `'recovery'`
64
+ * @returns {{ encryption: CollectionEncryption, wrapped: unknown }}
65
+ */
66
+ export declare function parseRecordFrame({ record, label }: {
67
+ record: unknown;
68
+ label: string;
69
+ }): {
70
+ encryption: CollectionEncryption;
71
+ wrapped: unknown;
72
+ };
23
73
  /**
24
74
  * The account pointer a keyring record carries in place of the retired data
25
75
  * seed: where the account lives (`spaceId` + `host`, the WAS server origin)
@@ -45,8 +95,9 @@ export interface KeyringRecordContents {
45
95
  }
46
96
  /**
47
97
  * Wraps the account-pointer contents into a keyring record: the controller,
48
- * email, and pointer (+ timestamp) encrypted under the unlock KAK via the EDV
49
- * cipher. Deliberately carries no key material of any kind.
98
+ * email, and pointer (+ timestamp) sealed under a freshly minted record epoch
99
+ * whose key is wrapped to the unlock KAK. Deliberately carries no key material
100
+ * of any kind.
50
101
  *
51
102
  * @param options {object}
52
103
  * @param options.controller {string} the account did:key
@@ -55,7 +106,7 @@ export interface KeyringRecordContents {
55
106
  * no-WAS deployments)
56
107
  * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
57
108
  * @param options.keyResolver {IKeyResolver}
58
- * @returns {Promise<{ version: number, wrapped: unknown }>}
109
+ * @returns {Promise<{ version: number, encryption: unknown, wrapped: unknown }>}
59
110
  */
60
111
  export declare function wrapKeyringRecord({ controller, email, pointer, keyAgreementKey, keyResolver }: {
61
112
  controller: string;
@@ -65,12 +116,12 @@ export declare function wrapKeyringRecord({ controller, email, pointer, keyAgree
65
116
  keyResolver: IKeyResolver;
66
117
  }): Promise<{
67
118
  version: number;
119
+ encryption: unknown;
68
120
  wrapped: unknown;
69
121
  }>;
70
122
  /**
71
123
  * Unwraps and validates a keyring record. Rejects a record whose `version` is
72
- * not the current one (version-1 records carried the retired wrapped data
73
- * seed and are refused -- accounts are re-provisioned, not migrated), and
124
+ * not the current one (accounts are re-provisioned, not migrated), and
74
125
  * sanity-checks the decrypted plaintext (non-empty controller, well-formed
75
126
  * pointer when present).
76
127
  *
@@ -1 +1 @@
1
- {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AAIrC;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,UAAU,EACV,KAAK,EACL,OAAO,EACP,eAAe,EACf,WAAW,EACZ,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,cAAc,CAAA;IACxB,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;CAC1B,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAuBjD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,MAAM,EACN,eAAe,EACf,WAAW,EACZ,EAAE;IACD,MAAM,EAAE,OAAO,CAAA;IACf,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;CAC1B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAsCjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAsB7E"}
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAIL,KAAK,SAAS,EAEf,MAAM,yBAAyB,CAAA;AAGhC;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,eAAe,EAChB,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;CAClC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiBhC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,EACjC,eAAe,EACf,WAAW,EACX,UAAU,EACX,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;IACzB,UAAU,EAAE,oBAAoB,CAAA;CACjC,GAAG,OAAO,CAAC,SAAS,CAAC,CAOrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,KAAK,EACN,EAAE;IACD,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd,GAAG;IAAE,UAAU,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAgBzD;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,UAAU,EACV,KAAK,EACL,OAAO,EACP,eAAe,EACf,WAAW,EACZ,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,cAAc,CAAA;IACxB,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;CAC1B,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAwBtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,MAAM,EACN,eAAe,EACf,WAAW,EACZ,EAAE;IACD,MAAM,EAAE,OAAO,CAAA;IACf,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;CAC1B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAgCjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAsB7E"}
@@ -1,16 +1,86 @@
1
- import { createEdvDocCipher } from '@interop/was-client/edv';
1
+ import { createEdvDocCipher, initRecipients, ownerRecipient } from '@interop/was-client/edv';
2
2
  import { KEYRING_COLLECTION } from '../space/collections.js';
3
3
  /**
4
- * The version stamped on the stored `{ version, wrapped }` keyring envelope.
5
- * Version 2 is the account-pointer record; version-1 records (which carried a
6
- * wrapped account-wide data seed) are refused as unusable -- such accounts are
7
- * re-provisioned, not migrated.
4
+ * The version stamped on the stored `{ version, encryption, wrapped }` keyring
5
+ * envelope: the record whose envelope seals under the record's own key epoch
6
+ * (the `encryption` member). Any other version is refused as unusable -- such
7
+ * accounts are re-provisioned, not migrated.
8
8
  */
9
- export const KEYRING_RECORD_VERSION = 2;
9
+ export const KEYRING_RECORD_VERSION = 1;
10
+ /**
11
+ * Mints the one-epoch descriptor a fresh record is sealed under: epoch[0]
12
+ * wrapped to the unlock KAK alone, built through `initRecipients` against a
13
+ * throwaway in-memory store (the descriptor's home is the record itself).
14
+ *
15
+ * @param options {object}
16
+ * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
17
+ * @returns {Promise<CollectionEncryption>}
18
+ */
19
+ export async function mintRecordEncryption({ keyAgreementKey }) {
20
+ let stored = null;
21
+ const store = {
22
+ async read() {
23
+ return stored ? { descriptor: stored } : null;
24
+ },
25
+ async replace(next) {
26
+ stored = next;
27
+ },
28
+ async create(next) {
29
+ stored = next;
30
+ }
31
+ };
32
+ return initRecipients({
33
+ store,
34
+ recipients: [ownerRecipient({ keyAgreementKey })]
35
+ });
36
+ }
37
+ /**
38
+ * Builds the record cipher: the keyring-context EDV cipher over the record's
39
+ * own descriptor. Shared by the wrap and unwrap paths (and by the recovery
40
+ * record, which reuses the keyring cipher context verbatim).
41
+ *
42
+ * @param options {object}
43
+ * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
44
+ * @param options.keyResolver {IKeyResolver}
45
+ * @param options.encryption {CollectionEncryption} the record's descriptor
46
+ * @returns {Promise<DocCipher>}
47
+ */
48
+ export async function recordCipher({ keyAgreementKey, keyResolver, encryption }) {
49
+ return createEdvDocCipher({
50
+ keyAgreementKey,
51
+ keyResolver,
52
+ collectionId: KEYRING_COLLECTION.id,
53
+ encryption
54
+ });
55
+ }
56
+ /**
57
+ * Validates the common `{ version, encryption, wrapped }` frame of a stored
58
+ * record (keyring or recovery -- `label` names the refusals) and returns its
59
+ * members.
60
+ *
61
+ * @param options {object}
62
+ * @param options.record {unknown}
63
+ * @param options.label {string} `'keyring'` or `'recovery'`
64
+ * @returns {{ encryption: CollectionEncryption, wrapped: unknown }}
65
+ */
66
+ export function parseRecordFrame({ record, label }) {
67
+ if (record === null || typeof record !== 'object') {
68
+ throw new Error(`Malformed ${label} record.`);
69
+ }
70
+ const { version, encryption, wrapped } = record;
71
+ if (version !== KEYRING_RECORD_VERSION) {
72
+ throw new Error(`Unsupported ${label} record version "${String(version)}".`);
73
+ }
74
+ if (encryption === null || typeof encryption !== 'object') {
75
+ throw new Error(`The ${label} record is missing its encryption descriptor.`);
76
+ }
77
+ return { encryption: encryption, wrapped };
78
+ }
10
79
  /**
11
80
  * Wraps the account-pointer contents into a keyring record: the controller,
12
- * email, and pointer (+ timestamp) encrypted under the unlock KAK via the EDV
13
- * cipher. Deliberately carries no key material of any kind.
81
+ * email, and pointer (+ timestamp) sealed under a freshly minted record epoch
82
+ * whose key is wrapped to the unlock KAK. Deliberately carries no key material
83
+ * of any kind.
14
84
  *
15
85
  * @param options {object}
16
86
  * @param options.controller {string} the account did:key
@@ -19,13 +89,14 @@ export const KEYRING_RECORD_VERSION = 2;
19
89
  * no-WAS deployments)
20
90
  * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
21
91
  * @param options.keyResolver {IKeyResolver}
22
- * @returns {Promise<{ version: number, wrapped: unknown }>}
92
+ * @returns {Promise<{ version: number, encryption: unknown, wrapped: unknown }>}
23
93
  */
24
94
  export async function wrapKeyringRecord({ controller, email, pointer, keyAgreementKey, keyResolver }) {
25
- const cipher = await createEdvDocCipher({
95
+ const encryption = await mintRecordEncryption({ keyAgreementKey });
96
+ const cipher = await recordCipher({
26
97
  keyAgreementKey,
27
98
  keyResolver,
28
- collectionId: KEYRING_COLLECTION.id
99
+ encryption
29
100
  });
30
101
  const { envelope } = await cipher.encrypt({
31
102
  data: {
@@ -43,12 +114,11 @@ export async function wrapKeyringRecord({ controller, email, pointer, keyAgreeme
43
114
  createdAt: new Date().toISOString()
44
115
  }
45
116
  });
46
- return { version: KEYRING_RECORD_VERSION, wrapped: envelope };
117
+ return { version: KEYRING_RECORD_VERSION, encryption, wrapped: envelope };
47
118
  }
48
119
  /**
49
120
  * Unwraps and validates a keyring record. Rejects a record whose `version` is
50
- * not the current one (version-1 records carried the retired wrapped data
51
- * seed and are refused -- accounts are re-provisioned, not migrated), and
121
+ * not the current one (accounts are re-provisioned, not migrated), and
52
122
  * sanity-checks the decrypted plaintext (non-empty controller, well-formed
53
123
  * pointer when present).
54
124
  *
@@ -59,17 +129,14 @@ export async function wrapKeyringRecord({ controller, email, pointer, keyAgreeme
59
129
  * @returns {Promise<KeyringRecordContents>}
60
130
  */
61
131
  export async function unwrapKeyringRecord({ record, keyAgreementKey, keyResolver }) {
62
- if (record === null || typeof record !== 'object') {
63
- throw new Error('Malformed keyring record.');
64
- }
65
- const { version, wrapped } = record;
66
- if (version !== KEYRING_RECORD_VERSION) {
67
- throw new Error(`Unsupported keyring record version "${String(version)}".`);
68
- }
69
- const cipher = await createEdvDocCipher({
132
+ const { encryption, wrapped } = parseRecordFrame({
133
+ record,
134
+ label: 'keyring'
135
+ });
136
+ const cipher = await recordCipher({
70
137
  keyAgreementKey,
71
138
  keyResolver,
72
- collectionId: KEYRING_COLLECTION.id
139
+ encryption
73
140
  });
74
141
  const plaintext = (await cipher.decrypt({
75
142
  envelope: wrapped
@@ -1 +1 @@
1
- {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"AAsBA,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,cAAc,EAGf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,EACzC,eAAe,EAGhB;IACC,IAAI,MAAM,GAAgC,IAAI,CAAA;IAC9C,MAAM,KAAK,GAA8B;QACvC,KAAK,CAAC,IAAI;YACR,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;QAC/C,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,IAAI;YAChB,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,IAAI;YACf,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;KACF,CAAA;IACD,OAAO,cAAc,CAAC;QACpB,KAAK;QACL,UAAU,EAAE,CAAC,cAAc,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC;KAClD,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EACjC,eAAe,EACf,WAAW,EACX,UAAU,EAKX;IACC,OAAO,kBAAkB,CAAC;QACxB,eAAe;QACf,WAAW;QACX,YAAY,EAAE,kBAAkB,CAAC,EAAE;QACnC,UAAU;KACX,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,MAAM,EACN,KAAK,EAIN;IACC,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,UAAU,CAAC,CAAA;IAC/C,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,MAIxC,CAAA;IACD,IAAI,OAAO,KAAK,sBAAsB,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,eAAe,KAAK,oBAAoB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9E,CAAC;IACD,IAAI,UAAU,KAAK,IAAI,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,OAAO,KAAK,+CAA+C,CAAC,CAAA;IAC9E,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,UAAkC,EAAE,OAAO,EAAE,CAAA;AACpE,CAAC;AA2BD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EACtC,UAAU,EACV,KAAK,EACL,OAAO,EACP,eAAe,EACf,WAAW,EAOZ;IACC,MAAM,UAAU,GAAG,MAAM,oBAAoB,CAAC,EAAE,eAAe,EAAE,CAAC,CAAA;IAClE,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,eAAe;QACf,WAAW;QACX,UAAU;KACX,CAAC,CAAA;IACF,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACxC,IAAI,EAAE;YACJ,UAAU;YACV,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,OAAO;gBACT,CAAC,CAAC;oBACE,OAAO,EAAE;wBACP,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;wBAC5C,OAAO,EAAE,OAAO,CAAC,OAAO;wBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;qBACnB;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC;KACF,CAAC,CAAA;IACF,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;AAC3E,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,EACxC,MAAM,EACN,eAAe,EACf,WAAW,EAKZ;IACC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC;QAC/C,MAAM;QACN,KAAK,EAAE,SAAS;KACjB,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,eAAe;QACf,WAAW;QACX,UAAU;KACX,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC;QACtC,QAAQ,EAAE,OAAgB;KAC3B,CAAC,CAID,CAAA;IAED,IAAI,OAAO,SAAS,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAA;IAC5D,CAAC;IACD,MAAM,OAAO,GAAG,kBAAkB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IAErD,OAAO;QACL,UAAU,EAAE,SAAS,CAAC,UAAU;QAChC,gEAAgE;QAChE,oCAAoC;QACpC,GAAG,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK;YACxD,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE;YAC5B,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChC,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;IACpE,CAAC;IACD,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,KAI9B,CAAA;IACD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IAC3E,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IACxE,CAAC;IACD,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IACxE,CAAC;IACD,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AACnD,CAAC"}
@@ -36,6 +36,9 @@
36
36
  * re-epoch that brings an encrypted collection onto the roster's current
37
37
  * user key, and the parallel best-effort fan-out over the collections the wallet
38
38
  * names (also the completion sweep's driver).
39
+ * - `ensureWalletSpaceEpochs` -- the provision-time epoch[0] install for the
40
+ * wallet Space's encrypted collections, the EDV-bearing second step of
41
+ * `provisionWalletSpace`.
39
42
  */
40
43
  export { mintUserKey, userKeyVaultKeys } from './userKey.js';
41
44
  export type { UserKey } from './userKey.js';
@@ -46,6 +49,7 @@ export { cascadeCollectionsToUserKey, userKeyAsRecipient, rotateCollectionEpochs
46
49
  export type { CollectionUserKeyRotationOutcome, UserKeyCascadeResult } from './userKeyCascade.js';
47
50
  export type { UserKeyRosterReadResult, RosterRecipientDocument } from './userKeyRoster.js';
48
51
  export { userKeyRosterDescriptorStore } from './rosterStore.js';
52
+ export { ensureWalletSpaceEpochs } from './spaceEpochs.js';
49
53
  export { readClientLabels, removeClientLabel, setClientLabel } from './clientLabels.js';
50
54
  export type { ClientLabelsRecord, ClientLabelsStore } from './clientLabels.js';
51
55
  export { wasClientLabelsStore } from './wasLabelsStore.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC5D,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,yBAAyB,EACzB,+BAA+B,EAC/B,mBAAmB,EACnB,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,8BAA8B,EAC9B,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,4BAA4B,EAC7B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,+BAA+B,EAC/B,wBAAwB,EACzB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EACV,gCAAgC,EAChC,oBAAoB,EACrB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EACV,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAA;AAE/D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACf,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC5D,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,yBAAyB,EACzB,+BAA+B,EAC/B,mBAAmB,EACnB,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,8BAA8B,EAC9B,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,4BAA4B,EAC7B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,+BAA+B,EAC/B,wBAAwB,EACzB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EACV,gCAAgC,EAChC,oBAAoB,EACrB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EACV,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAA;AAE/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACf,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
@@ -36,12 +36,16 @@
36
36
  * re-epoch that brings an encrypted collection onto the roster's current
37
37
  * user key, and the parallel best-effort fan-out over the collections the wallet
38
38
  * names (also the completion sweep's driver).
39
+ * - `ensureWalletSpaceEpochs` -- the provision-time epoch[0] install for the
40
+ * wallet Space's encrypted collections, the EDV-bearing second step of
41
+ * `provisionWalletSpace`.
39
42
  */
40
43
  export { mintUserKey, userKeyVaultKeys } from './userKey.js';
41
44
  export { assertEnrolledClientKeyRecord, decodeClientKeyRecord, encodeClientKeyRecord, parseClientRecordUserKey, parseClientRecordWebvhKeys } from './clientKeyRecord.js';
42
45
  export { addUserKeyRosterRecipient, convergeUserKeyRosterToDocument, ensureUserKeyRoster, UserKeyRosterContinuityError, UserKeyRosterIntegrityError, UserKeyRosterUnwrapError, userKeyRosterEpochsSigner, userKeyRosterRecipientResolver, readUserKeyRoster, rosterRecipientKid, rotateUserKeyRoster, verifyUserKeyRosterEpochsSig } from './userKeyRoster.js';
43
46
  export { cascadeCollectionsToUserKey, userKeyAsRecipient, rotateCollectionEpochsToUserKey, unwrapUserKeyGenerations } from './userKeyCascade.js';
44
47
  export { userKeyRosterDescriptorStore } from './rosterStore.js';
48
+ export { ensureWalletSpaceEpochs } from './spaceEpochs.js';
45
49
  export { readClientLabels, removeClientLabel, setClientLabel } from './clientLabels.js';
46
50
  export { wasClientLabelsStore } from './wasLabelsStore.js';
47
51
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAG5D,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,sBAAsB,CAAA;AAO7B,OAAO,EACL,yBAAyB,EACzB,+BAA+B,EAC/B,mBAAmB,EACnB,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,8BAA8B,EAC9B,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,4BAA4B,EAC7B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,+BAA+B,EAC/B,wBAAwB,EACzB,MAAM,qBAAqB,CAAA;AAU5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAA;AAE/D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACf,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAG5D,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,sBAAsB,CAAA;AAO7B,OAAO,EACL,yBAAyB,EACzB,+BAA+B,EAC/B,mBAAmB,EACnB,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,8BAA8B,EAC9B,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,4BAA4B,EAC7B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,+BAA+B,EAC/B,wBAAwB,EACzB,MAAM,qBAAqB,CAAA;AAU5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAA;AAE/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACf,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
@@ -0,0 +1,56 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * Provision-time key-epoch install for the wallet Space's encrypted
6
+ * collections: every encrypted collection's descriptor carries an epoch roster
7
+ * from birth (epoch[0] a fresh random epoch key, never a user-key generation),
8
+ * and reads/writes are refused fail-closed until it does. `provisionWalletSpace`
9
+ * (the crypto-free container ensure in `space`) declares the collections; this
10
+ * EDV-bearing second step installs each one's epoch[0], wrapped to the user key
11
+ * (recipient zero). It lives in `keys` rather than `space` so the root barrel,
12
+ * which re-exports `space`, stays free of the EDV crypto graph -- the same
13
+ * split was-client makes between `ensureSpaceAndCollection` and
14
+ * `ensureFirstEpoch`.
15
+ */
16
+ import type { WasClient } from '@interop/was-client';
17
+ import type { UserKey } from './userKey.js';
18
+ /**
19
+ * Installs key epoch[0] on every encrypted collection of the wallet Space
20
+ * roster (or on the given `collectionIds`), concurrently, wrapped to the user
21
+ * key. Each install is `ensureFirstEpoch`: create-if-absent through the
22
+ * descriptor-store seam, adopting (never overwriting) a roster another
23
+ * provisioner already landed -- so re-running after a tear converges, and
24
+ * exactly one epoch[0] ever exists per collection. Run it after
25
+ * `provisionWalletSpace` has declared the collections; the wallet Space's
26
+ * provisioning is complete only once both steps have.
27
+ *
28
+ * Run both steps from the sync engine's `ensureProvisioned` seam (or, for a
29
+ * driver of its own, equally before the collection's first content push): the
30
+ * descriptor-before-first-content-push invariant rests on it. A caller that
31
+ * minted envelopes eagerly against a locally-minted roster and finds
32
+ * `installed: false` here (another provisioner's create won) has adopted the
33
+ * winner's descriptor and must re-mint its pending envelopes under that
34
+ * descriptor's current epoch before pushing -- `remintPendingEnvelopes`
35
+ * (`@interop/wallet-core/sync`) is that path.
36
+ *
37
+ * @param options {object}
38
+ * @param options.was {WasClient}
39
+ * @param options.spaceId {string}
40
+ * @param options.userKey {UserKey} the account's user key, epoch[0]'s one
41
+ * initial recipient
42
+ * @param [options.collectionIds] {string[]} the encrypted collections to
43
+ * cover; defaults to the wallet Space roster's encrypted collections. A
44
+ * caller naming its own ids (e.g. `contacts`) must name only collections
45
+ * declared encrypted
46
+ * @returns {Promise<Record<string, boolean>>} per collection id, whether
47
+ * this call installed its epoch[0] (`false` means an existing roster was
48
+ * adopted)
49
+ */
50
+ export declare function ensureWalletSpaceEpochs({ was, spaceId, userKey, collectionIds }: {
51
+ was: WasClient;
52
+ spaceId: string;
53
+ userKey: UserKey;
54
+ collectionIds?: string[];
55
+ }): Promise<Record<string, boolean>>;
56
+ //# sourceMappingURL=spaceEpochs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spaceEpochs.d.ts","sourceRoot":"","sources":["../../src/keys/spaceEpochs.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAKpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,uBAAuB,CAAC,EAC5C,GAAG,EACH,OAAO,EACP,OAAO,EACP,aAAa,EACd,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CACzB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAyBnC"}
@@ -0,0 +1,55 @@
1
+ import { ensureFirstEpoch } from '@interop/was-client/edv';
2
+ import { WALLET_SPACE_PROVISION_ROSTER } from '../space/collections.js';
3
+ import { userKeyAsRecipient } from './userKeyCascade.js';
4
+ /**
5
+ * Installs key epoch[0] on every encrypted collection of the wallet Space
6
+ * roster (or on the given `collectionIds`), concurrently, wrapped to the user
7
+ * key. Each install is `ensureFirstEpoch`: create-if-absent through the
8
+ * descriptor-store seam, adopting (never overwriting) a roster another
9
+ * provisioner already landed -- so re-running after a tear converges, and
10
+ * exactly one epoch[0] ever exists per collection. Run it after
11
+ * `provisionWalletSpace` has declared the collections; the wallet Space's
12
+ * provisioning is complete only once both steps have.
13
+ *
14
+ * Run both steps from the sync engine's `ensureProvisioned` seam (or, for a
15
+ * driver of its own, equally before the collection's first content push): the
16
+ * descriptor-before-first-content-push invariant rests on it. A caller that
17
+ * minted envelopes eagerly against a locally-minted roster and finds
18
+ * `installed: false` here (another provisioner's create won) has adopted the
19
+ * winner's descriptor and must re-mint its pending envelopes under that
20
+ * descriptor's current epoch before pushing -- `remintPendingEnvelopes`
21
+ * (`@interop/wallet-core/sync`) is that path.
22
+ *
23
+ * @param options {object}
24
+ * @param options.was {WasClient}
25
+ * @param options.spaceId {string}
26
+ * @param options.userKey {UserKey} the account's user key, epoch[0]'s one
27
+ * initial recipient
28
+ * @param [options.collectionIds] {string[]} the encrypted collections to
29
+ * cover; defaults to the wallet Space roster's encrypted collections. A
30
+ * caller naming its own ids (e.g. `contacts`) must name only collections
31
+ * declared encrypted
32
+ * @returns {Promise<Record<string, boolean>>} per collection id, whether
33
+ * this call installed its epoch[0] (`false` means an existing roster was
34
+ * adopted)
35
+ */
36
+ export async function ensureWalletSpaceEpochs({ was, spaceId, userKey, collectionIds }) {
37
+ const ids = collectionIds ??
38
+ WALLET_SPACE_PROVISION_ROSTER.filter(spec => spec.encryption === 'edv').map(spec => spec.collectionId);
39
+ const installed = {};
40
+ await Promise.all(ids.map(async (collectionId) => {
41
+ try {
42
+ const result = await ensureFirstEpoch({
43
+ collection: was.space(spaceId).collection(collectionId),
44
+ recipients: [userKeyAsRecipient({ userKey })]
45
+ });
46
+ installed[collectionId] = result.installed;
47
+ }
48
+ catch (err) {
49
+ throw new Error(`Error installing the first key epoch for collection ` +
50
+ `"${collectionId}" in space "${spaceId}".`, { cause: err });
51
+ }
52
+ }));
53
+ return installed;
54
+ }
55
+ //# sourceMappingURL=spaceEpochs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spaceEpochs.js","sourceRoot":"","sources":["../../src/keys/spaceEpochs.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1D,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAGxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,EAC5C,GAAG,EACH,OAAO,EACP,OAAO,EACP,aAAa,EAMd;IACC,MAAM,GAAG,GACP,aAAa;QACb,6BAA6B,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,GAAG,CACzE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAC1B,CAAA;IACH,MAAM,SAAS,GAA4B,EAAE,CAAA;IAC7C,MAAM,OAAO,CAAC,GAAG,CACf,GAAG,CAAC,GAAG,CAAC,KAAK,EAAC,YAAY,EAAC,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;gBACpC,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC;gBACvD,UAAU,EAAE,CAAC,kBAAkB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;aAC9C,CAAC,CAAA;YACF,SAAS,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,SAAS,CAAA;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,sDAAsD;gBACpD,IAAI,YAAY,eAAe,OAAO,IAAI,EAC5C,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAA;QACH,CAAC;IACH,CAAC,CAAC,CACH,CAAA;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
@@ -19,12 +19,13 @@
19
19
  * unwrapUserKeyGenerations}) -- needed both to recognize a stale epoch and to
20
20
  * escrow the fresh user key into a stranded collection's history.
21
21
  *
22
- * One residue, accepted: a collection with NO epochs gets the newest prior
23
- * generation installed as its first epoch (pre-epoch envelopes are sealed to
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.
22
+ * The cascade is rotation-only: every encrypted collection's descriptor
23
+ * carries an epoch roster from provisioning (`ensureWalletSpaceEpochs`), so a
24
+ * descriptor met without epochs can only mean a tampering or pre-provisioning
25
+ * host and is refused fail-closed. No construction anywhere installs a
26
+ * user-key secret as a collection epoch secret, which is what keeps a
27
+ * collection-epoch escrow (an App Connect grant, a share) from ever handing a
28
+ * grantee the user key itself.
28
29
  */
29
30
  import type { IKeyAgreementKey } from '@interop/data-integrity-core';
30
31
  import type { CollectionEncryption } from '@interop/was-client';
@@ -64,21 +65,15 @@ export declare function unwrapUserKeyGenerations({ descriptor, clientKeyAgreemen
64
65
  }): Promise<UserKey[]>;
65
66
  /**
66
67
  * 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).
68
+ * key), `escrowed` (the current user key's wrap was completed into history
69
+ * with no stale epoch to rotate), or `rotated` (a fresh epoch sealed to the
70
+ * current user key, the stale generations retired).
72
71
  */
73
- export type CollectionUserKeyRotationOutcome = 'noop' | 'installed' | 'escrowed' | 'rotated';
72
+ export type CollectionUserKeyRotationOutcome = 'noop' | 'escrowed' | 'rotated';
74
73
  /**
75
74
  * Brings ONE encrypted collection's epoch roster onto the current user key --
76
75
  * the per-collection op of the revocation cascade and of the completion sweep:
77
76
  *
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 user key,
80
- * then fall through to the rotation. A first-generation account (nothing
81
- * prior) installs the current user key alone and is done.
82
77
  * - **Stale current epoch** (names a non-current generation): one
83
78
  * `replaceRecipient` write -- the current user key escrowed into every epoch,
84
79
  * a fresh epoch minted without the stale generations. Two requests per
@@ -86,6 +81,9 @@ export type CollectionUserKeyRotationOutcome = 'noop' | 'installed' | 'escrowed'
86
81
  * default did:key resolver re-wraps them).
87
82
  * - **Current already** and fully escrowed: no write at all, so a naive
88
83
  * re-run after a mid-cascade crash converges with zero redundant epochs.
84
+ * - **No epochs**: refused fail-closed (see the module doc) -- provisioning
85
+ * installs every encrypted collection's epoch[0], so the cascade never
86
+ * mints a first epoch.
89
87
  *
90
88
  * The pull axis is deliberately a no-op here: a user key rotation follows a
91
89
  * document edit (client revocation, code retirement) that already killed the