@interop/was-react 0.1.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 (223) hide show
  1. package/LICENSE.md +20 -0
  2. package/README.md +410 -0
  3. package/dist/auth/chapi.d.ts +43 -0
  4. package/dist/auth/chapi.d.ts.map +1 -0
  5. package/dist/auth/chapi.js +107 -0
  6. package/dist/auth/chapi.js.map +1 -0
  7. package/dist/auth/loginFlow.d.ts +93 -0
  8. package/dist/auth/loginFlow.d.ts.map +1 -0
  9. package/dist/auth/loginFlow.js +149 -0
  10. package/dist/auth/loginFlow.js.map +1 -0
  11. package/dist/auth/loginRequest.d.ts +66 -0
  12. package/dist/auth/loginRequest.d.ts.map +1 -0
  13. package/dist/auth/loginRequest.js +83 -0
  14. package/dist/auth/loginRequest.js.map +1 -0
  15. package/dist/auth/verifyResponse.d.ts +52 -0
  16. package/dist/auth/verifyResponse.d.ts.map +1 -0
  17. package/dist/auth/verifyResponse.js +138 -0
  18. package/dist/auth/verifyResponse.js.map +1 -0
  19. package/dist/auth/verifyResponse.test.d.ts +2 -0
  20. package/dist/auth/verifyResponse.test.d.ts.map +1 -0
  21. package/dist/auth/verifyResponse.test.js +282 -0
  22. package/dist/auth/verifyResponse.test.js.map +1 -0
  23. package/dist/auth/walletRequestTypes.d.ts +143 -0
  24. package/dist/auth/walletRequestTypes.d.ts.map +1 -0
  25. package/dist/auth/walletRequestTypes.js +2 -0
  26. package/dist/auth/walletRequestTypes.js.map +1 -0
  27. package/dist/config.d.ts +150 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +27 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/dev/cli.d.ts +16 -0
  32. package/dist/dev/cli.d.ts.map +1 -0
  33. package/dist/dev/cli.js +122 -0
  34. package/dist/dev/cli.js.map +1 -0
  35. package/dist/dev/cli.test.d.ts +2 -0
  36. package/dist/dev/cli.test.d.ts.map +1 -0
  37. package/dist/dev/cli.test.js +39 -0
  38. package/dist/dev/cli.test.js.map +1 -0
  39. package/dist/dev/index.d.ts +10 -0
  40. package/dist/dev/index.d.ts.map +1 -0
  41. package/dist/dev/index.js +10 -0
  42. package/dist/dev/index.js.map +1 -0
  43. package/dist/dev/provisionDevGrants.d.ts +73 -0
  44. package/dist/dev/provisionDevGrants.d.ts.map +1 -0
  45. package/dist/dev/provisionDevGrants.js +176 -0
  46. package/dist/dev/provisionDevGrants.js.map +1 -0
  47. package/dist/grants.d.ts +59 -0
  48. package/dist/grants.d.ts.map +1 -0
  49. package/dist/grants.js +82 -0
  50. package/dist/grants.js.map +1 -0
  51. package/dist/grants.test.d.ts +2 -0
  52. package/dist/grants.test.d.ts.map +1 -0
  53. package/dist/grants.test.js +79 -0
  54. package/dist/grants.test.js.map +1 -0
  55. package/dist/identity/agents.d.ts +99 -0
  56. package/dist/identity/agents.d.ts.map +1 -0
  57. package/dist/identity/agents.js +132 -0
  58. package/dist/identity/agents.js.map +1 -0
  59. package/dist/identity/appSession.d.ts +78 -0
  60. package/dist/identity/appSession.d.ts.map +1 -0
  61. package/dist/identity/appSession.js +93 -0
  62. package/dist/identity/appSession.js.map +1 -0
  63. package/dist/identity/documentLoader.d.ts +20 -0
  64. package/dist/identity/documentLoader.d.ts.map +1 -0
  65. package/dist/identity/documentLoader.js +99 -0
  66. package/dist/identity/documentLoader.js.map +1 -0
  67. package/dist/identity/initAppSession.d.ts +27 -0
  68. package/dist/identity/initAppSession.d.ts.map +1 -0
  69. package/dist/identity/initAppSession.js +30 -0
  70. package/dist/identity/initAppSession.js.map +1 -0
  71. package/dist/identity/seedCredential.d.ts +74 -0
  72. package/dist/identity/seedCredential.d.ts.map +1 -0
  73. package/dist/identity/seedCredential.js +165 -0
  74. package/dist/identity/seedCredential.js.map +1 -0
  75. package/dist/identity/seedCredential.test.d.ts +2 -0
  76. package/dist/identity/seedCredential.test.d.ts.map +1 -0
  77. package/dist/identity/seedCredential.test.js +205 -0
  78. package/dist/identity/seedCredential.test.js.map +1 -0
  79. package/dist/identity/seedStore.d.ts +27 -0
  80. package/dist/identity/seedStore.d.ts.map +1 -0
  81. package/dist/identity/seedStore.js +74 -0
  82. package/dist/identity/seedStore.js.map +1 -0
  83. package/dist/index.d.ts +37 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +46 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/mui/ProtectedRoute.d.ts +10 -0
  88. package/dist/mui/ProtectedRoute.d.ts.map +1 -0
  89. package/dist/mui/ProtectedRoute.js +54 -0
  90. package/dist/mui/ProtectedRoute.js.map +1 -0
  91. package/dist/mui/ReconnectBanner.d.ts +2 -0
  92. package/dist/mui/ReconnectBanner.d.ts.map +1 -0
  93. package/dist/mui/ReconnectBanner.js +20 -0
  94. package/dist/mui/ReconnectBanner.js.map +1 -0
  95. package/dist/mui/SyncStatusChip.d.ts +2 -0
  96. package/dist/mui/SyncStatusChip.d.ts.map +1 -0
  97. package/dist/mui/SyncStatusChip.js +27 -0
  98. package/dist/mui/SyncStatusChip.js.map +1 -0
  99. package/dist/mui/index.d.ts +12 -0
  100. package/dist/mui/index.d.ts.map +1 -0
  101. package/dist/mui/index.js +12 -0
  102. package/dist/mui/index.js.map +1 -0
  103. package/dist/react/WasSessionProvider.d.ts +36 -0
  104. package/dist/react/WasSessionProvider.d.ts.map +1 -0
  105. package/dist/react/WasSessionProvider.js +35 -0
  106. package/dist/react/WasSessionProvider.js.map +1 -0
  107. package/dist/react/hooks.d.ts +66 -0
  108. package/dist/react/hooks.d.ts.map +1 -0
  109. package/dist/react/hooks.js +121 -0
  110. package/dist/react/hooks.js.map +1 -0
  111. package/dist/react/index.d.ts +11 -0
  112. package/dist/react/index.d.ts.map +1 -0
  113. package/dist/react/index.js +11 -0
  114. package/dist/react/index.js.map +1 -0
  115. package/dist/session/appReadyStore.d.ts +10 -0
  116. package/dist/session/appReadyStore.d.ts.map +1 -0
  117. package/dist/session/appReadyStore.js +22 -0
  118. package/dist/session/appReadyStore.js.map +1 -0
  119. package/dist/session/authStore.d.ts +71 -0
  120. package/dist/session/authStore.d.ts.map +1 -0
  121. package/dist/session/authStore.js +347 -0
  122. package/dist/session/authStore.js.map +1 -0
  123. package/dist/session/index.d.ts +10 -0
  124. package/dist/session/index.d.ts.map +1 -0
  125. package/dist/session/index.js +10 -0
  126. package/dist/session/index.js.map +1 -0
  127. package/dist/storage/entityStore.d.ts +51 -0
  128. package/dist/storage/entityStore.d.ts.map +1 -0
  129. package/dist/storage/entityStore.js +78 -0
  130. package/dist/storage/entityStore.js.map +1 -0
  131. package/dist/storage/localStore.d.ts +144 -0
  132. package/dist/storage/localStore.d.ts.map +1 -0
  133. package/dist/storage/localStore.js +289 -0
  134. package/dist/storage/localStore.js.map +1 -0
  135. package/dist/storage/rehydrate.d.ts +56 -0
  136. package/dist/storage/rehydrate.d.ts.map +1 -0
  137. package/dist/storage/rehydrate.js +87 -0
  138. package/dist/storage/rehydrate.js.map +1 -0
  139. package/dist/storage/rehydrate.test.d.ts +2 -0
  140. package/dist/storage/rehydrate.test.d.ts.map +1 -0
  141. package/dist/storage/rehydrate.test.js +172 -0
  142. package/dist/storage/rehydrate.test.js.map +1 -0
  143. package/dist/storage/storageManager.d.ts +39 -0
  144. package/dist/storage/storageManager.d.ts.map +1 -0
  145. package/dist/storage/storageManager.js +68 -0
  146. package/dist/storage/storageManager.js.map +1 -0
  147. package/dist/storage/syncController.d.ts +79 -0
  148. package/dist/storage/syncController.d.ts.map +1 -0
  149. package/dist/storage/syncController.js +189 -0
  150. package/dist/storage/syncController.js.map +1 -0
  151. package/dist/storage/syncStatusStore.d.ts +17 -0
  152. package/dist/storage/syncStatusStore.d.ts.map +1 -0
  153. package/dist/storage/syncStatusStore.js +19 -0
  154. package/dist/storage/syncStatusStore.js.map +1 -0
  155. package/dist/storage/wasRemoteStore.d.ts +73 -0
  156. package/dist/storage/wasRemoteStore.d.ts.map +1 -0
  157. package/dist/storage/wasRemoteStore.js +76 -0
  158. package/dist/storage/wasRemoteStore.js.map +1 -0
  159. package/dist/storage/wasSync.d.ts +42 -0
  160. package/dist/storage/wasSync.d.ts.map +1 -0
  161. package/dist/storage/wasSync.js +34 -0
  162. package/dist/storage/wasSync.js.map +1 -0
  163. package/dist/sync/changesQuery.d.ts +44 -0
  164. package/dist/sync/changesQuery.d.ts.map +1 -0
  165. package/dist/sync/changesQuery.js +61 -0
  166. package/dist/sync/changesQuery.js.map +1 -0
  167. package/dist/sync/changesQuery.test.d.ts +2 -0
  168. package/dist/sync/changesQuery.test.d.ts.map +1 -0
  169. package/dist/sync/changesQuery.test.js +145 -0
  170. package/dist/sync/changesQuery.test.js.map +1 -0
  171. package/dist/sync/docCipher.d.ts +68 -0
  172. package/dist/sync/docCipher.d.ts.map +1 -0
  173. package/dist/sync/docCipher.js +89 -0
  174. package/dist/sync/docCipher.js.map +1 -0
  175. package/dist/sync/feedMasterPort.d.ts +30 -0
  176. package/dist/sync/feedMasterPort.d.ts.map +1 -0
  177. package/dist/sync/feedMasterPort.js +58 -0
  178. package/dist/sync/feedMasterPort.js.map +1 -0
  179. package/dist/sync/index.d.ts +21 -0
  180. package/dist/sync/index.d.ts.map +1 -0
  181. package/dist/sync/index.js +21 -0
  182. package/dist/sync/index.js.map +1 -0
  183. package/dist/sync/lww.d.ts +29 -0
  184. package/dist/sync/lww.d.ts.map +1 -0
  185. package/dist/sync/lww.js +28 -0
  186. package/dist/sync/lww.js.map +1 -0
  187. package/dist/sync/lww.test.d.ts +2 -0
  188. package/dist/sync/lww.test.d.ts.map +1 -0
  189. package/dist/sync/lww.test.js +28 -0
  190. package/dist/sync/lww.test.js.map +1 -0
  191. package/dist/sync/lwwConflictHandler.d.ts +49 -0
  192. package/dist/sync/lwwConflictHandler.d.ts.map +1 -0
  193. package/dist/sync/lwwConflictHandler.js +65 -0
  194. package/dist/sync/lwwConflictHandler.js.map +1 -0
  195. package/dist/sync/lwwConflictHandler.test.d.ts +2 -0
  196. package/dist/sync/lwwConflictHandler.test.d.ts.map +1 -0
  197. package/dist/sync/lwwConflictHandler.test.js +78 -0
  198. package/dist/sync/lwwConflictHandler.test.js.map +1 -0
  199. package/dist/sync/pushWrites.d.ts +57 -0
  200. package/dist/sync/pushWrites.d.ts.map +1 -0
  201. package/dist/sync/pushWrites.js +159 -0
  202. package/dist/sync/pushWrites.js.map +1 -0
  203. package/dist/sync/pushWrites.test.d.ts +2 -0
  204. package/dist/sync/pushWrites.test.d.ts.map +1 -0
  205. package/dist/sync/pushWrites.test.js +287 -0
  206. package/dist/sync/pushWrites.test.js.map +1 -0
  207. package/dist/sync/syncedDocSchema.d.ts +22 -0
  208. package/dist/sync/syncedDocSchema.d.ts.map +1 -0
  209. package/dist/sync/syncedDocSchema.js +26 -0
  210. package/dist/sync/syncedDocSchema.js.map +1 -0
  211. package/dist/sync/types.d.ts +194 -0
  212. package/dist/sync/types.d.ts.map +1 -0
  213. package/dist/sync/types.js +36 -0
  214. package/dist/sync/types.js.map +1 -0
  215. package/dist/sync/wasReplication.d.ts +47 -0
  216. package/dist/sync/wasReplication.d.ts.map +1 -0
  217. package/dist/sync/wasReplication.js +55 -0
  218. package/dist/sync/wasReplication.js.map +1 -0
  219. package/dist/sync/wasSyncPort.d.ts +45 -0
  220. package/dist/sync/wasSyncPort.d.ts.map +1 -0
  221. package/dist/sync/wasSyncPort.js +170 -0
  222. package/dist/sync/wasSyncPort.js.map +1 -0
  223. package/package.json +142 -0
