@learncard/lca-api-service 1.3.2 → 1.5.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 (240) hide show
  1. package/.env.example +99 -2
  2. package/AGENTS.md +155 -44
  3. package/CHANGELOG.md +84 -0
  4. package/bootstrapLambda.test.ts +76 -0
  5. package/compose.env +5 -0
  6. package/config/config.dev.json +23 -0
  7. package/config/config.example.json +22 -0
  8. package/config/config.json +1 -0
  9. package/config/config.production.json +12 -0
  10. package/config/config.scouts.dev.json +11 -0
  11. package/config/config.scouts.production.json +11 -0
  12. package/dist/accesslayer/signing-authority/index.d.ts +8 -2
  13. package/dist/app.d.ts +361 -8
  14. package/dist/cache/challenges.d.ts +2 -0
  15. package/dist/cache/getDel.d.ts +2 -0
  16. package/dist/cache/getDel.test.d.ts +1 -0
  17. package/dist/cache/login-tickets.d.ts +12 -0
  18. package/dist/cache/recoverySessions.d.ts +42 -0
  19. package/dist/config/applyDockerStageConfig.d.ts +1 -0
  20. package/dist/config/cacheEnvironment.d.ts +16 -0
  21. package/dist/config/environment.d.ts +253 -2
  22. package/dist/config/environment.test.d.ts +1 -0
  23. package/dist/config/escrowBundleMode.test.d.ts +1 -0
  24. package/dist/config/escrowDeployment.test.d.ts +1 -0
  25. package/dist/config/oidcEnvironment.d.ts +74 -0
  26. package/dist/config/serverlessFunctionEnv.test.d.ts +1 -0
  27. package/dist/config/stageConfig.d.ts +62 -0
  28. package/dist/config/stageConfig.test.d.ts +1 -0
  29. package/dist/didkit_wasm_bg.wasm +0 -0
  30. package/dist/docker-entry.d.ts +1 -1
  31. package/dist/helpers/auth.helpers.d.ts +1 -1
  32. package/dist/helpers/escrowCancelLink.d.ts +7 -0
  33. package/dist/helpers/escrowCancelLink.test.d.ts +1 -0
  34. package/dist/helpers/escrowCancelToken.d.ts +4 -0
  35. package/dist/helpers/escrowCancelToken.test.d.ts +1 -0
  36. package/dist/helpers/keycloak.helpers.d.ts +23 -0
  37. package/dist/helpers/learnCard.signingAuthority.test.d.ts +1 -0
  38. package/dist/helpers/login-ticket.helpers.d.ts +31 -0
  39. package/dist/helpers/login-ticket.helpers.test.d.ts +1 -0
  40. package/dist/helpers/oidc.helpers.d.ts +47 -0
  41. package/dist/helpers/rate-limit.helpers.d.ts +6 -0
  42. package/dist/helpers/rate-limit.helpers.test.d.ts +1 -0
  43. package/dist/helpers/rateLimit.helpers.d.ts +4 -0
  44. package/dist/helpers/seedEncryption.helpers.d.ts +23 -0
  45. package/dist/helpers/seedEncryption.helpers.test.d.ts +1 -0
  46. package/dist/helpers/social-token.helpers.d.ts +19 -0
  47. package/dist/index.d.ts +1 -0
  48. package/dist/index.js +2 -2
  49. package/dist/jobs/escrowBlobRewrap.d.ts +9 -0
  50. package/dist/jobs/escrowHoldReminders.d.ts +12 -0
  51. package/dist/migrations/signingAuthoritySeeds.d.ts +13 -0
  52. package/dist/migrations/signingAuthoritySeedsCli.test.d.ts +1 -0
  53. package/dist/models/AuthSubject.d.ts +20 -0
  54. package/dist/models/EscrowHold.d.ts +153 -0
  55. package/dist/models/Notification.d.ts +5 -0
  56. package/dist/models/SigningAuthority.d.ts +7 -1
  57. package/dist/models/UserKey.d.ts +326 -3
  58. package/dist/models/UserKey.escrowPin.test.d.ts +1 -0
  59. package/dist/models/index.d.ts +2 -0
  60. package/dist/oidc.d.ts +5 -0
  61. package/dist/routes/auth.d.ts +31 -0
  62. package/dist/routes/escrow.d.ts +164 -0
  63. package/dist/routes/index.d.ts +6 -0
  64. package/dist/routes/keys.d.ts +182 -7
  65. package/dist/routes/notifications.d.ts +5 -0
  66. package/dist/routes/qr-login.d.ts +1 -1
  67. package/dist/routes/signing-authority.d.ts +14 -0
  68. package/dist/services/escrow-enclave/blobIdentity.d.ts +10 -0
  69. package/dist/services/escrow-enclave/blobIdentity.test.d.ts +1 -0
  70. package/dist/services/escrow-enclave/cancellation.d.ts +3 -0
  71. package/dist/services/escrow-enclave/enclaveTrustAnchors.d.ts +1 -0
  72. package/dist/services/escrow-enclave/index.d.ts +25 -0
  73. package/dist/services/escrow-enclave/notifications.d.ts +24 -0
  74. package/dist/services/escrow-enclave/notifications.test.d.ts +1 -0
  75. package/dist/services/escrow-enclave/production.test.d.ts +1 -0
  76. package/dist/services/escrow-enclave/remoteEnclave.contract.test.d.ts +1 -0
  77. package/dist/services/escrow-enclave/remoteEnclave.d.ts +20 -0
  78. package/dist/services/escrow-enclave/remoteEnclave.test.d.ts +1 -0
  79. package/dist/services/escrow-enclave/remoteEnclave.tls.test.d.ts +1 -0
  80. package/dist/services/escrow-enclave/softwareEnclave.d.ts +27 -0
  81. package/dist/services/escrow-enclave/softwareEnclave.test.d.ts +1 -0
  82. package/dist/services/escrow-enclave/types.d.ts +111 -0
  83. package/dist/types/notifications.d.ts +5 -0
  84. package/dist/types/seed-encryption.d.ts +31 -0
  85. package/dist/types/seed-migration.d.ts +49 -0
  86. package/esbuildPlugins.cjs +1 -0
  87. package/lambda.ts +27 -112
  88. package/lambdaApp.ts +146 -0
  89. package/oidcLambda.test.ts +38 -0
  90. package/oidcLambda.ts +18 -0
  91. package/oidcLambdaApp.ts +11 -0
  92. package/package.json +30 -14
  93. package/scripts/README.md +88 -0
  94. package/scripts/check-seed-encryption-infrastructure.mjs +139 -0
  95. package/scripts/check-signing-authority-redis.mjs +93 -0
  96. package/scripts/escrow-blob-mode-report.test.ts +227 -0
  97. package/scripts/escrow-blob-mode-report.ts +465 -0
  98. package/scripts/keycloak-admin.ts +108 -0
  99. package/scripts/keycloak-provider-links.ts +147 -0
  100. package/scripts/migrate-signing-authority-seeds-local.sh +51 -0
  101. package/scripts/migrate-signing-authority-seeds.mjs +105 -0
  102. package/scripts/print-seed-rollout-flags.ts +21 -0
  103. package/scripts/provision-keycloak-users.ts +419 -0
  104. package/seedMigrationApp.test.ts +145 -0
  105. package/seedMigrationApp.ts +50 -0
  106. package/seedMigrationLambda.test.ts +40 -0
  107. package/seedMigrationLambda.ts +19 -0
  108. package/serverless-local.yml +28 -1
  109. package/serverless.function-env.cjs +58 -0
  110. package/serverless.yml +294 -26
  111. package/src/accesslayer/notifications/index.ts +9 -2
  112. package/src/accesslayer/signing-authority/create.ts +11 -7
  113. package/src/accesslayer/signing-authority/index.ts +4 -1
  114. package/src/app.ts +4 -0
  115. package/src/cache/challenges.ts +12 -2
  116. package/src/cache/getDel.test.ts +34 -0
  117. package/src/cache/getDel.ts +32 -0
  118. package/src/cache/in-memory-lru.ts +1 -0
  119. package/src/cache/index.ts +1 -1
  120. package/src/cache/login-tickets.ts +26 -0
  121. package/src/cache/recoverySessions.ts +252 -0
  122. package/src/config/applyDockerStageConfig.ts +6 -0
  123. package/src/config/cacheEnvironment.ts +34 -0
  124. package/src/config/environment.test.ts +159 -0
  125. package/src/config/environment.ts +164 -0
  126. package/src/config/escrowBundleMode.test.ts +52 -0
  127. package/src/config/escrowDeployment.test.ts +114 -0
  128. package/src/config/oidcEnvironment.ts +59 -0
  129. package/src/config/serverlessFunctionEnv.test.ts +98 -0
  130. package/src/config/stageConfig.test.ts +26 -0
  131. package/src/config/stageConfig.ts +24 -0
  132. package/src/docker-entry.ts +13 -0
  133. package/src/helpers/auth.helpers.ts +68 -30
  134. package/src/helpers/escrowCancelLink.test.ts +60 -0
  135. package/src/helpers/escrowCancelLink.ts +38 -0
  136. package/src/helpers/escrowCancelToken.test.ts +105 -0
  137. package/src/helpers/escrowCancelToken.ts +47 -0
  138. package/src/helpers/keycloak.helpers.ts +120 -0
  139. package/src/helpers/learnCard.helpers.test.ts +11 -1
  140. package/src/helpers/learnCard.helpers.ts +57 -45
  141. package/src/helpers/learnCard.signingAuthority.test.ts +194 -0
  142. package/src/helpers/login-ticket.helpers.test.ts +45 -0
  143. package/src/helpers/login-ticket.helpers.ts +91 -0
  144. package/src/helpers/oidc.helpers.ts +354 -0
  145. package/src/helpers/rate-limit.helpers.test.ts +61 -0
  146. package/src/helpers/rate-limit.helpers.ts +73 -0
  147. package/src/helpers/rateLimit.helpers.ts +112 -0
  148. package/src/helpers/seedEncryption.helpers.test.ts +427 -0
  149. package/src/helpers/seedEncryption.helpers.ts +372 -0
  150. package/src/helpers/signingAuthority.helpers.ts +4 -1
  151. package/src/helpers/social-token.helpers.ts +144 -0
  152. package/src/index.ts +1 -0
  153. package/src/jobs/escrowBlobRewrap.ts +121 -0
  154. package/src/jobs/escrowHoldReminders.ts +177 -0
  155. package/src/migrations/signingAuthoritySeeds.ts +430 -0
  156. package/src/migrations/signingAuthoritySeedsCli.test.ts +70 -0
  157. package/src/models/AuthSubject.ts +147 -0
  158. package/src/models/EscrowHold.ts +541 -0
  159. package/src/models/SigningAuthority.ts +14 -3
  160. package/src/models/UserKey.escrowPin.test.ts +49 -0
  161. package/src/models/UserKey.ts +1109 -40
  162. package/src/models/index.ts +2 -0
  163. package/src/models/pruneOrphanedRecoveryMethods.ts +3 -1
  164. package/src/oidc.ts +371 -0
  165. package/src/routes/auth.ts +173 -0
  166. package/src/routes/credentials.ts +8 -0
  167. package/src/routes/escrow.ts +1073 -0
  168. package/src/routes/firebase.ts +90 -48
  169. package/src/routes/index.ts +98 -5
  170. package/src/routes/keys.ts +1022 -174
  171. package/src/routes/qr-login.ts +51 -53
  172. package/src/routes/signing-authority.ts +39 -1
  173. package/src/services/delivery/adapters/postmark.adapter.ts +48 -1
  174. package/src/services/escrow-enclave/README.md +122 -0
  175. package/src/services/escrow-enclave/__fixtures__/tls/pinned-ca.pem +11 -0
  176. package/src/services/escrow-enclave/__fixtures__/tls/pinned-leaf.key +5 -0
  177. package/src/services/escrow-enclave/__fixtures__/tls/pinned-leaf.pem +12 -0
  178. package/src/services/escrow-enclave/__fixtures__/tls/rogue-ca.pem +11 -0
  179. package/src/services/escrow-enclave/__fixtures__/tls/rogue-leaf.key +5 -0
  180. package/src/services/escrow-enclave/__fixtures__/tls/rogue-leaf.pem +12 -0
  181. package/src/services/escrow-enclave/blobIdentity.test.ts +42 -0
  182. package/src/services/escrow-enclave/blobIdentity.ts +55 -0
  183. package/src/services/escrow-enclave/cancellation.ts +59 -0
  184. package/src/services/escrow-enclave/enclaveTrustAnchors.ts +34 -0
  185. package/src/services/escrow-enclave/index.ts +174 -0
  186. package/src/services/escrow-enclave/notifications.test.ts +377 -0
  187. package/src/services/escrow-enclave/notifications.ts +348 -0
  188. package/src/services/escrow-enclave/production.test.ts +34 -0
  189. package/src/services/escrow-enclave/remoteEnclave.contract.test.ts +344 -0
  190. package/src/services/escrow-enclave/remoteEnclave.test.ts +490 -0
  191. package/src/services/escrow-enclave/remoteEnclave.tls.test.ts +62 -0
  192. package/src/services/escrow-enclave/remoteEnclave.ts +263 -0
  193. package/src/services/escrow-enclave/softwareEnclave.test.ts +450 -0
  194. package/src/services/escrow-enclave/softwareEnclave.ts +254 -0
  195. package/src/services/escrow-enclave/types.ts +122 -0
  196. package/src/types/seed-encryption.ts +51 -0
  197. package/src/types/seed-migration.ts +61 -0
  198. package/test/auth-tickets.spec.ts +480 -0
  199. package/test/credentials.spec.ts +2 -0
  200. package/test/helpers/keycloak-broker.ts +181 -0
  201. package/test/helpers/live-mongo.ts +9 -0
  202. package/test/jobs/escrowBlobRewrap.spec.ts +262 -0
  203. package/test/jobs/escrowHoldReminders.spec.ts +319 -0
  204. package/test/keycloak-admin.spec.ts +58 -0
  205. package/test/keycloak-broker-roundtrip.integration.spec.ts +40 -0
  206. package/test/keycloak-migration.integration.spec.ts +266 -0
  207. package/test/keycloak-provider-links.spec.ts +201 -0
  208. package/test/keycloak-social-broker.integration.spec.ts +228 -0
  209. package/test/keycloak-verify.integration.spec.ts +100 -0
  210. package/test/keycloak-verify.spec.ts +307 -0
  211. package/test/keys-escrow.spec.ts +2328 -0
  212. package/test/keys-identity-rebind.spec.ts +456 -0
  213. package/test/keys-lifecycle-invariants.spec.ts +785 -0
  214. package/test/keys-recovery-confirmation.spec.ts +586 -0
  215. package/test/keys-recovery-rate-limit.spec.ts +123 -0
  216. package/test/keys-secret-leakage.spec.ts +265 -0
  217. package/test/models/authSubjectIndexes.spec.ts +132 -0
  218. package/test/models/escrow.spec.ts +572 -0
  219. package/test/notifications.spec.ts +4 -0
  220. package/test/oidc-e2e/broker.spec.ts +116 -0
  221. package/test/oidc-e2e/compose.yaml +33 -0
  222. package/test/oidc-e2e/fixtures.ts +245 -0
  223. package/test/oidc-e2e/lock.test.mjs +73 -0
  224. package/test/oidc-e2e/playwright.config.ts +21 -0
  225. package/test/oidc-e2e/run.mjs +312 -0
  226. package/test/oidc-e2e/tsconfig.json +10 -0
  227. package/test/oidc-e2e/types.ts +21 -0
  228. package/test/oidc.integration.spec.ts +54 -0
  229. package/test/oidc.spec.ts +697 -0
  230. package/test/oidcLambdaApp.boot.spec.ts +105 -0
  231. package/test/provision-keycloak-users.spec.ts +299 -0
  232. package/test/signing-authority-http.spec.ts +161 -0
  233. package/test/signing-authority-seed-migration.spec.ts +414 -0
  234. package/test/signing-authority.spec.ts +90 -0
  235. package/tsconfig.build.json +1 -1
  236. package/tsconfig.json +1 -0
  237. package/vitest.config.ts +24 -1
  238. package/vitest.integration.config.ts +25 -3
  239. package/dist/cache/signing-authority.d.ts +0 -6
  240. package/src/cache/signing-authority.ts +0 -37
