@interop/wallet-core 0.21.0 → 0.22.1

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 (76) hide show
  1. package/README.md +5 -2
  2. package/dist/descriptors/acquire.d.ts +23 -15
  3. package/dist/descriptors/acquire.d.ts.map +1 -1
  4. package/dist/descriptors/acquire.js +11 -8
  5. package/dist/descriptors/acquire.js.map +1 -1
  6. package/dist/descriptors/cipher.d.ts +9 -3
  7. package/dist/descriptors/cipher.d.ts.map +1 -1
  8. package/dist/descriptors/cipher.js +43 -12
  9. package/dist/descriptors/cipher.js.map +1 -1
  10. package/dist/descriptors/index.d.ts +6 -2
  11. package/dist/descriptors/index.d.ts.map +1 -1
  12. package/dist/descriptors/index.js +6 -2
  13. package/dist/descriptors/index.js.map +1 -1
  14. package/dist/keyring/index.d.ts +8 -3
  15. package/dist/keyring/index.d.ts.map +1 -1
  16. package/dist/keyring/index.js +8 -3
  17. package/dist/keyring/index.js.map +1 -1
  18. package/dist/keyring/record.d.ts +89 -18
  19. package/dist/keyring/record.d.ts.map +1 -1
  20. package/dist/keyring/record.js +119 -23
  21. package/dist/keyring/record.js.map +1 -1
  22. package/dist/keys/index.d.ts +5 -0
  23. package/dist/keys/index.d.ts.map +1 -1
  24. package/dist/keys/index.js +4 -0
  25. package/dist/keys/index.js.map +1 -1
  26. package/dist/keys/spaceEpochs.d.ts +95 -0
  27. package/dist/keys/spaceEpochs.d.ts.map +1 -0
  28. package/dist/keys/spaceEpochs.js +71 -0
  29. package/dist/keys/spaceEpochs.js.map +1 -0
  30. package/dist/keys/userKeyCascade.d.ts +14 -16
  31. package/dist/keys/userKeyCascade.d.ts.map +1 -1
  32. package/dist/keys/userKeyCascade.js +9 -43
  33. package/dist/keys/userKeyCascade.js.map +1 -1
  34. package/dist/recovery/recoveryRecord.d.ts +18 -13
  35. package/dist/recovery/recoveryRecord.d.ts.map +1 -1
  36. package/dist/recovery/recoveryRecord.js +15 -17
  37. package/dist/recovery/recoveryRecord.js.map +1 -1
  38. package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
  39. package/dist/recovery/recoveryWebvh.js +4 -0
  40. package/dist/recovery/recoveryWebvh.js.map +1 -1
  41. package/dist/space/collections.d.ts +2 -2
  42. package/dist/space/collections.js +2 -2
  43. package/dist/space/index.d.ts +5 -2
  44. package/dist/space/index.d.ts.map +1 -1
  45. package/dist/space/index.js +5 -2
  46. package/dist/space/index.js.map +1 -1
  47. package/dist/space/provisioning.d.ts +25 -14
  48. package/dist/space/provisioning.d.ts.map +1 -1
  49. package/dist/space/provisioning.js +22 -33
  50. package/dist/space/provisioning.js.map +1 -1
  51. package/dist/sync/engine.d.ts +28 -1
  52. package/dist/sync/engine.d.ts.map +1 -1
  53. package/dist/sync/engine.js +5 -0
  54. package/dist/sync/engine.js.map +1 -1
  55. package/dist/sync/index.d.ts +8 -3
  56. package/dist/sync/index.d.ts.map +1 -1
  57. package/dist/sync/index.js +8 -3
  58. package/dist/sync/index.js.map +1 -1
  59. package/dist/sync/push.d.ts.map +1 -1
  60. package/dist/sync/push.js +3 -2
  61. package/dist/sync/push.js.map +1 -1
  62. package/dist/sync/remint.d.ts +94 -0
  63. package/dist/sync/remint.d.ts.map +1 -0
  64. package/dist/sync/remint.js +111 -0
  65. package/dist/sync/remint.js.map +1 -0
  66. package/dist/sync/types.d.ts +38 -4
  67. package/dist/sync/types.d.ts.map +1 -1
  68. package/dist/sync/types.js +4 -4
  69. package/dist/sync/types.js.map +1 -1
  70. package/dist/webvh/didWebvh.d.ts.map +1 -1
  71. package/dist/webvh/didWebvh.js +3 -0
  72. package/dist/webvh/didWebvh.js.map +1 -1
  73. package/dist/webvh/revokeClient.d.ts.map +1 -1
  74. package/dist/webvh/revokeClient.js +1 -0
  75. package/dist/webvh/revokeClient.js.map +1 -1
  76. 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
 