@@ -0,0 +1,27 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * App-session identity bootstrap: everything derivable from the master seed in
6
+ * one call. Thin composition over the pinned derivation in `agents.ts` (which
7
+ * is the shared-key contract: `CapabilityAgent.fromSeed` on raw bytes for the
8
+ * identity, HKDF `info = 'kak:v1:<collectionId>'` for each collection's KAK --
9
+ * never change either without a migration plan).
10
+ *
11
+ * The per-collection KAKs themselves are derived where they are consumed (the
12
+ * local store calls `deriveCollectionKeys` per collection); this module only
13
+ * surfaces the identity/zcap side the auth flow needs.
14
+ */
15
+ import { type IdentityAgents } from './agents.js';
16
+ /**
17
+ * Derives the app's identity agents (stable did:key controller, signer,
18
+ * ZcapClient) from the master seed.
19
+ *
20
+ * @param options {object}
21
+ * @param options.seed {Uint8Array} the 32-byte master seed
22
+ * @returns {Promise<IdentityAgents>}
23
+ */
24
+ export declare function initAppSession({ seed }: {
25
+ seed: Uint8Array;
26
+ }): Promise<IdentityAgents>;
27
+ //# sourceMappingURL=initAppSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initAppSession.d.ts","sourceRoot":"","sources":["../../src/identity/initAppSession.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;GAUG;AACH,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjE;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,EACnC,IAAI,EACL,EAAE;IACD,IAAI,EAAE,UAAU,CAAA;CACjB,GAAG,OAAO,CAAC,cAAc,CAAC,CAK1B"}
@@ -0,0 +1,30 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * App-session identity bootstrap: everything derivable from the master seed in
6
+ * one call. Thin composition over the pinned derivation in `agents.ts` (which
7
+ * is the shared-key contract: `CapabilityAgent.fromSeed` on raw bytes for the
8
+ * identity, HKDF `info = 'kak:v1:<collectionId>'` for each collection's KAK --
9
+ * never change either without a migration plan).
10
+ *
11
+ * The per-collection KAKs themselves are derived where they are consumed (the
12
+ * local store calls `deriveCollectionKeys` per collection); this module only
13
+ * surfaces the identity/zcap side the auth flow needs.
14
+ */
15
+ import { deriveIdentity } from './agents.js';
16
+ /**
17
+ * Derives the app's identity agents (stable did:key controller, signer,
18
+ * ZcapClient) from the master seed.
19
+ *
20
+ * @param options {object}
21
+ * @param options.seed {Uint8Array} the 32-byte master seed
22
+ * @returns {Promise<IdentityAgents>}
23
+ */
24
+ export async function initAppSession({ seed }) {
25
+ if (seed.length !== 32) {
26
+ throw new Error(`Master seed must be 32 bytes (got ${seed.length}).`);
27
+ }
28
+ return await deriveIdentity({ seed });
29
+ }
30
+ //# sourceMappingURL=initAppSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initAppSession.js","sourceRoot":"","sources":["../../src/identity/initAppSession.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;GAUG;AACH,OAAO,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAA;AAEjE;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EACnC,IAAI,EAGL;IACC,IAAI,IAAI,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;IACvE,CAAC;IACD,OAAO,MAAM,cAAc,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;AACvC,CAAC"}
@@ -0,0 +1,74 @@
1
+ import type { IVerifiableCredential, IVerifiablePresentation } from '@interop/data-integrity-core';
2
+ import type { DocumentLoader } from './documentLoader.js';
3
+ /**
4
+ * App-supplied naming for the seed credential: the VC `type` name and the URN
5
+ * vocabulary namespace its inline-context terms are minted under (e.g.
6
+ * `credentialType: 'MyAppKey'`, `vocabBase: 'urn:my-app:vocab#'`).
7
+ */
8
+ export interface SeedCredentialConfig {
9
+ credentialType: string;
10
+ vocabBase: string;
11
+ }
12
+ /** A parsed and structurally validated seed credential. */
13
+ export interface ParsedSeedCredential {
14
+ seed: Uint8Array;
15
+ controllerDid: string;
16
+ }
17
+ /** Encodes bytes as base64url (no padding), browser- and Node-safe. */
18
+ export declare function bytesToBase64url(bytes: Uint8Array): string;
19
+ /** Decodes base64url text back into bytes. */
20
+ export declare function base64urlToBytes(text: string): Uint8Array;
21
+ /**
22
+ * Self-issues the app-key credential for `seed`, signed Ed25519Signature2020 by
23
+ * the seed-derived signer.
24
+ *
25
+ * @param options {object}
26
+ * @param options.seed {Uint8Array} the 32-byte master seed
27
+ * @param options.origin {string} this app's web origin (anti-phishing bind)
28
+ * @param options.config {SeedCredentialConfig} credential type + vocab
29
+ * @param options.documentLoader {DocumentLoader}
30
+ * @returns {Promise<IVerifiableCredential>}
31
+ */
32
+ export declare function issueSeedCredential({ seed, origin, config, documentLoader }: {
33
+ seed: Uint8Array;
34
+ origin: string;
35
+ config: SeedCredentialConfig;
36
+ documentLoader: DocumentLoader;
37
+ }): Promise<IVerifiableCredential>;
38
+ /**
39
+ * Wraps a credential in a minimal (unsigned) VP for CHAPI `store()`. The
40
+ * credential's own proof self-authenticates; the offer envelope needs none.
41
+ */
42
+ export declare function wrapCredentialForStore(credential: IVerifiableCredential): IVerifiablePresentation;
43
+ /**
44
+ * Parses a seed credential and enforces the structural contract: type,
45
+ * self-issue (issuer === subject id), origin binding, a well-formed 32-byte
46
+ * seed, and -- the strongest check -- that the DID derived from the embedded
47
+ * seed IS the credential's subject/issuer DID. (The cryptographic proof on the
48
+ * credential is verified separately at the presentation level.)
49
+ *
50
+ * @param options {object}
51
+ * @param options.credential {IVerifiableCredential}
52
+ * @param options.origin {string} the expected app origin
53
+ * @param options.config {SeedCredentialConfig} credential type + vocab
54
+ * @returns {Promise<ParsedSeedCredential>}
55
+ */
56
+ export declare function parseSeedCredential({ credential, origin, config }: {
57
+ credential: IVerifiableCredential;
58
+ origin: string;
59
+ config: SeedCredentialConfig;
60
+ }): Promise<ParsedSeedCredential>;
61
+ /**
62
+ * Finds the seed credential inside a wallet response VP, or `null` when the
63
+ * wallet returned none (the first-run signal).
64
+ *
65
+ * @param options {object}
66
+ * @param options.presentation {IVerifiablePresentation}
67
+ * @param options.credentialType {string} the app's seed-credential type name
68
+ * @returns {IVerifiableCredential | null}
69
+ */
70
+ export declare function findSeedCredential({ presentation, credentialType }: {
71
+ presentation: IVerifiablePresentation;
72
+ credentialType: string;
73
+ }): IVerifiableCredential | null;
74
+ //# sourceMappingURL=seedCredential.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seedCredential.d.ts","sourceRoot":"","sources":["../../src/identity/seedCredential.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,8BAA8B,CAAA;AAErC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAIzD;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,UAAU,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;CACtB;AAmBD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAM1D;AAED,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CASzD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,IAAI,EACJ,MAAM,EACN,MAAM,EACN,cAAc,EACf,EAAE;IACD,IAAI,EAAE,UAAU,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,oBAAoB,CAAA;IAC5B,cAAc,EAAE,cAAc,CAAA;CAC/B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAsBjC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,qBAAqB,GAChC,uBAAuB,CAMzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,UAAU,EACV,MAAM,EACN,MAAM,EACP,EAAE;IACD,UAAU,EAAE,qBAAqB,CAAA;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,oBAAoB,CAAA;CAC7B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAyChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,YAAY,EACZ,cAAc,EACf,EAAE;IACD,YAAY,EAAE,uBAAuB,CAAA;IACrC,cAAc,EAAE,MAAM,CAAA;CACvB,GAAG,qBAAqB,GAAG,IAAI,CAY/B"}
@@ -0,0 +1,165 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The app-key credential: a self-issued VC holding the app's 32-byte master
6
+ * seed, stored in (and recovered from) the user's wallet. An inline-context
7
+ * pattern keeps it verifiable with no remote vocabulary fetch.
8
+ *
9
+ * The credential is self-issued: `issuer === credentialSubject.id`, and both
10
+ * equal the did:key controller DERIVED FROM THE EMBEDDED SEED -- so possession
11
+ * of the credential is possession of the identity, and a parsed credential can
12
+ * be re-checked against its own seed. `credentialSubject.origin` binds the
13
+ * credential to this app's web origin (the anti-phishing guard checked at
14
+ * login). The credential type name and vocabulary namespace are app-supplied
15
+ * (`SeedCredentialConfig`) so different apps hold sibling credential types on
16
+ * the same pattern.
17
+ */
18
+ import * as vc from '@interop/vc';
19
+ import { Ed25519Signature2020 } from '@interop/ed25519-signature';
20
+ import { deriveIdentity } from './agents.js';
21
+ const VC_1_CONTEXT_URL = 'https://www.w3.org/2018/credentials/v1';
22
+ /**
23
+ * Builds the inline context object binding the app's credential terms to its
24
+ * URN vocabulary namespace, so the credential stays verifiable with no remote
25
+ * vocabulary fetch.
26
+ */
27
+ function seedContext({ credentialType, vocabBase }) {
28
+ return {
29
+ '@protected': true,
30
+ [credentialType]: `${vocabBase}${credentialType}`,
31
+ seed: `${vocabBase}seed`,
32
+ origin: `${vocabBase}origin`
33
+ };
34
+ }
35
+ /** Encodes bytes as base64url (no padding), browser- and Node-safe. */
36
+ export function bytesToBase64url(bytes) {
37
+ let binary = '';
38
+ for (const byte of bytes) {
39
+ binary += String.fromCharCode(byte);
40
+ }
41
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
42
+ }
43
+ /** Decodes base64url text back into bytes. */
44
+ export function base64urlToBytes(text) {
45
+ const base64 = text.replace(/-/g, '+').replace(/_/g, '/');
46
+ const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
47
+ const binary = atob(padded);
48
+ const bytes = new Uint8Array(binary.length);
49
+ for (let i = 0; i < binary.length; i++) {
50
+ bytes[i] = binary.charCodeAt(i);
51
+ }
52
+ return bytes;
53
+ }
54
+ /**
55
+ * Self-issues the app-key credential for `seed`, signed Ed25519Signature2020 by
56
+ * the seed-derived signer.
57
+ *
58
+ * @param options {object}
59
+ * @param options.seed {Uint8Array} the 32-byte master seed
60
+ * @param options.origin {string} this app's web origin (anti-phishing bind)
61
+ * @param options.config {SeedCredentialConfig} credential type + vocab
62
+ * @param options.documentLoader {DocumentLoader}
63
+ * @returns {Promise<IVerifiableCredential>}
64
+ */
65
+ export async function issueSeedCredential({ seed, origin, config, documentLoader }) {
66
+ if (seed.length !== 32) {
67
+ throw new Error(`Master seed must be 32 bytes (got ${seed.length}).`);
68
+ }
69
+ const { controllerDid, keyAgent } = await deriveIdentity({ seed });
70
+ const credential = {
71
+ '@context': [VC_1_CONTEXT_URL, seedContext(config)],
72
+ id: `urn:uuid:${crypto.randomUUID()}`,
73
+ type: ['VerifiableCredential', config.credentialType],
74
+ issuer: controllerDid,
75
+ credentialSubject: {
76
+ id: controllerDid,
77
+ seed: bytesToBase64url(seed),
78
+ origin
79
+ }
80
+ };
81
+ const suite = new Ed25519Signature2020({ signer: keyAgent.getSigner() });
82
+ return (await vc.issue({
83
+ credential,
84
+ suite,
85
+ documentLoader
86
+ }));
87
+ }
88
+ /**
89
+ * Wraps a credential in a minimal (unsigned) VP for CHAPI `store()`. The
90
+ * credential's own proof self-authenticates; the offer envelope needs none.
91
+ */
92
+ export function wrapCredentialForStore(credential) {
93
+ return {
94
+ '@context': [VC_1_CONTEXT_URL],
95
+ type: ['VerifiablePresentation'],
96
+ verifiableCredential: [credential]
97
+ };
98
+ }
99
+ /**
100
+ * Parses a seed credential and enforces the structural contract: type,
101
+ * self-issue (issuer === subject id), origin binding, a well-formed 32-byte
102
+ * seed, and -- the strongest check -- that the DID derived from the embedded
103
+ * seed IS the credential's subject/issuer DID. (The cryptographic proof on the
104
+ * credential is verified separately at the presentation level.)
105
+ *
106
+ * @param options {object}
107
+ * @param options.credential {IVerifiableCredential}
108
+ * @param options.origin {string} the expected app origin
109
+ * @param options.config {SeedCredentialConfig} credential type + vocab
110
+ * @returns {Promise<ParsedSeedCredential>}
111
+ */
112
+ export async function parseSeedCredential({ credential, origin, config }) {
113
+ const { credentialType } = config;
114
+ const types = Array.isArray(credential.type)
115
+ ? credential.type
116
+ : [credential.type];
117
+ if (!types.includes(credentialType)) {
118
+ throw new Error(`Credential is not a ${credentialType} credential.`);
119
+ }
120
+ const issuer = typeof credential.issuer === 'string'
121
+ ? credential.issuer
122
+ : credential.issuer?.id;
123
+ const subject = credential.credentialSubject;
124
+ if (!issuer || !subject?.id || issuer !== subject.id) {
125
+ throw new Error(`${credentialType} credential is not self-issued.`);
126
+ }
127
+ if (subject.origin !== origin) {
128
+ throw new Error(`${credentialType} origin "${subject.origin ?? ''}" does not match this app's origin "${origin}".`);
129
+ }
130
+ if (typeof subject.seed !== 'string' || subject.seed.length === 0) {
131
+ throw new Error(`${credentialType} credential carries no seed.`);
132
+ }
133
+ const seed = base64urlToBytes(subject.seed);
134
+ if (seed.length !== 32) {
135
+ throw new Error(`${credentialType} seed must decode to 32 bytes (got ${seed.length}).`);
136
+ }
137
+ const { controllerDid } = await deriveIdentity({ seed });
138
+ if (controllerDid !== subject.id) {
139
+ throw new Error(`${credentialType} seed does not derive the credential subject DID.`);
140
+ }
141
+ return { seed, controllerDid };
142
+ }
143
+ /**
144
+ * Finds the seed credential inside a wallet response VP, or `null` when the
145
+ * wallet returned none (the first-run signal).
146
+ *
147
+ * @param options {object}
148
+ * @param options.presentation {IVerifiablePresentation}
149
+ * @param options.credentialType {string} the app's seed-credential type name
150
+ * @returns {IVerifiableCredential | null}
151
+ */
152
+ export function findSeedCredential({ presentation, credentialType }) {
153
+ const embedded = presentation
154
+ .verifiableCredential;
155
+ const list = Array.isArray(embedded) ? embedded : embedded ? [embedded] : [];
156
+ for (const entry of list) {
157
+ const types = entry.type;
158
+ const asArray = Array.isArray(types) ? types : [types];
159
+ if (asArray.includes(credentialType)) {
160
+ return entry;
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+ //# sourceMappingURL=seedCredential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seedCredential.js","sourceRoot":"","sources":["../../src/identity/seedCredential.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=seedCredential.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seedCredential.test.d.ts","sourceRoot":"","sources":["../../src/identity/seedCredential.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,205 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * Seed-credential tests: issue/parse round trip, the self-issue / origin /
6
+ * seed-to-DID structural contract, and cryptographic verifiability of the
7
+ * issued credential.
8
+ */
9
+ import { describe, expect, it } from 'vitest';
10
+ import { verifyCredential } from '@interop/verifier-core';
11
+ import { base64urlToBytes, bytesToBase64url, findSeedCredential, issueSeedCredential, parseSeedCredential, wrapCredentialForStore } from './seedCredential.js';
12
+ import { createDocumentLoader } from './documentLoader.js';
13
+ import { deriveIdentity } from './agents.js';
14
+ const ORIGIN = 'http://localhost:5173';
15
+ const CONFIG = {
16
+ credentialType: 'TestAppKey',
17
+ vocabBase: 'urn:test-app:vocab#'
18
+ };
19
+ const documentLoader = createDocumentLoader();
20
+ function randomSeed() {
21
+ return crypto.getRandomValues(new Uint8Array(32));
22
+ }
23
+ describe('base64url helpers', () => {
24
+ it('round-trips arbitrary bytes', () => {
25
+ const bytes = randomSeed();
26
+ expect(base64urlToBytes(bytesToBase64url(bytes))).toEqual(bytes);
27
+ });
28
+ it('produces no padding or unsafe characters', () => {
29
+ const encoded = bytesToBase64url(randomSeed());
30
+ expect(encoded).toMatch(/^[A-Za-z0-9_-]+$/);
31
+ });
32
+ });
33
+ describe('issueSeedCredential', () => {
34
+ it('issues a self-issued app key bound to the seed-derived DID', async () => {
35
+ const seed = randomSeed();
36
+ const { controllerDid } = await deriveIdentity({ seed });
37
+ const credential = await issueSeedCredential({
38
+ seed,
39
+ origin: ORIGIN,
40
+ config: CONFIG,
41
+ documentLoader
42
+ });
43
+ expect(credential.type).toContain(CONFIG.credentialType);
44
+ expect(credential.issuer).toBe(controllerDid);
45
+ const subject = credential.credentialSubject;
46
+ expect(subject.id).toBe(controllerDid);
47
+ expect(subject.origin).toBe(ORIGIN);
48
+ expect(base64urlToBytes(subject.seed)).toEqual(seed);
49
+ expect(credential.proof).toBeDefined();
50
+ });
51
+ it('issues a cryptographically verifiable credential', async () => {
52
+ const credential = await issueSeedCredential({
53
+ seed: randomSeed(),
54
+ origin: ORIGIN,
55
+ config: CONFIG,
56
+ documentLoader
57
+ });
58
+ const result = await verifyCredential({
59
+ credential,
60
+ registries: [],
61
+ documentLoader
62
+ });
63
+ expect(result.verified).toBe(true);
64
+ });
65
+ it('rejects a seed that is not 32 bytes', async () => {
66
+ await expect(issueSeedCredential({
67
+ seed: new Uint8Array(16),
68
+ origin: ORIGIN,
69
+ config: CONFIG,
70
+ documentLoader
71
+ })).rejects.toThrow(/32 bytes/);
72
+ });
73
+ });
74
+ describe('parseSeedCredential', () => {
75
+ it('round-trips the seed and controller DID', async () => {
76
+ const seed = randomSeed();
77
+ const { controllerDid } = await deriveIdentity({ seed });
78
+ const credential = await issueSeedCredential({
79
+ seed,
80
+ origin: ORIGIN,
81
+ config: CONFIG,
82
+ documentLoader
83
+ });
84
+ const parsed = await parseSeedCredential({
85
+ credential,
86
+ origin: ORIGIN,
87
+ config: CONFIG
88
+ });
89
+ expect(parsed.seed).toEqual(seed);
90
+ expect(parsed.controllerDid).toBe(controllerDid);
91
+ });
92
+ it('rejects an origin mismatch', async () => {
93
+ const credential = await issueSeedCredential({
94
+ seed: randomSeed(),
95
+ origin: 'https://evil.example',
96
+ config: CONFIG,
97
+ documentLoader
98
+ });
99
+ await expect(parseSeedCredential({ credential, origin: ORIGIN, config: CONFIG })).rejects.toThrow(/origin/);
100
+ });
101
+ it('rejects a non-self-issued credential', async () => {
102
+ const credential = await issueSeedCredential({
103
+ seed: randomSeed(),
104
+ origin: ORIGIN,
105
+ config: CONFIG,
106
+ documentLoader
107
+ });
108
+ const tampered = {
109
+ ...credential,
110
+ issuer: 'did:key:z6MkfDbczcXk3XiivKp9kJvBGnBcyhrbsmLAjLgyDJnYCyj4'
111
+ };
112
+ await expect(parseSeedCredential({
113
+ credential: tampered,
114
+ origin: ORIGIN,
115
+ config: CONFIG
116
+ })).rejects.toThrow(/self-issued/);
117
+ });
118
+ it('rejects a credential whose seed does not derive its subject DID', async () => {
119
+ const credential = await issueSeedCredential({
120
+ seed: randomSeed(),
121
+ origin: ORIGIN,
122
+ config: CONFIG,
123
+ documentLoader
124
+ });
125
+ const subject = credential.credentialSubject;
126
+ const tampered = {
127
+ ...credential,
128
+ credentialSubject: {
129
+ ...subject,
130
+ seed: bytesToBase64url(randomSeed())
131
+ }
132
+ };
133
+ await expect(parseSeedCredential({
134
+ credential: tampered,
135
+ origin: ORIGIN,
136
+ config: CONFIG
137
+ })).rejects.toThrow(/does not derive/);
138
+ });
139
+ it('rejects the wrong credential type', async () => {
140
+ const credential = {
141
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
142
+ type: ['VerifiableCredential'],
143
+ issuer: 'did:example:x',
144
+ credentialSubject: { id: 'did:example:x' }
145
+ };
146
+ await expect(parseSeedCredential({ credential, origin: ORIGIN, config: CONFIG })).rejects.toThrow(/not a TestAppKey/);
147
+ });
148
+ it('rejects a malformed seed', async () => {
149
+ const credential = await issueSeedCredential({
150
+ seed: randomSeed(),
151
+ origin: ORIGIN,
152
+ config: CONFIG,
153
+ documentLoader
154
+ });
155
+ const subject = credential.credentialSubject;
156
+ const tampered = {
157
+ ...credential,
158
+ credentialSubject: {
159
+ ...subject,
160
+ seed: bytesToBase64url(new Uint8Array(8))
161
+ }
162
+ };
163
+ await expect(parseSeedCredential({
164
+ credential: tampered,
165
+ origin: ORIGIN,
166
+ config: CONFIG
167
+ })).rejects.toThrow(/32 bytes/);
168
+ });
169
+ });
170
+ describe('findSeedCredential / wrapCredentialForStore', () => {
171
+ it('finds the app key inside a VP and ignores other credentials', async () => {
172
+ const credential = await issueSeedCredential({
173
+ seed: randomSeed(),
174
+ origin: ORIGIN,
175
+ config: CONFIG,
176
+ documentLoader
177
+ });
178
+ const other = {
179
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
180
+ type: ['VerifiableCredential'],
181
+ issuer: 'did:example:x',
182
+ credentialSubject: {}
183
+ };
184
+ const vp = wrapCredentialForStore(credential);
185
+ vp.verifiableCredential = [
186
+ other,
187
+ credential
188
+ ];
189
+ expect(findSeedCredential({
190
+ presentation: vp,
191
+ credentialType: CONFIG.credentialType
192
+ })).toBe(credential);
193
+ });
194
+ it('returns null when the VP carries no app key (first-run signal)', () => {
195
+ const vp = {
196
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
197
+ type: ['VerifiablePresentation']
198
+ };
199
+ expect(findSeedCredential({
200
+ presentation: vp,
201
+ credentialType: CONFIG.credentialType
202
+ })).toBeNull();
203
+ });
204
+ });
205
+ //# sourceMappingURL=seedCredential.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seedCredential.test.js","sourceRoot":"","sources":["../../src/identity/seedCredential.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /** The bound seed-store operations returned by `createSeedStore`. */
2
+ export interface SeedStore {
3
+ /** Persists the 32-byte master seed. */
4
+ saveSeed(seed: Uint8Array): Promise<void>;
5
+ /** Loads the persisted master seed, or `null`. */
6
+ loadSeed(): Promise<Uint8Array | null>;
7
+ /** Persists an opaque session record (see `appSession.ts`). */
8
+ saveRecord(record: unknown): Promise<void>;
9
+ /** Loads the persisted session record, or `null`. */
10
+ loadRecord(): Promise<unknown | null>;
11
+ /** Wipes the seed and the session record (logout). */
12
+ clearSeedStore(): Promise<void>;
13
+ }
14
+ /**
15
+ * Creates a seed store bound to `dbName` and `idb`.
16
+ *
17
+ * @param options {object}
18
+ * @param options.dbName {string} the IndexedDB database name
19
+ * @param [options.idb] {IDBFactory} the IndexedDB factory (defaults to the
20
+ * global `indexedDB`; inject a fake for tests)
21
+ * @returns {SeedStore}
22
+ */
23
+ export declare function createSeedStore({ dbName, idb }: {
24
+ dbName: string;
25
+ idb?: IDBFactory;
26
+ }): SeedStore;
27
+ //# sourceMappingURL=seedStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seedStore.d.ts","sourceRoot":"","sources":["../../src/identity/seedStore.ts"],"names":[],"mappings":"AAiBA,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC,kDAAkD;IAClD,QAAQ,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IACtC,+DAA+D;IAC/D,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,qDAAqD;IACrD,UAAU,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IACrC,sDAAsD;IACtD,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAChC;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,GAAe,EAChB,EAAE;IACD,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,UAAU,CAAA;CACjB,GAAG,SAAS,CA2DZ"}
@@ -0,0 +1,74 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * Seed persistence: the master seed at rest in the app's own IndexedDB, so a
6
+ * reload restores the session with zero wallet popups. A raw-IndexedDB pattern
7
+ * (one db, one object store, fixed record keys, `db.close()` after every
8
+ * operation). Wiped on logout.
9
+ *
10
+ * `createSeedStore` binds a database name (each app supplies its own) and an
11
+ * optional `idb` factory (injectable for tests, e.g. fake-indexeddb), returning
12
+ * the five bound operations.
13
+ */
14
+ const SESSION_STORE = 'session';
15
+ const SEED_RECORD = 'seed';
16
+ const SESSION_RECORD = 'record';
17
+ /**
18
+ * Creates a seed store bound to `dbName` and `idb`.
19
+ *
20
+ * @param options {object}
21
+ * @param options.dbName {string} the IndexedDB database name
22
+ * @param [options.idb] {IDBFactory} the IndexedDB factory (defaults to the
23
+ * global `indexedDB`; inject a fake for tests)
24
+ * @returns {SeedStore}
25
+ */
26
+ export function createSeedStore({ dbName, idb = indexedDB }) {
27
+ async function openSessionDb() {
28
+ return await new Promise((resolve, reject) => {
29
+ const request = idb.open(dbName, 1);
30
+ request.onupgradeneeded = () => {
31
+ request.result.createObjectStore(SESSION_STORE);
32
+ };
33
+ request.onsuccess = () => resolve(request.result);
34
+ request.onerror = () => reject(request.error ?? new Error('IndexedDB open failed.'));
35
+ });
36
+ }
37
+ async function withSessionStore(mode, operation) {
38
+ const db = await openSessionDb();
39
+ try {
40
+ return await new Promise((resolve, reject) => {
41
+ const transaction = db.transaction(SESSION_STORE, mode);
42
+ const request = operation(transaction.objectStore(SESSION_STORE));
43
+ request.onsuccess = () => resolve(request.result);
44
+ request.onerror = () => reject(request.error ?? new Error('IndexedDB operation failed.'));
45
+ });
46
+ }
47
+ finally {
48
+ db.close();
49
+ }
50
+ }
51
+ return {
52
+ async saveSeed(seed) {
53
+ await withSessionStore('readwrite', store => store.put(seed, SEED_RECORD));
54
+ },
55
+ async loadSeed() {
56
+ const stored = await withSessionStore('readonly', store => store.get(SEED_RECORD));
57
+ return stored instanceof Uint8Array && stored.length === 32
58
+ ? stored
59
+ : null;
60
+ },
61
+ async saveRecord(record) {
62
+ await withSessionStore('readwrite', store => store.put(record, SESSION_RECORD));
63
+ },
64
+ async loadRecord() {
65
+ const stored = await withSessionStore('readonly', store => store.get(SESSION_RECORD));
66
+ return stored ?? null;
67
+ },
68
+ async clearSeedStore() {
69
+ await withSessionStore('readwrite', store => store.delete(SEED_RECORD));
70
+ await withSessionStore('readwrite', store => store.delete(SESSION_RECORD));
71
+ }
72
+ };
73
+ }
74
+ //# sourceMappingURL=seedStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seedStore.js","sourceRoot":"","sources":["../../src/identity/seedStore.ts"],"names":[],"mappings":"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"}