@@ -1,9 +1,126 @@
1
+ import { z } from 'zod';
2
+ import { AuthProviderType } from '@helpers/auth.helpers';
3
+ import { type ContactMethod, type AuthProviderMapping, type MongoUserKeyType } from '@models';
4
+ export declare const AuthProviderTypeValidator: z.ZodEnum<{
5
+ firebase: "firebase";
6
+ supertokens: "supertokens";
7
+ keycloak: "keycloak";
8
+ oidc: "oidc";
9
+ }>;
10
+ export declare const AuthInputValidator: z.ZodObject<{
11
+ authToken: z.ZodString;
12
+ providerType: z.ZodEnum<{
13
+ firebase: "firebase";
14
+ supertokens: "supertokens";
15
+ keycloak: "keycloak";
16
+ oidc: "oidc";
17
+ }>;
18
+ }, z.core.$strip>;
19
+ export declare const RecoverySessionTokenValidator: z.ZodString;
20
+ export declare function verifyAndGetContactMethod(input: {
21
+ authToken: string;
22
+ providerType: AuthProviderType;
23
+ }): Promise<{
24
+ user: {
25
+ id: string;
26
+ email?: string;
27
+ phone?: string;
28
+ providerType: string;
29
+ };
30
+ contactMethod: ContactMethod;
31
+ authProvider: AuthProviderMapping;
32
+ }>;
33
+ export declare const requireUserKey: (authProvider: AuthProviderMapping, message?: string) => Promise<MongoUserKeyType>;
34
+ export declare const assertDidOwner: (userKey: MongoUserKeyType, authenticatedDid: string) => void;
35
+ export declare const requireRecoverySession: (token: string, scope: "recover" | "rebind") => Promise<AuthProviderMapping>;
1
36
  export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