@@ -4,11 +4,16 @@
4
4
  /**
5
5
  * Collection encryption-descriptor acquisition: reading a collection's
6
6
  * `CollectionEncryption` descriptor (its key-epoch roster) from the Collection
7
- * Description, caching each success, and falling back to the cached copy when
8
- * the description cannot be fetched -- offline, a previously-shared collection
9
- * must keep encrypting under its current epoch. A successful fetch that
10
- * returns no descriptor (an unshared collection) yields `undefined`: the
11
- * single-key path.
7
+ * Description, caching each success, and falling back to the cached copy
8
+ * whenever the description yields no descriptor -- whether it could not be
9
+ * fetched at all (offline, a collection must keep encrypting under its current
10
+ * epoch) or came back empty (WAS masks an unauthorized read as an absent one,
11
+ * so an empty description is ambiguous, never an authoritative "no
12
+ * encryption"). `undefined` therefore means only this: nothing, fetched or
13
+ * cached, describes this collection's encryption -- a plaintext collection, or
14
+ * an encrypted one whose epoch[0] install has not landed. A caller that has
15
+ * declared the collection encrypted must refuse fail-closed rather than
16
+ * encrypt without a roster.
12
17
  *
13
18
  * The two seams are deliberately narrow, so a wallet app's own classes satisfy
14
19
  * them structurally -- no adapter needed. An {@link EncryptionDescriptorSource}
@@ -20,9 +25,11 @@
20
25
  import type { CollectionEncryption, WasClient } from '@interop/was-client';
21
26
  /**
22
27
  * Where descriptors come from: one signed read of the collection's
23
- * Description. Resolves `undefined` for a collection that is plaintext or has
24
- * no descriptor; network errors throw through (callers treat the fetch as
25
- * best-effort and fall back to a cached copy).
28
+ * Description. Resolves `undefined` when the description carries no encryption
29
+ * member -- which a WAS host also serves for a read this client is not
30
+ * authorized to make, so the absence is ambiguous and callers fall back to a
31
+ * cached copy just as they do for a thrown fetch. Network errors throw through
32
+ * (callers treat the fetch as best-effort).
26
33
  */
27
34
  export interface EncryptionDescriptorSource {
28
35
  collectionEncryption(options: {
@@ -59,11 +66,12 @@ export declare function wasDescriptorSource({ was, spaceId }: {
59
66
  }): EncryptionDescriptorSource;
60
67
  /**
61
68
  * Acquires one collection's descriptor: fetches it from the source, caching a
62
- * success; falls back to the cached copy when the fetch fails; and with no
63
- * source at all (a purely local code path) reads the cache alone. A successful
64
- * fetch that returns no descriptor resolves `undefined` -- an unshared
65
- * collection stays on the single-key path -- and deliberately leaves any
66
- * cached copy in place, mirroring the fetch-failure fallback.
69
+ * success; falls back to the cached copy whenever the fetch yields no
70
+ * descriptor (it threw, or it came back empty -- a masked 404 for an
71
+ * unauthorized read looks exactly like an unencrypted collection); and with no
72
+ * source at all (a purely local code path) reads the cache alone. Any cached
73
+ * copy is deliberately left in place, never cleared by an empty fetch.
74
+ * `undefined` means no descriptor exists anywhere for this collection.
67
75
  *
68
76
  * @param options {object}
69
77
  * @param [options.source] {EncryptionDescriptorSource} omit for cache-only
@@ -71,8 +79,8 @@ export declare function wasDescriptorSource({ was, spaceId }: {
71
79
  * @param options.cache {EncryptionDescriptorCache}
72
80
  * @param options.collectionId {string}
73
81
  * @param [options.onFetchError] {function} observes a swallowed fetch
74
- * failure (the cached-fallback branch); errors from the cache itself throw
75
- * through
82
+ * failure (the thrown-fetch branch only; an empty description is not an
83
+ * error). Errors from the cache itself throw through
76
84
  * @returns {Promise<CollectionEncryption | undefined>}
77
85
  */
78
86
  export declare function acquireDescriptor({ source, cache, collectionId, onFetchError }: {
@@ -1 +1 @@
1
- {"version":3,"file":"acquire.d.ts","sourceRoot":"","sources":["../../src/descriptors/acquire.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,CAAC,OAAO,EAAE;QAC5B,YAAY,EAAE,MAAM,CAAA;KACrB,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAA;CAC9C;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,cAAc,CAAC,OAAO,EAAE;QACtB,YAAY,EAAE,MAAM,CAAA;KACrB,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAA;IAC7C,eAAe,CAAC,OAAO,EAAE;QACvB,YAAY,EAAE,MAAM,CAAA;QACpB,UAAU,EAAE,oBAAoB,CAAA;KACjC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,GAAG,EACH,OAAO,EACR,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,0BAA0B,CAU7B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,MAAM,EACN,KAAK,EACL,YAAY,EACZ,YAAY,EACb,EAAE;IACD,MAAM,CAAC,EAAE,0BAA0B,CAAA;IACnC,KAAK,EAAE,yBAAyB,CAAA;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAe5C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,MAAM,EACN,KAAK,EACL,aAAa,EACb,YAAY,EACb,EAAE;IACD,MAAM,CAAC,EAAE,0BAA0B,CAAA;IACnC,KAAK,EAAE,yBAAyB,CAAA;IAChC,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAiBhD"}
1
+ {"version":3,"file":"acquire.d.ts","sourceRoot":"","sources":["../../src/descriptors/acquire.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAE1E;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,CAAC,OAAO,EAAE;QAC5B,YAAY,EAAE,MAAM,CAAA;KACrB,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAA;CAC9C;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,cAAc,CAAC,OAAO,EAAE;QACtB,YAAY,EAAE,MAAM,CAAA;KACrB,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAAA;IAC7C,eAAe,CAAC,OAAO,EAAE;QACvB,YAAY,EAAE,MAAM,CAAA;QACpB,UAAU,EAAE,oBAAoB,CAAA;KACjC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,GAAG,EACH,OAAO,EACR,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,0BAA0B,CAU7B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,MAAM,EACN,KAAK,EACL,YAAY,EACZ,YAAY,EACb,EAAE;IACD,MAAM,CAAC,EAAE,0BAA0B,CAAA;IACnC,KAAK,EAAE,yBAAyB,CAAA;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAiB5C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,MAAM,EACN,KAAK,EACL,aAAa,EACb,YAAY,EACb,EAAE;IACD,MAAM,CAAC,EAAE,0BAA0B,CAAA;IACnC,KAAK,EAAE,yBAAyB,CAAA;IAChC,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAiBhD"}
@@ -21,11 +21,12 @@ export function wasDescriptorSource({ was, spaceId }) {
21
21
  }
22
22
  /**
23
23
  * Acquires one collection's descriptor: fetches it from the source, caching a
24
- * success; falls back to the cached copy when the fetch fails; and with no
25
- * source at all (a purely local code path) reads the cache alone. A successful
26
- * fetch that returns no descriptor resolves `undefined` -- an unshared
27
- * collection stays on the single-key path -- and deliberately leaves any
28
- * cached copy in place, mirroring the fetch-failure fallback.
24
+ * success; falls back to the cached copy whenever the fetch yields no
25
+ * descriptor (it threw, or it came back empty -- a masked 404 for an
26
+ * unauthorized read looks exactly like an unencrypted collection); and with no
27
+ * source at all (a purely local code path) reads the cache alone. Any cached
28
+ * copy is deliberately left in place, never cleared by an empty fetch.
29
+ * `undefined` means no descriptor exists anywhere for this collection.
29
30
  *
30
31
  * @param options {object}
31
32
  * @param [options.source] {EncryptionDescriptorSource} omit for cache-only
@@ -33,8 +34,8 @@ export function wasDescriptorSource({ was, spaceId }) {
33
34
  * @param options.cache {EncryptionDescriptorCache}
34
35
  * @param options.collectionId {string}
35
36
  * @param [options.onFetchError] {function} observes a swallowed fetch
36
- * failure (the cached-fallback branch); errors from the cache itself throw
37
- * through
37
+ * failure (the thrown-fetch branch only; an empty description is not an
38
+ * error). Errors from the cache itself throw through
38
39
  * @returns {Promise<CollectionEncryption | undefined>}
39
40
  */
40
41
  export async function acquireDescriptor({ source, cache, collectionId, onFetchError }) {
@@ -47,7 +48,9 @@ export async function acquireDescriptor({ source, cache, collectionId, onFetchEr
47
48
  await cache.writeDescriptor({ collectionId, descriptor: fetched });
48
49
  return fetched;
49
50
  }
50
- return undefined;
51
+ // Empty description: not authoritative (an unauthorized read is masked as
52
+ // an absent one), so a warm cache still serves the collection.
53
+ return cache.readDescriptor({ collectionId });
51
54
  }
52
55
  catch (err) {
53
56
  onFetchError?.(err, { collectionId });
@@ -1 +1 @@
1
- {"version":3,"file":"acquire.js","sourceRoot":"","sources":["../../src/descriptors/acquire.ts"],"names":[],"mappings":"AAgDA;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,GAAG,EACH,OAAO,EAIR;IACC,OAAO;QACL,KAAK,CAAC,oBAAoB,CAAC,EAAE,YAAY,EAAE;YACzC,MAAM,WAAW,GAAG,MAAM,GAAG;iBAC1B,KAAK,CAAC,OAAO,CAAC;iBACd,UAAU,CAAC,YAAY,CAAC;iBACxB,QAAQ,EAAE,CAAA;YACb,OAAO,WAAW,EAAE,UAAU,IAAI,SAAS,CAAA;QAC7C,CAAC;KACF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EACtC,MAAM,EACN,KAAK,EACL,YAAY,EACZ,YAAY,EAMb;IACC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,KAAK,CAAC,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC,CAAA;IAC/C,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,EAAE,YAAY,EAAE,CAAC,CAAA;QACnE,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,KAAK,CAAC,eAAe,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAA;YAClE,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;QACrC,OAAO,KAAK,CAAC,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC,CAAA;IAC/C,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EACvC,MAAM,EACN,KAAK,EACL,aAAa,EACb,YAAY,EAMb;IACC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,aAAa,CAAC,GAAG,CACf,KAAK,EAAC,YAAY,EAAC,EAAE,CACnB;QACE,YAAY;QACZ,MAAM,iBAAiB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;KAC9D,CACb,CACF,CAAA;IACD,MAAM,WAAW,GAAyC,EAAE,CAAA;IAC5D,KAAK,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,IAAI,QAAQ,EAAE,CAAC;QAClD,IAAI,UAAU,EAAE,CAAC;YACf,WAAW,CAAC,YAAY,CAAC,GAAG,UAAU,CAAA;QACxC,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAA;AACpB,CAAC"}
1
+ {"version":3,"file":"acquire.js","sourceRoot":"","sources":["../../src/descriptors/acquire.ts"],"names":[],"mappings":"AAuDA;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,GAAG,EACH,OAAO,EAIR;IACC,OAAO;QACL,KAAK,CAAC,oBAAoB,CAAC,EAAE,YAAY,EAAE;YACzC,MAAM,WAAW,GAAG,MAAM,GAAG;iBAC1B,KAAK,CAAC,OAAO,CAAC;iBACd,UAAU,CAAC,YAAY,CAAC;iBACxB,QAAQ,EAAE,CAAA;YACb,OAAO,WAAW,EAAE,UAAU,IAAI,SAAS,CAAA;QAC7C,CAAC;KACF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EACtC,MAAM,EACN,KAAK,EACL,YAAY,EACZ,YAAY,EAMb;IACC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,KAAK,CAAC,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC,CAAA;IAC/C,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,EAAE,YAAY,EAAE,CAAC,CAAA;QACnE,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,KAAK,CAAC,eAAe,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAA;YAClE,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,0EAA0E;QAC1E,+DAA+D;QAC/D,OAAO,KAAK,CAAC,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC,CAAA;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;QACrC,OAAO,KAAK,CAAC,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC,CAAA;IAC/C,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EACvC,MAAM,EACN,KAAK,EACL,aAAa,EACb,YAAY,EAMb;IACC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,aAAa,CAAC,GAAG,CACf,KAAK,EAAC,YAAY,EAAC,EAAE,CACnB;QACE,YAAY;QACZ,MAAM,iBAAiB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;KAC9D,CACb,CACF,CAAA;IACD,MAAM,WAAW,GAAyC,EAAE,CAAA;IAC5D,KAAK,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,IAAI,QAAQ,EAAE,CAAC;QAClD,IAAI,UAAU,EAAE,CAAC;YACf,WAAW,CAAC,YAAY,CAAC,GAAG,UAAU,CAAA;QACxC,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAA;AACpB,CAAC"}
@@ -8,14 +8,20 @@
8
8
  * conflict resolver) rather than a row-scanning store.
9
9
  *
10
10
  * Built, the cipher acquires the collection's descriptor (fetch, cache the
11
- * success, cached fallback on failure -- see `acquire.ts`) and constructs the
11
+ * success, cached fallback whenever the fetch yields none -- see
12
+ * `acquire.ts`) and constructs the
12
13
  * 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
14
+ * epochs, the build refuses fail-closed (every encrypted collection's
15
+ * descriptor carries an epoch roster from provisioning, so the absence can
16
+ * only mean the install has not landed or the host is lying). When a decrypt
17
+ * throws
14
18
  * `UnknownEpochError`, the cipher re-acquires the descriptor, rebuilds itself,
15
19
  * and retries that decrypt exactly once -- and only once per cipher instance,
16
20
  * which the host scopes to one `(profile, collection)` session by dropping
17
21
  * its cipher cache when the session ends. A second failure (or any unknown
18
- * epoch after the one refresh is spent) propagates.
22
+ * epoch after the one refresh is spent) propagates. A refresh that itself
23
+ * fails does not count as spent -- the original `UnknownEpochError` is
24
+ * rethrown and a later decrypt may try again.
19
25
  */
20
26
  import type { IKeyAgreementKey, IKeyResolver } from '@interop/data-integrity-core';
21
27
  import { type DocCipher } from '@interop/was-client/edv';
@@ -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;;;;;;;;;;;;;;;;;;;;;GAqBG;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,CAoFrB"}
@@ -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
@@ -56,10 +64,33 @@ export async function createRefreshingEdvDocCipher({ keyAgreementKey, keyResolve
56
64
  if (!(err instanceof UnknownEpochError) || !source) {
57
65
  throw err;
58
66
  }
59
- refreshed ??= build().then(cipher => {
60
- inner = cipher;
61
- });
62
- await refreshed;
67
+ if (!refreshed) {
68
+ const attempt = build().then(cipher => {
69
+ inner = cipher;
70
+ });
71
+ refreshed = attempt;
72
+ // Only a COMPLETED refresh is spent. A rejected one (the description
73
+ // could not be read and no cached copy answered either) un-arms the
74
+ // guard so a later unknown-epoch decrypt may try again; a successful
75
+ // refresh that still cannot route the envelope stays spent for the
76
+ // session, which is what keeps a genuinely foreign envelope from
77
+ // driving a refetch loop.
78
+ attempt.catch(() => {
79
+ if (refreshed === attempt) {
80
+ refreshed = null;
81
+ }
82
+ });
83
+ }
84
+ try {
85
+ await refreshed;
86
+ }
87
+ catch {
88
+ // The refresh failed, so nothing was learned about this envelope's
89
+ // epoch: surface the original UnknownEpochError rather than the
90
+ // build failure, so callers classifying on it (the create-loss
91
+ // re-mint) still see the row they exist to repair.
92
+ throw err;
93
+ }
63
94
  // One retry under the swapped cipher. If the refresh was already
64
95
  // spent before this decrypt began, this re-attempt is a local
65
96
  // no-network decrypt that fails the same way -- so a genuinely
@@ -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":"AA6BA,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,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;wBACpC,KAAK,GAAG,MAAM,CAAA;oBAChB,CAAC,CAAC,CAAA;oBACF,SAAS,GAAG,OAAO,CAAA;oBACnB,qEAAqE;oBACrE,oEAAoE;oBACpE,qEAAqE;oBACrE,mEAAmE;oBACnE,iEAAiE;oBACjE,0BAA0B;oBAC1B,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE;wBACjB,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;4BAC1B,SAAS,GAAG,IAAI,CAAA;wBAClB,CAAC;oBACH,CAAC,CAAC,CAAA;gBACJ,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,SAAS,CAAA;gBACjB,CAAC;gBAAC,MAAM,CAAC;oBACP,mEAAmE;oBACnE,gEAAgE;oBAChE,+DAA+D;oBAC/D,mDAAmD;oBACnD,MAAM,GAAG,CAAA;gBACX,CAAC;gBACD,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"}
@@ -15,8 +15,12 @@
15
15
  * - `wasDescriptorSource` -- the `EncryptionDescriptorSource` over a
16
16
  * was-client handle.
17
17
  * - `acquireDescriptor` / `acquireDescriptors` -- fetch + cache with the
18
- * cached fallback (offline, a previously-shared collection keeps encrypting
19
- * under its current epoch; no descriptor at all is the single-key path).
18
+ * cached fallback whenever the fetch yields no descriptor, thrown or empty
19
+ * (offline, a collection keeps encrypting under its current epoch; an empty
20
+ * description is ambiguous, since WAS masks an unauthorized read as an
21
+ * absent one). No descriptor anywhere means a plaintext collection, or an
22
+ * encrypted one whose epoch[0] install has not landed -- which a caller that
23
+ * has declared the collection encrypted must refuse fail-closed.
20
24
  * - `DescriptorRefreshPolicy` -- the once-per-collection-per-session
21
25
  * unknown-epoch refresh guard, plus the refresh-and-re-read-once wrapper
22
26
  * for hosts whose reads scan rows and count unknown-epoch skips.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/descriptors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC3B,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAEtD,OAAO,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/descriptors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC3B,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAEtD,OAAO,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA"}
@@ -15,8 +15,12 @@
15
15
  * - `wasDescriptorSource` -- the `EncryptionDescriptorSource` over a
16
16
  * was-client handle.
17
17
  * - `acquireDescriptor` / `acquireDescriptors` -- fetch + cache with the
18
- * cached fallback (offline, a previously-shared collection keeps encrypting
19
- * under its current epoch; no descriptor at all is the single-key path).
18
+ * cached fallback whenever the fetch yields no descriptor, thrown or empty
19
+ * (offline, a collection keeps encrypting under its current epoch; an empty
20
+ * description is ambiguous, since WAS masks an unauthorized read as an
21
+ * absent one). No descriptor anywhere means a plaintext collection, or an
22
+ * encrypted one whose epoch[0] install has not landed -- which a caller that
23
+ * has declared the collection encrypted must refuse fail-closed.
20
24
  * - `DescriptorRefreshPolicy` -- the once-per-collection-per-session
21
25
  * unknown-epoch refresh guard, plus the refresh-and-re-read-once wrapper
22
26
  * for hosts whose reads scan rows and count unknown-epoch skips.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/descriptors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,cAAc,CAAA;AAMrB,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAEtD,OAAO,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/descriptors/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,cAAc,CAAA;AAMrB,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAEtD,OAAO,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA"}
@@ -10,8 +10,13 @@
10
10
  * wire-level unlock derivation (implemented over `@noble/hashes`, so it runs
11
11
  * unchanged where WebCrypto's `deriveBits` is unavailable) and the unlock
12
12
  * Space addressing convention.
13
- * - `wrapKeyringRecord` / `unwrapKeyringRecord` -- the `{ version, wrapped }`
14
- * account-pointer record codec.
13
+ * - `wrapKeyringRecord` / `unwrapKeyringRecord` -- the
14
+ * `{ version, encryption, wrapped }` account-pointer record codec.
15
+ * - `mintRecordEncryption` / `recordCipher` / `parseRecordFrame` -- the
16
+ * record-own-epoch envelope construction the codec seals with plus the frame
17
+ * validation it opens with, exported so an app's own locally stored records
18
+ * seal and unseal the same way (under their own cipher context) rather than
19
+ * re-deriving the construction.
15
20
  * - `ensureUnlockSpace` / `getUnlockKeyring` / `putUnlockKeyring` /
16
21
  * `deleteUnlockSpace` / `deleteUnlockSpaceWithCapability` -- the unlock
17
22
  * Space's lifecycle and its one resource.
@@ -23,7 +28,7 @@
23
28
  */
24
29
  export { deriveUnlockIdentity, KEYRING_KDF, UNLOCK_HANDLE, UNLOCK_KEY_NAME, unlockSpaceIdFor } from './kdf.js';
25
30
  export type { UnlockIdentity, UnlockKdf } from './kdf.js';
26
- export { KEYRING_RECORD_VERSION, parseRecordPointer, unwrapKeyringRecord, wrapKeyringRecord } from './record.js';
31
+ export { KEYRING_RECORD_VERSION, mintRecordEncryption, parseRecordFrame, parseRecordPointer, recordCipher, unwrapKeyringRecord, wrapKeyringRecord } from './record.js';
27
32
  export type { AccountPointer, KeyringRecordContents } from './record.js';
28
33
  export { deleteUnlockSpace, deleteUnlockSpaceWithCapability, ensureUnlockSpace, getUnlockKeyring, putUnlockKeyring, putUnlockKeyringWithCapability, UNLOCK_SPACE_NAME } from './unlockSpace.js';
29
34
  export { fetchKeyringRecord } from './fetch.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keyring/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,eAAe,EACf,gBAAgB,EACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzD,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAExE,OAAO,EACL,iBAAiB,EACjB,+BAA+B,EAC/B,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,8BAA8B,EAC9B,iBAAiB,EAClB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keyring/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,eAAe,EACf,gBAAgB,EACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzD,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAExE,OAAO,EACL,iBAAiB,EACjB,+BAA+B,EAC/B,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,8BAA8B,EAC9B,iBAAiB,EAClB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA"}
@@ -10,8 +10,13 @@
10
10
  * wire-level unlock derivation (implemented over `@noble/hashes`, so it runs
11
11
  * unchanged where WebCrypto's `deriveBits` is unavailable) and the unlock
12
12
  * Space addressing convention.
13
- * - `wrapKeyringRecord` / `unwrapKeyringRecord` -- the `{ version, wrapped }`
14
- * account-pointer record codec.
13
+ * - `wrapKeyringRecord` / `unwrapKeyringRecord` -- the
14
+ * `{ version, encryption, wrapped }` account-pointer record codec.
15
+ * - `mintRecordEncryption` / `recordCipher` / `parseRecordFrame` -- the
16
+ * record-own-epoch envelope construction the codec seals with plus the frame
17
+ * validation it opens with, exported so an app's own locally stored records
18
+ * seal and unseal the same way (under their own cipher context) rather than
19
+ * re-deriving the construction.
15
20
  * - `ensureUnlockSpace` / `getUnlockKeyring` / `putUnlockKeyring` /
16
21
  * `deleteUnlockSpace` / `deleteUnlockSpaceWithCapability` -- the unlock
17
22
  * Space's lifecycle and its one resource.
@@ -22,7 +27,7 @@
22
27
  * was-client dependency graph (the same isolation pattern as `./identity`).
23
28
  */
24
29
  export { deriveUnlockIdentity, KEYRING_KDF, UNLOCK_HANDLE, UNLOCK_KEY_NAME, unlockSpaceIdFor } from './kdf.js';
25
- export { KEYRING_RECORD_VERSION, parseRecordPointer, unwrapKeyringRecord, wrapKeyringRecord } from './record.js';
30
+ export { KEYRING_RECORD_VERSION, mintRecordEncryption, parseRecordFrame, parseRecordPointer, recordCipher, unwrapKeyringRecord, wrapKeyringRecord } from './record.js';
26
31
  export { deleteUnlockSpace, deleteUnlockSpaceWithCapability, ensureUnlockSpace, getUnlockKeyring, putUnlockKeyring, putUnlockKeyringWithCapability, UNLOCK_SPACE_NAME } from './unlockSpace.js';
27
32
  export { fetchKeyringRecord } from './fetch.js';
28
33
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/keyring/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,eAAe,EACf,gBAAgB,EACjB,MAAM,UAAU,CAAA;AAGjB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,iBAAiB,EACjB,+BAA+B,EAC/B,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,8BAA8B,EAC9B,iBAAiB,EAClB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/keyring/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,eAAe,EACf,gBAAgB,EACjB,MAAM,UAAU,CAAA;AAGjB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,iBAAiB,EACjB,+BAA+B,EAC/B,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,8BAA8B,EAC9B,iBAAiB,EAClB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA"}
@@ -2,24 +2,93 @@
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 sealed under the record's own key
12
+ * epoch: every EDV envelope seals to an epoch key, so the record carries its
13
+ * one-epoch descriptor in its `encryption` member, epoch[0] wrapped to the
14
+ * unlock key-agreement key. The record stays self-contained -- unlock KAK in,
15
+ * contents out. The cipher's `keyring` collection context labels errors only
16
+ * (the codec is agnostic to it); what keeps a swapped-in foreign record from
17
+ * being accepted is the contents validation on unwrap, not the cipher.
14
18
  */
15
19
  import type { IKeyAgreementKey, IKeyResolver } from '@interop/data-integrity-core';
20
+ import type { CollectionEncryption } from '@interop/was-client';
21
+ import { type DocCipher } from '@interop/was-client/edv';
16
22
  /**
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.
23
+ * The version stamped on the stored `{ version, encryption, wrapped }` keyring
24
+ * envelope: the record whose envelope seals under the record's own key epoch
25
+ * (the `encryption` member). Any other version is refused as unusable -- such
26
+ * accounts are re-provisioned, not migrated.
21
27
  */
22
- export declare const KEYRING_RECORD_VERSION = 2;
28
+ export declare const KEYRING_RECORD_VERSION = 1;
29
+ /**
30
+ * Mints the one-epoch descriptor a fresh record is sealed under: epoch[0]
31
+ * wrapped to the given KAK alone, built through `initRecipients` against a
32
+ * throwaway in-memory store (the descriptor's home is the record itself).
33
+ * Exported for any consumer sealing a self-contained
34
+ * `{ version, encryption, wrapped }` record -- the keyring and recovery
35
+ * records here, and a wallet app's own locally stored records (e.g.
36
+ * freewallet's client-key record and unlock-methods registry).
37
+ *
38
+ * @param options {object}
39
+ * @param options.keyAgreementKey {IKeyAgreementKey} the wrapping KAK (for
40
+ * the keyring record, the unlock KAK)
41
+ * @returns {Promise<CollectionEncryption>}
42
+ */
43
+ export declare function mintRecordEncryption({ keyAgreementKey }: {
44
+ keyAgreementKey: IKeyAgreementKey;
45
+ }): Promise<CollectionEncryption>;
46
+ /**
47
+ * Builds the record cipher: an EDV cipher over the record's own descriptor.
48
+ * Shared by the wrap and unwrap paths (and by the recovery record, which
49
+ * reuses the keyring cipher context verbatim); an app's own record kind
50
+ * passes its own `collectionId` so its failures name the record kind. The
51
+ * context labels errors only -- the codec is agnostic to it, so a record
52
+ * kind's real swap protection is its contents validation on unwrap.
53
+ *
54
+ * @param options {object}
55
+ * @param options.keyAgreementKey {IKeyAgreementKey} the wrapping KAK (for
56
+ * the keyring record, the unlock KAK)
57
+ * @param options.keyResolver {IKeyResolver}
58
+ * @param options.encryption {CollectionEncryption} the record's descriptor
59
+ * @param [options.collectionId] {string} the cipher context failures are
60
+ * labeled with; defaults to the keyring context
61
+ * @returns {Promise<DocCipher>}
62
+ */
63
+ export declare function recordCipher({ keyAgreementKey, keyResolver, encryption, collectionId }: {
64
+ keyAgreementKey: IKeyAgreementKey;
65
+ keyResolver: IKeyResolver;
66
+ encryption: CollectionEncryption;
67
+ collectionId?: string;
68
+ }): Promise<DocCipher>;
69
+ /**
70
+ * Validates the common `{ version, encryption, wrapped }` frame of a stored
71
+ * record (keyring or recovery -- `label` names the refusals) and returns its
72
+ * members. Exported so an app's own record kinds open their records through
73
+ * the same frame validation the codec here seals with, rather than re-deriving
74
+ * the version and shape checks.
75
+ *
76
+ * @param options {object}
77
+ * @param options.record {unknown}
78
+ * @param options.label {string} `'keyring'`, `'recovery'`, or an app record
79
+ * kind's own label
80
+ * @param [options.version] {number} the version the frame must carry;
81
+ * defaults to the keyring record version
82
+ * @returns {{ encryption: CollectionEncryption, wrapped: unknown }}
83
+ */
84
+ export declare function parseRecordFrame({ record, label, version }: {
85
+ record: unknown;
86
+ label: string;
87
+ version?: number;
88
+ }): {
89
+ encryption: CollectionEncryption;
90
+ wrapped: unknown;
91
+ };
23
92
  /**
24
93
  * The account pointer a keyring record carries in place of the retired data
25
94
  * seed: where the account lives (`spaceId` + `host`, the WAS server origin)
@@ -45,8 +114,9 @@ export interface KeyringRecordContents {
45
114
  }
46
115
  /**
47
116
  * 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.
117
+ * email, and pointer (+ timestamp) sealed under a freshly minted record epoch
118
+ * whose key is wrapped to the unlock KAK. Deliberately carries no key material
119
+ * of any kind.
50
120
  *
51
121
  * @param options {object}
52
122
  * @param options.controller {string} the account did:key
@@ -55,7 +125,8 @@ export interface KeyringRecordContents {
55
125
  * no-WAS deployments)
56
126
  * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
57
127
  * @param options.keyResolver {IKeyResolver}
58
- * @returns {Promise<{ version: number, wrapped: unknown }>}
128
+ * @returns {Promise<{ version: number, encryption: CollectionEncryption,
129
+ * wrapped: unknown }>}
59
130
  */
60
131
  export declare function wrapKeyringRecord({ controller, email, pointer, keyAgreementKey, keyResolver }: {
61
132
  controller: string;
@@ -65,12 +136,12 @@ export declare function wrapKeyringRecord({ controller, email, pointer, keyAgree
65
136
  keyResolver: IKeyResolver;
66
137
  }): Promise<{
67
138
  version: number;
139
+ encryption: CollectionEncryption;
68
140
  wrapped: unknown;
69
141
  }>;
70
142
  /**
71
143
  * 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
144
+ * not the current one (accounts are re-provisioned, not migrated), and
74
145
  * sanity-checks the decrypted plaintext (non-empty controller, well-formed
75
146
  * pointer when present).
76
147
  *
@@ -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;;;;;;;;;;;;;;GAcG;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;;;;;;;;;;;;;GAaG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,eAAe,EAChB,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;CAClC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiBhC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAAC,EACjC,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAoC,EACrC,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;IACzB,UAAU,EAAE,oBAAoB,CAAA;IAChC,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,GAAG,OAAO,CAAC,SAAS,CAAC,CAOrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,KAAK,EACL,OAAgC,EACjC,EAAE;IACD,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,GAAG;IAAE,UAAU,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAoCzD;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;;;;;;;;;;;;;;;GAeG;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;IACV,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,oBAAoB,CAAA;IAChC,OAAO,EAAE,OAAO,CAAA;CACjB,CAAC,CAwBD;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"}