2
37
  ctx: import("@routes").Context;
3
38
  meta: import("trpc-to-openapi").OpenApiMeta;
4
39
  errorShape: import("@trpc/server").TRPCDefaultErrorShape;
5
40
  transformer: false;
6
41
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
42
+ startRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
43
+ input: {
44
+ email: string;
45
+ };
46
+ output: {
47
+ success: true;
48
+ };
49
+ meta: import("trpc-to-openapi").OpenApiMeta;
50
+ }>;
51
+ verifyRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
52
+ input: {
53
+ email: string;
54
+ code: string;
55
+ };
56
+ output: {
57
+ recoverySessionToken: string;
58
+ recoveryMethods: {
59
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
60
+ createdAt: string;
61
+ credentialId?: string | undefined;
62
+ shareVersion?: number | undefined;
63
+ }[];
64
+ escrowPin: {
65
+ state: "enabled" | "none" | "locked" | "stale";
66
+ enabled: boolean;
67
+ attemptsRemaining: number;
68
+ salt?: string | undefined;
69
+ };
70
+ };
71
+ meta: import("trpc-to-openapi").OpenApiMeta;
72
+ }>;
73
+ useRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
74
+ input: {
75
+ recoverySessionToken: string;
76
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
77
+ credentialId?: string | undefined;
78
+ };
79
+ output: {
80
+ authShare: {
81
+ encryptedData: string;
82
+ encryptedDek: string;
83
+ iv: string;
84
+ };
85
+ primaryDid: string;
86
+ shareVersion: number;
87
+ rebindSessionToken: string;
88
+ encryptedShare?: {
89
+ encryptedData: string;
90
+ iv: string;
91
+ salt?: string | undefined;
92
+ } | undefined;
93
+ };
94
+ meta: import("trpc-to-openapi").OpenApiMeta;
95
+ }>;
96
+ completeRecoveryRebind: import("@trpc/server").TRPCMutationProcedure<{
97
+ input: {
98
+ recoverySessionToken: string;
99
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
100
+ primaryDid: string;
101
+ authShare: {
102
+ encryptedData: string;
103
+ encryptedDek: string;
104
+ iv: string;
105
+ };
106
+ newAuthToken?: string | undefined;
107
+ };
108
+ output: {
109
+ success: true;
110
+ shareVersion: number;
111
+ recoveryMethodsRequireConfirmation: ("email" | "passkey" | "backup" | "phrase" | "escrow")[];
112
+ };
113
+ meta: import("trpc-to-openapi").OpenApiMeta;
114
+ }>;
115
+ issueDidChallenge: import("@trpc/server").TRPCMutationProcedure<{
116
+ input: {
117
+ did: string;
118
+ };
119
+ output: {
120
+ challenge: string;
121
+ };
122
+ meta: import("trpc-to-openapi").OpenApiMeta;
123
+ }>;
7
124
  getAuthShare: import("@trpc/server").TRPCMutationProcedure<{
8
125
  input: {
9
126
  authToken: string;
@@ -19,14 +136,24 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
19
136
  primaryDid: string | null;
20
137
  securityLevel: "basic" | "enhanced" | "advanced";
21
138
  recoveryMethods: {
22
- type: "email" | "passkey" | "backup" | "phrase";
139
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
23
140
  createdAt: string;
24
141
  credentialId?: string | undefined;
25
142
  shareVersion?: number | undefined;
143
+ confirmedAt?: string | undefined;
26
144
  }[];
27
145
  keyProvider: "web3auth" | "sss";
28
146
  shareVersion: number;
29
147
  maskedRecoveryEmail: string | null;
148
+ escrowOptedOut: boolean;
149
+ escrowPin: {
150
+ state: "enabled" | "none" | "locked" | "stale";
151
+ enabled: boolean;
152
+ attemptsRemaining: number;
153
+ salt?: string | undefined;
154
+ };
155
+ sssActivationState: "provisional" | "active";
156
+ escrowStale?: "mode-mismatch" | "key-rotated" | undefined;
30
157
  } | null;
31
158
  meta: import("trpc-to-openapi").OpenApiMeta;
32
159
  }>;
@@ -42,10 +169,13 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
42
169
  primaryDid: string;
43
170
  securityLevel?: "basic" | "enhanced" | "advanced" | undefined;
44
171
  keyProvider?: "web3auth" | "sss" | undefined;
172
+ sssActivationState?: "provisional" | undefined;
173
+ expectedShareVersion?: number | undefined;
45
174
  };
46
175
  output: {
47
176
  success: boolean;
48
177
  shareVersion: number;
178
+ expectedShareVersionChecked: boolean;
49
179
  };
50
180
  meta: import("trpc-to-openapi").OpenApiMeta;
51
181
  }>;
@@ -53,7 +183,7 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
53
183
  input: {
54
184
  authToken: string;
55
185
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
56
- type: "email" | "passkey" | "backup" | "phrase";
186
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
57
187
  encryptedShare?: {
58
188
  encryptedData: string;
59
189
  iv: string;
@@ -67,11 +197,36 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
67
197
  };
68
198
  meta: import("trpc-to-openapi").OpenApiMeta;
69
199
  }>;
70
- getRecoveryShare: import("@trpc/server").TRPCQueryProcedure<{
200
+ confirmRecoveryMethod: import("@trpc/server").TRPCMutationProcedure<{
71
201
  input: {
72
202
  authToken: string;
73
203
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
74
- type: "email" | "passkey" | "backup" | "phrase";
204
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
205
+ credentialId?: string | undefined;
206
+ code?: string | undefined;
207
+ };
208
+ output: {
209
+ success: boolean;
210
+ };
211
+ meta: import("trpc-to-openapi").OpenApiMeta;
212
+ }>;
213
+ deleteRecoveryMethod: import("@trpc/server").TRPCMutationProcedure<{
214
+ input: {
215
+ authToken: string;
216
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
217
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
218
+ credentialId?: string | undefined;
219
+ };
220
+ output: {
221
+ success: boolean;
222
+ };
223
+ meta: import("trpc-to-openapi").OpenApiMeta;
224
+ }>;
225
+ getRecoveryShare: import("@trpc/server").TRPCQueryProcedure<{
226
+ input: {
227
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
228
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
229
+ authToken?: string | undefined;
75
230
  credentialId?: string | undefined;
76
231
  };
77
232
  output: {
@@ -94,6 +249,16 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
94
249
  };
95
250
  meta: import("trpc-to-openapi").OpenApiMeta;
96
251
  }>;
252
+ activate: import("@trpc/server").TRPCMutationProcedure<{
253
+ input: {
254
+ authToken: string;
255
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
256
+ };
257
+ output: {
258
+ success: boolean;
259
+ };
260
+ meta: import("trpc-to-openapi").OpenApiMeta;
261
+ }>;
97
262
  addRecoveryEmail: import("@trpc/server").TRPCMutationProcedure<{
98
263
  input: {
99
264
  authToken: string;
@@ -131,9 +296,18 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
131
296
  input: {
132
297
  authToken: string;
133
298
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
134
- emailShare: string;
135
- email?: string | undefined;
136
- useRecoveryEmail?: boolean | undefined;
299
+ relayPayload: {
300
+ version: 1;
301
+ algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
302
+ keyId: string;
303
+ ephemeralPublicKey: string;
304
+ salt: string;
305
+ iv: string;
306
+ ciphertext: string;
307
+ };
308
+ confirmationCode: string;
309
+ email: string;
310
+ shareVersion?: number | undefined;
137
311
  };
138
312
  output: {
139
313
  success: boolean;
@@ -159,6 +333,7 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
159
333
  input: {
160
334
  authToken: string;
161
335
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
336
+ challenge: string;
162
337
  };
163
338
  output: {
164
339
  success: boolean;
@@ -207,6 +207,11 @@ export declare const notificationsRouter: import("@trpc/server").TRPCBuiltRouter
207
207
  approvedAt: string;
208
208
  contractUpdatedAt: string;
209
209
  } | undefined;
210
+ referral?: {
211
+ requestId: string;
212
+ requestedBy: string;
213
+ externalReferenceId?: string | undefined;
214
+ } | undefined;
210
215
  uris?: string[] | undefined;
211
216
  } | undefined;
212
217
  inbox?: {
@@ -22,7 +22,7 @@ export declare const qrLoginRouter: import("@trpc/server").TRPCBuiltRouter<{
22
22
  output: {
23
23
  sessionId: string;
24
24
  publicKey: string;
25
- status: "approved" | "pending";
25
+ status: "pending" | "approved";
26
26
  encryptedPayload?: string | undefined;
27
27
  approverDid?: string | undefined;
28
28
  };
@@ -18,6 +18,20 @@ export declare const signingAuthorityRouter: import("@trpc/server").TRPCBuiltRou
18
18
  };
19
19
  meta: import("trpc-to-openapi").OpenApiMeta;
20
20
  }>;
21
+ ensureManagedSigningAuthority: import("@trpc/server").TRPCMutationProcedure<{
22
+ input: {
23
+ name: string;
24
+ ownerDid: string;
25
+ };
26
+ output: {
27
+ ownerDid: string;
28
+ name: string;
29
+ did: string;
30
+ endpoint: string;
31
+ _id?: string | undefined;
32
+ };
33
+ meta: import("trpc-to-openapi").OpenApiMeta;
34
+ }>;
21
35
  signingAuthorities: import("@trpc/server").TRPCQueryProcedure<{
22
36
  input: void;
23
37
  output: {
@@ -0,0 +1,10 @@
1
+ export interface EscrowEnvelopeFields {
2
+ version: number;
3
+ algorithm: string;
4
+ keyId: string;
5
+ ephemeralPublicKey: string;
6
+ salt: string;
7
+ iv: string;
8
+ ciphertext: string;
9
+ }
10
+ export declare const escrowBlobIdentity: (envelope: EscrowEnvelopeFields) => string;
@@ -0,0 +1,3 @@
1
+ import { type EscrowHold, type MongoUserKeyType } from '@models';
2
+ export declare const logEscrowCancellationPending: (urgent: boolean) => void;
3
+ export declare const cancelHoldInEnclave: (hold: EscrowHold, userKey?: MongoUserKeyType | null, now?: Date) => Promise<boolean>;
@@ -0,0 +1 @@
1
+ export declare const pinnedEnclaveCa: (baseUrl: string) => string | undefined;
@@ -0,0 +1,25 @@
1
+ import type { EscrowBlobStaleReason } from '@learncard/types';
2
+ import { type EscrowEnclave } from './types';
3
+ export * from './types';
4
+ export * from './softwareEnclave';
5
+ export * from './remoteEnclave';
6
+ export * from './notifications';
7
+ export declare const isEscrowEnabled: () => boolean;
8
+ export declare const isEscrowRemoteMode: () => boolean;
9
+ export declare const activeEscrowClientMode: () => "software" | "nitro";
10
+ export declare const getEscrowHoldDurationMs: () => number;
11
+ export declare const getEscrowHoldRestartMinAgeMs: () => number;
12
+ export declare const getEscrowEnclave: () => EscrowEnclave;
13
+ export type { EscrowBlobStaleReason };
14
+ interface EscrowAttestationIdentity {
15
+ keyId: string;
16
+ previousKeyIds: string[];
17
+ mode: 'software' | 'nitro';
18
+ }
19
+ export declare const getEnclaveAttestationIdentity: () => Promise<EscrowAttestationIdentity>;
20
+ export declare const getEscrowBlobStaleReason: (blob: {
21
+ enclaveMode: "software" | "nitro";
22
+ enclaveKeyId: string;
23
+ }) => Promise<EscrowBlobStaleReason | undefined>;
24
+ export declare const __setEscrowEnclaveForTests: (replacement: EscrowEnclave | undefined) => void;
25
+ export declare const __resetEscrowAttestationCacheForTests: () => void;
@@ -0,0 +1,24 @@
1
+ import type { ResolvedTenant } from '@learncard/email-templates';
2
+ import type { EscrowHold, MongoUserKeyType } from '@models';
3
+ import { type DeliveryService } from '../delivery';
4
+ export type EscrowHoldEventKind = 'started' | 'reminder' | 'completed' | 'cancelled' | 'pin-locked';
5
+ export interface EscrowHoldEvent {
6
+ kind: EscrowHoldEventKind;
7
+ hold: EscrowHold;
8
+ userKey: MongoUserKeyType;
9
+ reason?: 'superseded';
10
+ cancelToken?: string;
11
+ tenant?: ResolvedTenant;
12
+ activeHold?: EscrowHold;
13
+ }
14
+ type SendPushNotification = typeof import('@helpers/pushNotifications.helpers').sendPushNotification;
15
+ export interface EscrowHoldNotifierDeps {
16
+ send: DeliveryService['send'];
17
+ sendPush: SendPushNotification;
18
+ now: () => Date;
19
+ rotateCancelToken: (holdId: string) => Promise<string | null>;
20
+ recordNotification: (holdId: string, kind: EscrowHoldEventKind) => Promise<void>;
21
+ }
22
+ export declare const ESCROW_NOTIFICATION_TIMEOUT_MS = 3000;
23
+ export declare const notifyEscrowHoldEvent: (event: EscrowHoldEvent, overrides?: Partial<EscrowHoldNotifierDeps>) => Promise<boolean>;
24
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { type EscrowEnclave } from './types';
2
+ export interface RemoteEnclaveTransportResponse {
3
+ status: number;
4
+ data: unknown;
5
+ }
6
+ export interface RemoteEnclaveTransport {
7
+ post(url: string, body: unknown, config: {
8
+ headers: Record<string, string>;
9
+ timeout: number;
10
+ }): Promise<RemoteEnclaveTransportResponse>;
11
+ }
12
+ export declare const createHttpTransport: (caPem?: string) => RemoteEnclaveTransport;
13
+ export interface RemoteEnclaveConfig {
14
+ baseUrl: string;
15
+ token: string;
16
+ timeoutMs: number;
17
+ caPem?: string;
18
+ transport?: RemoteEnclaveTransport;
19
+ }
20
+ export declare const createRemoteEnclave: (config: RemoteEnclaveConfig) => EscrowEnclave;
@@ -0,0 +1,27 @@
1
+ import type { EnclaveCreateHoldInput, EscrowHoldRecord, CancelHoldRequest } from './types';
2
+ import { type EscrowEnclave, type EnclaveAttestation, type VerifyEscrowBlobInput, type VerifyEscrowBlobResult, type ReleaseRequest, type ReleaseResult, type CarryPinVerifierInput, type RewrapEscrowBlobInput } from './types';
3
+ export interface SoftwareEnclaveConfig {
4
+ privateKeys: Record<string, string>;
5
+ activeKeyId: string;
6
+ holdDurationMs?: number;
7
+ }
8
+ export declare class SoftwareEnclave implements EscrowEnclave {
9
+ private readonly privateKeys;
10
+ private readonly activeKeyId;
11
+ private readonly holdDurationMs;
12
+ constructor(config: SoftwareEnclaveConfig);
13
+ createHold(input: EnclaveCreateHoldInput): Promise<{
14
+ holdRecord: EscrowHoldRecord;
15
+ }>;
16
+ cancelHold(input: CancelHoldRequest): Promise<void>;
17
+ getAttestation(_nonce?: Uint8Array): Promise<EnclaveAttestation>;
18
+ private decrypt;
19
+ verifyEscrowBlob(input: VerifyEscrowBlobInput): Promise<VerifyEscrowBlobResult>;
20
+ carryPinVerifier(input: CarryPinVerifierInput): Promise<{
21
+ envelope: import("@learncard/sss-key-manager").EscrowEnvelope;
22
+ }>;
23
+ rewrapEscrowBlob(input: RewrapEscrowBlobInput): Promise<{
24
+ envelope: import("@learncard/sss-key-manager").EscrowEnvelope;
25
+ }>;
26
+ releaseEscrow(input: ReleaseRequest): Promise<ReleaseResult>;
27
+ }
@@ -0,0 +1,111 @@
1
+ import type { EscrowEnvelope } from '@learncard/sss-key-manager';
2
+ export interface EnclaveAttestation {
3
+ mode: 'software' | 'nitro';
4
+ keyId: string;
5
+ previousKeyIds?: string[];
6
+ publicKey: string;
7
+ measurements: {
8
+ imageSha384?: string;
9
+ pcr0?: string;
10
+ pcr1?: string;
11
+ pcr2?: string;
12
+ };
13
+ document: string;
14
+ issuedAt: string;
15
+ }
16
+ export interface EscrowHoldRecord {
17
+ [key: string]: unknown;
18
+ hold: {
19
+ [key: string]: unknown;
20
+ holdId: string;
21
+ did: string;
22
+ shareVersion: number;
23
+ blobHash: string;
24
+ enrollmentEpoch: number;
25
+ releasePolicy: 'hold' | 'pin';
26
+ clientEphemeralPublicKey: string;
27
+ createdLo: number;
28
+ createdHi: number;
29
+ policyVersion: number;
30
+ signature: string;
31
+ };
32
+ holdDurationMs: number;
33
+ ledgerSeq: number;
34
+ }
35
+ export interface EnclaveCreateHoldInput {
36
+ envelope: EscrowEnvelope;
37
+ holdId: string;
38
+ expectedDid: string;
39
+ expectedShareVersion: number;
40
+ enrollmentEpoch: number;
41
+ releasePolicy: 'hold' | 'pin';
42
+ clientEphemeralPublicKey: string;
43
+ }
44
+ export interface CancelHoldRequest {
45
+ envelope: EscrowEnvelope;
46
+ hold: EscrowHoldRecord;
47
+ clientEphemeralPublicKey: string;
48
+ expectedDid: string;
49
+ }
50
+ export interface VerifyEscrowBlobInput {
51
+ envelope: EscrowEnvelope;
52
+ expectedDid: string;
53
+ expectedShareVersion: number;
54
+ }
55
+ export type VerifyEscrowBlobResult = {
56
+ ok: true;
57
+ hasPin: boolean;
58
+ } | {
59
+ ok: false;
60
+ hasPin: boolean;
61
+ reason: string;
62
+ };
63
+ export interface ReleaseRequest extends CancelHoldRequest {
64
+ now?: Date;
65
+ pinProof?: string;
66
+ }
67
+ export interface ReleaseResult {
68
+ sealed: EscrowEnvelope;
69
+ }
70
+ export interface CarryPinVerifierInput {
71
+ sourceEnvelope: EscrowEnvelope;
72
+ targetEnvelope: EscrowEnvelope;
73
+ expectedDid: string;
74
+ sourceShareVersion: number;
75
+ targetShareVersion: number;
76
+ sourceEnrollmentEpoch: number;
77
+ targetEnrollmentEpoch: number;
78
+ }
79
+ export interface RewrapEscrowBlobInput {
80
+ envelope: EscrowEnvelope;
81
+ expectedDid: string;
82
+ expectedShareVersion: number;
83
+ sourceEnrollmentEpoch: number;
84
+ }
85
+ export interface EscrowEnclave {
86
+ getAttestation(nonce?: Uint8Array): Promise<EnclaveAttestation>;
87
+ verifyEscrowBlob(input: VerifyEscrowBlobInput): Promise<VerifyEscrowBlobResult>;
88
+ carryPinVerifier(input: CarryPinVerifierInput): Promise<{
89
+ envelope: EscrowEnvelope;
90
+ }>;
91
+ rewrapEscrowBlob(input: RewrapEscrowBlobInput): Promise<{
92
+ envelope: EscrowEnvelope;
93
+ }>;
94
+ releaseEscrow(input: ReleaseRequest): Promise<ReleaseResult>;
95
+ createHold(input: EnclaveCreateHoldInput): Promise<{
96
+ holdRecord: EscrowHoldRecord;
97
+ }>;
98
+ cancelHold(input: CancelHoldRequest): Promise<void>;
99
+ }
100
+ export declare class EscrowPolicyError extends Error {
101
+ constructor();
102
+ }
103
+ export declare class EscrowPinMismatchError extends Error {
104
+ constructor();
105
+ }
106
+ export declare class EscrowBlobError extends Error {
107
+ constructor();
108
+ }
109
+ export declare class EscrowUnavailableError extends Error {
110
+ constructor();
111
+ }
@@ -237,6 +237,11 @@ export declare const PaginatedNotificationsValidator: z.ZodObject<{
237
237
  approvedAt: z.ZodString;
238
238
  contractUpdatedAt: z.ZodString;
239
239
  }, z.core.$strip>>;
240
+ referral: z.ZodOptional<z.ZodObject<{
241
+ requestId: z.ZodString;
242
+ requestedBy: z.ZodString;
243
+ externalReferenceId: z.ZodOptional<z.ZodString>;
244
+ }, z.core.$strip>>;
240
245
  id: z.ZodString;
241
246
  action: z.ZodEnum<{
242
247
  write: "write";
@@ -0,0 +1,31 @@
1
+ export interface SigningAuthoritySeedIdentity {
2
+ _id: string;
3
+ ownerDid: string;
4
+ name: string;
5
+ did?: string;
6
+ }
7
+ export interface EncryptedSigningAuthoritySeed {
8
+ encryptedSeed: string;
9
+ encryptedDek: string;
10
+ keyVersion: 'kms-v1' | 'local-v1';
11
+ }
12
+ export interface SigningAuthoritySeedRecord {
13
+ _id?: unknown;
14
+ ownerDid: string;
15
+ name: string;
16
+ did?: string;
17
+ seed?: string;
18
+ encryptedSeed?: unknown;
19
+ encryptedDek?: unknown;
20
+ keyVersion?: unknown;
21
+ }
22
+ export interface SeedEncryptionConfig {
23
+ kmsKeyArn?: string;
24
+ localKek?: string;
25
+ allowLocal: boolean;
26
+ }
27
+ export interface SeedEncryption {
28
+ encrypt: (seed: string, identity: SigningAuthoritySeedIdentity) => Promise<EncryptedSigningAuthoritySeed>;
29
+ decrypt: (envelope: EncryptedSigningAuthoritySeed, identity: SigningAuthoritySeedIdentity) => Promise<string>;
30
+ }
31
+ export type SeedEncryptionFailure = 'invalid_record' | 'invalid_envelope' | 'configuration' | 'legacy_disabled' | 'authentication_failed' | 'kms_access_denied' | 'kms_unavailable' | 'kms_failed';
@@ -0,0 +1,49 @@
1
+ import type { Document, ObjectId } from 'mongodb';
2
+ import type { EncryptedSigningAuthoritySeed } from './seed-encryption';
3
+ export type SeedMigrationPhase = 'dry-run' | 'prepare' | 'verify' | 'purge';
4
+ export interface SeedMigrationRequest {
5
+ phase: SeedMigrationPhase;
6
+ batchSize?: number;
7
+ }
8
+ export interface SeedMigrationCounts {
9
+ total: number;
10
+ encrypted: number;
11
+ legacyOnly: number;
12
+ malformed: number;
13
+ plaintextRemaining: number;
14
+ }
15
+ export interface SeedMigrationResult {
16
+ phase: SeedMigrationPhase;
17
+ done: boolean;
18
+ processed: number;
19
+ counts: SeedMigrationCounts;
20
+ countsReconciled: boolean;
21
+ rescanRequired?: boolean;
22
+ }
23
+ export interface MigrationSigningAuthority extends Document {
24
+ _id: string | ObjectId;
25
+ ownerDid: string;
26
+ name: string;
27
+ did?: string;
28
+ seed?: string;
29
+ }
30
+ export interface SeedMigrationState {
31
+ _id: string;
32
+ leaseOwner?: string;
33
+ leaseExpiresAt?: Date;
34
+ phase?: SeedMigrationPhase;
35
+ epoch?: string;
36
+ verifiedEpoch?: string;
37
+ status?: 'running' | 'complete' | 'failed';
38
+ processed?: number;
39
+ counts?: SeedMigrationCounts;
40
+ updatedAt?: Date;
41
+ cursor?: string | ObjectId;
42
+ }
43
+ export interface SeedMigrationReceipt extends EncryptedSigningAuthoritySeed {
44
+ _id: string | ObjectId;
45
+ epoch: string;
46
+ ownerDid: string;
47
+ name: string;
48
+ did: string | null;
49
+ }
@@ -41,6 +41,7 @@ module.exports = [
41
41
  forceCjsSodiumWrapperPlugin,
42
42
  copyDidkitWasmPlugin,
43
43
  sentryEsbuildPlugin({
44
+ telemetry: process.env.SENTRY_BUILD_TELEMETRY !== 'false',
44
45
  authToken: process.env.SENTRY_AUTH_TOKEN,
45
46
  org: process.env.SENTRY_ORG,
46
47
  project: process.env.SENTRY_PROJECT,