@learncard/lca-api-service 1.3.1 → 1.4.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 (99) hide show
  1. package/.env.example +26 -2
  2. package/AGENTS.md +59 -44
  3. package/CHANGELOG.md +57 -0
  4. package/compose.env +5 -0
  5. package/dist/accesslayer/signing-authority/index.d.ts +8 -2
  6. package/dist/app.d.ts +297 -8
  7. package/dist/cache/challenges.d.ts +2 -0
  8. package/dist/cache/recoverySessions.d.ts +42 -0
  9. package/dist/config/environment.d.ts +152 -2
  10. package/dist/config/environment.test.d.ts +1 -0
  11. package/dist/helpers/learnCard.signingAuthority.test.d.ts +1 -0
  12. package/dist/helpers/rateLimit.helpers.d.ts +4 -0
  13. package/dist/helpers/seedEncryption.helpers.d.ts +23 -0
  14. package/dist/helpers/seedEncryption.helpers.test.d.ts +1 -0
  15. package/dist/index.js +2 -2
  16. package/dist/migrations/signingAuthoritySeeds.d.ts +13 -0
  17. package/dist/migrations/signingAuthoritySeedsCli.test.d.ts +1 -0
  18. package/dist/models/EscrowHold.d.ts +75 -0
  19. package/dist/models/SigningAuthority.d.ts +7 -1
  20. package/dist/models/UserKey.d.ts +316 -3
  21. package/dist/models/UserKey.escrowPin.test.d.ts +1 -0
  22. package/dist/models/index.d.ts +1 -0
  23. package/dist/routes/escrow.d.ts +150 -0
  24. package/dist/routes/firebase.test.d.ts +1 -0
  25. package/dist/routes/index.d.ts +6 -0
  26. package/dist/routes/keys.d.ts +181 -7
  27. package/dist/routes/qr-login.d.ts +1 -1
  28. package/dist/services/escrow-enclave/index.d.ts +9 -0
  29. package/dist/services/escrow-enclave/notifications.d.ts +9 -0
  30. package/dist/services/escrow-enclave/softwareEnclave.d.ts +17 -0
  31. package/dist/services/escrow-enclave/softwareEnclave.test.d.ts +1 -0
  32. package/dist/services/escrow-enclave/types.d.ts +67 -0
  33. package/dist/types/seed-encryption.d.ts +31 -0
  34. package/dist/types/seed-migration.d.ts +49 -0
  35. package/lambda.ts +8 -3
  36. package/package.json +17 -11
  37. package/scripts/check-seed-encryption-infrastructure.mjs +123 -0
  38. package/scripts/check-signing-authority-redis.mjs +93 -0
  39. package/scripts/migrate-signing-authority-seeds-local.sh +51 -0
  40. package/scripts/migrate-signing-authority-seeds.mjs +105 -0
  41. package/seedMigrationLambda.test.ts +145 -0
  42. package/seedMigrationLambda.ts +50 -0
  43. package/serverless-local.yml +3 -0
  44. package/serverless.escrow-env.cjs +20 -0
  45. package/serverless.yml +133 -0
  46. package/src/accesslayer/signing-authority/create.ts +11 -7
  47. package/src/app.ts +2 -0
  48. package/src/cache/challenges.ts +12 -2
  49. package/src/cache/in-memory-lru.ts +1 -0
  50. package/src/cache/recoverySessions.ts +252 -0
  51. package/src/config/environment.test.ts +116 -0
  52. package/src/config/environment.ts +97 -0
  53. package/src/docker-entry.ts +4 -0
  54. package/src/helpers/learnCard.helpers.test.ts +11 -1
  55. package/src/helpers/learnCard.helpers.ts +57 -45
  56. package/src/helpers/learnCard.signingAuthority.test.ts +194 -0
  57. package/src/helpers/rateLimit.helpers.ts +112 -0
  58. package/src/helpers/seedEncryption.helpers.test.ts +427 -0
  59. package/src/helpers/seedEncryption.helpers.ts +372 -0
  60. package/src/helpers/signingAuthority.helpers.ts +4 -1
  61. package/src/migrations/signingAuthoritySeeds.ts +430 -0
  62. package/src/migrations/signingAuthoritySeedsCli.test.ts +70 -0
  63. package/src/models/EscrowHold.ts +225 -0
  64. package/src/models/SigningAuthority.ts +14 -3
  65. package/src/models/UserKey.escrowPin.test.ts +49 -0
  66. package/src/models/UserKey.ts +1024 -40
  67. package/src/models/index.ts +1 -0
  68. package/src/models/pruneOrphanedRecoveryMethods.ts +3 -1
  69. package/src/routes/credentials.ts +8 -0
  70. package/src/routes/escrow.ts +826 -0
  71. package/src/routes/firebase.test.ts +94 -0
  72. package/src/routes/firebase.ts +99 -56
  73. package/src/routes/index.ts +98 -5
  74. package/src/routes/keys.ts +1017 -174
  75. package/src/routes/qr-login.ts +51 -53
  76. package/src/services/delivery/adapters/postmark.adapter.ts +5 -0
  77. package/src/services/escrow-enclave/README.md +51 -0
  78. package/src/services/escrow-enclave/index.ts +36 -0
  79. package/src/services/escrow-enclave/notifications.ts +14 -0
  80. package/src/services/escrow-enclave/softwareEnclave.test.ts +249 -0
  81. package/src/services/escrow-enclave/softwareEnclave.ts +168 -0
  82. package/src/services/escrow-enclave/types.ts +76 -0
  83. package/src/types/seed-encryption.ts +51 -0
  84. package/src/types/seed-migration.ts +61 -0
  85. package/test/credentials.spec.ts +2 -0
  86. package/test/keys-escrow.spec.ts +1389 -0
  87. package/test/keys-identity-rebind.spec.ts +456 -0
  88. package/test/keys-lifecycle-invariants.spec.ts +785 -0
  89. package/test/keys-recovery-confirmation.spec.ts +586 -0
  90. package/test/keys-recovery-rate-limit.spec.ts +123 -0
  91. package/test/keys-secret-leakage.spec.ts +265 -0
  92. package/test/models/escrow.spec.ts +210 -0
  93. package/test/signing-authority-http.spec.ts +161 -0
  94. package/test/signing-authority-seed-migration.spec.ts +414 -0
  95. package/test/signing-authority.spec.ts +40 -0
  96. package/vitest.config.ts +1 -1
  97. package/vitest.integration.config.ts +5 -0
  98. package/dist/cache/signing-authority.d.ts +0 -6
  99. package/src/cache/signing-authority.ts +0 -37
package/dist/app.d.ts CHANGED
@@ -994,6 +994,88 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
994
994
  errorShape: import("@trpc/server").TRPCDefaultErrorShape;
995
995
  transformer: false;
996
996
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
997
+ startRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
998
+ input: {
999
+ email: string;
1000
+ };
1001
+ output: {
1002
+ success: true;
1003
+ };
1004
+ meta: import("trpc-to-openapi").OpenApiMeta;
1005
+ }>;
1006
+ verifyRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
1007
+ input: {
1008
+ email: string;
1009
+ code: string;
1010
+ };
1011
+ output: {
1012
+ recoverySessionToken: string;
1013
+ recoveryMethods: {
1014
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
1015
+ createdAt: string;
1016
+ credentialId?: string | undefined;
1017
+ shareVersion?: number | undefined;
1018
+ }[];
1019
+ escrowPin: {
1020
+ state: "enabled" | "none" | "locked" | "stale";
1021
+ enabled: boolean;
1022
+ attemptsRemaining: number;
1023
+ salt?: string | undefined;
1024
+ };
1025
+ };
1026
+ meta: import("trpc-to-openapi").OpenApiMeta;
1027
+ }>;
1028
+ useRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
1029
+ input: {
1030
+ recoverySessionToken: string;
1031
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
1032
+ credentialId?: string | undefined;
1033
+ };
1034
+ output: {
1035
+ authShare: {
1036
+ encryptedData: string;
1037
+ encryptedDek: string;
1038
+ iv: string;
1039
+ };
1040
+ primaryDid: string;
1041
+ shareVersion: number;
1042
+ rebindSessionToken: string;
1043
+ encryptedShare?: {
1044
+ encryptedData: string;
1045
+ iv: string;
1046
+ salt?: string | undefined;
1047
+ } | undefined;
1048
+ };
1049
+ meta: import("trpc-to-openapi").OpenApiMeta;
1050
+ }>;
1051
+ completeRecoveryRebind: import("@trpc/server").TRPCMutationProcedure<{
1052
+ input: {
1053
+ recoverySessionToken: string;
1054
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1055
+ primaryDid: string;
1056
+ authShare: {
1057
+ encryptedData: string;
1058
+ encryptedDek: string;
1059
+ iv: string;
1060
+ };
1061
+ newAuthToken?: string | undefined;
1062
+ };
1063
+ output: {
1064
+ success: true;
1065
+ shareVersion: number;
1066
+ recoveryMethodsRequireConfirmation: ("email" | "passkey" | "backup" | "phrase" | "escrow")[];
1067
+ };
1068
+ meta: import("trpc-to-openapi").OpenApiMeta;
1069
+ }>;
1070
+ issueDidChallenge: import("@trpc/server").TRPCMutationProcedure<{
1071
+ input: {
1072
+ did: string;
1073
+ };
1074
+ output: {
1075
+ challenge: string;
1076
+ };
1077
+ meta: import("trpc-to-openapi").OpenApiMeta;
1078
+ }>;
997
1079
  getAuthShare: import("@trpc/server").TRPCMutationProcedure<{
998
1080
  input: {
999
1081
  authToken: string;
@@ -1009,14 +1091,23 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1009
1091
  primaryDid: string | null;
1010
1092
  securityLevel: "basic" | "enhanced" | "advanced";
1011
1093
  recoveryMethods: {
1012
- type: "email" | "passkey" | "backup" | "phrase";
1094
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
1013
1095
  createdAt: string;
1014
1096
  credentialId?: string | undefined;
1015
1097
  shareVersion?: number | undefined;
1098
+ confirmedAt?: string | undefined;
1016
1099
  }[];
1017
1100
  keyProvider: "web3auth" | "sss";
1018
1101
  shareVersion: number;
1019
1102
  maskedRecoveryEmail: string | null;
1103
+ escrowOptedOut: boolean;
1104
+ escrowPin: {
1105
+ state: "enabled" | "none" | "locked" | "stale";
1106
+ enabled: boolean;
1107
+ attemptsRemaining: number;
1108
+ salt?: string | undefined;
1109
+ };
1110
+ sssActivationState: "provisional" | "active";
1020
1111
  } | null;
1021
1112
  meta: import("trpc-to-openapi").OpenApiMeta;
1022
1113
  }>;
@@ -1032,10 +1123,13 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1032
1123
  primaryDid: string;
1033
1124
  securityLevel?: "basic" | "enhanced" | "advanced" | undefined;
1034
1125
  keyProvider?: "web3auth" | "sss" | undefined;
1126
+ sssActivationState?: "provisional" | undefined;
1127
+ expectedShareVersion?: number | undefined;
1035
1128
  };
1036
1129
  output: {
1037
1130
  success: boolean;
1038
1131
  shareVersion: number;
1132
+ expectedShareVersionChecked: boolean;
1039
1133
  };
1040
1134
  meta: import("trpc-to-openapi").OpenApiMeta;
1041
1135
  }>;
@@ -1043,7 +1137,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1043
1137
  input: {
1044
1138
  authToken: string;
1045
1139
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1046
- type: "email" | "passkey" | "backup" | "phrase";
1140
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
1047
1141
  encryptedShare?: {
1048
1142
  encryptedData: string;
1049
1143
  iv: string;
@@ -1057,11 +1151,36 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1057
1151
  };
1058
1152
  meta: import("trpc-to-openapi").OpenApiMeta;
1059
1153
  }>;
1060
- getRecoveryShare: import("@trpc/server").TRPCQueryProcedure<{
1154
+ confirmRecoveryMethod: import("@trpc/server").TRPCMutationProcedure<{
1061
1155
  input: {
1062
1156
  authToken: string;
1063
1157
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1064
- type: "email" | "passkey" | "backup" | "phrase";
1158
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
1159
+ credentialId?: string | undefined;
1160
+ code?: string | undefined;
1161
+ };
1162
+ output: {
1163
+ success: boolean;
1164
+ };
1165
+ meta: import("trpc-to-openapi").OpenApiMeta;
1166
+ }>;
1167
+ deleteRecoveryMethod: import("@trpc/server").TRPCMutationProcedure<{
1168
+ input: {
1169
+ authToken: string;
1170
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1171
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
1172
+ credentialId?: string | undefined;
1173
+ };
1174
+ output: {
1175
+ success: boolean;
1176
+ };
1177
+ meta: import("trpc-to-openapi").OpenApiMeta;
1178
+ }>;
1179
+ getRecoveryShare: import("@trpc/server").TRPCQueryProcedure<{
1180
+ input: {
1181
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1182
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
1183
+ authToken?: string | undefined;
1065
1184
  credentialId?: string | undefined;
1066
1185
  };
1067
1186
  output: {
@@ -1084,6 +1203,16 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1084
1203
  };
1085
1204
  meta: import("trpc-to-openapi").OpenApiMeta;
1086
1205
  }>;
1206
+ activate: import("@trpc/server").TRPCMutationProcedure<{
1207
+ input: {
1208
+ authToken: string;
1209
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1210
+ };
1211
+ output: {
1212
+ success: boolean;
1213
+ };
1214
+ meta: import("trpc-to-openapi").OpenApiMeta;
1215
+ }>;
1087
1216
  addRecoveryEmail: import("@trpc/server").TRPCMutationProcedure<{
1088
1217
  input: {
1089
1218
  authToken: string;
@@ -1121,9 +1250,18 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1121
1250
  input: {
1122
1251
  authToken: string;
1123
1252
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1124
- emailShare: string;
1125
- email?: string | undefined;
1126
- useRecoveryEmail?: boolean | undefined;
1253
+ relayPayload: {
1254
+ version: 1;
1255
+ algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
1256
+ keyId: string;
1257
+ ephemeralPublicKey: string;
1258
+ salt: string;
1259
+ iv: string;
1260
+ ciphertext: string;
1261
+ };
1262
+ confirmationCode: string;
1263
+ email: string;
1264
+ shareVersion?: number | undefined;
1127
1265
  };
1128
1266
  output: {
1129
1267
  success: boolean;
@@ -1149,6 +1287,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1149
1287
  input: {
1150
1288
  authToken: string;
1151
1289
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1290
+ challenge: string;
1152
1291
  };
1153
1292
  output: {
1154
1293
  success: boolean;
@@ -1156,6 +1295,156 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1156
1295
  meta: import("trpc-to-openapi").OpenApiMeta;
1157
1296
  }>;
1158
1297
  }>>;
1298
+ escrow: import("@trpc/server").TRPCBuiltRouter<{
1299
+ ctx: import("@routes").Context;
1300
+ meta: import("trpc-to-openapi").OpenApiMeta;
1301
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape;
1302
+ transformer: false;
1303
+ }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
1304
+ getAttestation: import("@trpc/server").TRPCQueryProcedure<{
1305
+ input: Record<string, never>;
1306
+ output: {
1307
+ attestation: {
1308
+ mode: "software" | "nitro";
1309
+ keyId: string;
1310
+ publicKey: string;
1311
+ measurements: {
1312
+ imageSha384?: string | undefined;
1313
+ pcr0?: string | undefined;
1314
+ pcr1?: string | undefined;
1315
+ pcr2?: string | undefined;
1316
+ };
1317
+ document: string;
1318
+ issuedAt: string;
1319
+ };
1320
+ holdDurationMs: number;
1321
+ };
1322
+ meta: import("trpc-to-openapi").OpenApiMeta;
1323
+ }>;
1324
+ enroll: import("@trpc/server").TRPCMutationProcedure<{
1325
+ input: {
1326
+ authToken: string;
1327
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1328
+ envelope: {
1329
+ version: 1;
1330
+ algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
1331
+ keyId: string;
1332
+ ephemeralPublicKey: string;
1333
+ salt: string;
1334
+ iv: string;
1335
+ ciphertext: string;
1336
+ };
1337
+ shareVersion: number;
1338
+ enclaveKeyId: string;
1339
+ pinSalt?: string | undefined;
1340
+ clearPin?: boolean | undefined;
1341
+ };
1342
+ output: {
1343
+ success: true;
1344
+ shareVersion: number;
1345
+ };
1346
+ meta: import("trpc-to-openapi").OpenApiMeta;
1347
+ }>;
1348
+ remove: import("@trpc/server").TRPCMutationProcedure<{
1349
+ input: {
1350
+ authToken: string;
1351
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1352
+ optOut: boolean;
1353
+ };
1354
+ output: {
1355
+ success: true;
1356
+ };
1357
+ meta: import("trpc-to-openapi").OpenApiMeta;
1358
+ }>;
1359
+ optIn: import("@trpc/server").TRPCMutationProcedure<{
1360
+ input: {
1361
+ authToken: string;
1362
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1363
+ };
1364
+ output: {
1365
+ success: true;
1366
+ };
1367
+ meta: import("trpc-to-openapi").OpenApiMeta;
1368
+ }>;
1369
+ startRecovery: import("@trpc/server").TRPCMutationProcedure<{
1370
+ input: {
1371
+ clientEphemeralPublicKey: string;
1372
+ releasePolicy?: "pin" | "hold" | undefined;
1373
+ restart?: boolean | undefined;
1374
+ authToken?: string | undefined;
1375
+ providerType?: "firebase" | "supertokens" | "keycloak" | "oidc" | undefined;
1376
+ recoverySessionToken?: string | undefined;
1377
+ };
1378
+ output: {
1379
+ requestedAt: string;
1380
+ releaseAfter: string;
1381
+ releasePolicy: "pin" | "hold";
1382
+ holdId: string;
1383
+ status: "pending";
1384
+ resumeToken: string | null;
1385
+ pinSalt?: string | undefined;
1386
+ };
1387
+ meta: import("trpc-to-openapi").OpenApiMeta;
1388
+ }>;
1389
+ getStatus: import("@trpc/server").TRPCQueryProcedure<{
1390
+ input: {
1391
+ holdId?: string | undefined;
1392
+ resumeToken?: string | undefined;
1393
+ authToken?: string | undefined;
1394
+ providerType?: "firebase" | "supertokens" | "keycloak" | "oidc" | undefined;
1395
+ };
1396
+ output: {
1397
+ hold: {
1398
+ holdId: string;
1399
+ status: "pending" | "cancelled" | "completed" | "expired";
1400
+ requestedAt: string;
1401
+ releaseAfter: string;
1402
+ releasePolicy: "pin" | "hold";
1403
+ cancelledAt?: string | undefined;
1404
+ completedAt?: string | undefined;
1405
+ } | null;
1406
+ };
1407
+ meta: import("trpc-to-openapi").OpenApiMeta;
1408
+ }>;
1409
+ cancelRecovery: import("@trpc/server").TRPCMutationProcedure<{
1410
+ input: {
1411
+ authToken: string;
1412
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
1413
+ };
1414
+ output: {
1415
+ success: true;
1416
+ cancelled: boolean;
1417
+ };
1418
+ meta: import("trpc-to-openapi").OpenApiMeta;
1419
+ }>;
1420
+ completeRecovery: import("@trpc/server").TRPCMutationProcedure<{
1421
+ input: {
1422
+ holdId: string;
1423
+ resumeToken: string;
1424
+ pinProof?: string | undefined;
1425
+ };
1426
+ output: {
1427
+ sealedShare: {
1428
+ version: 1;
1429
+ algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
1430
+ keyId: string;
1431
+ ephemeralPublicKey: string;
1432
+ salt: string;
1433
+ iv: string;
1434
+ ciphertext: string;
1435
+ };
1436
+ authShare: {
1437
+ encryptedData: string;
1438
+ encryptedDek: string;
1439
+ iv: string;
1440
+ };
1441
+ primaryDid: string;
1442
+ shareVersion: number;
1443
+ rebindSessionToken: string;
1444
+ };
1445
+ meta: import("trpc-to-openapi").OpenApiMeta;
1446
+ }>;
1447
+ }>>;
1159
1448
  qrLogin: import("@trpc/server").TRPCBuiltRouter<{
1160
1449
  ctx: import("@routes").Context;
1161
1450
  meta: import("trpc-to-openapi").OpenApiMeta;
@@ -1180,7 +1469,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1180
1469
  output: {
1181
1470
  sessionId: string;
1182
1471
  publicKey: string;
1183
- status: "approved" | "pending";
1472
+ status: "pending" | "approved";
1184
1473
  encryptedPayload?: string | undefined;
1185
1474
  approverDid?: string | undefined;
1186
1475
  };
@@ -1,6 +1,8 @@
1
1
  export declare const getChallengeCacheKey: (did: string, challenge: string) => string;
2
2
  export declare const VALID = "valid";
3
+ export declare const DID_CHALLENGE_TTL_SECS: number;
3
4
  export declare const isChallengeValidForDid: (did: string, challenge: string) => Promise<typeof VALID | null | undefined>;
5
+ export declare const consumeChallengeForDid: (did: string, challenge: string) => Promise<boolean>;
4
6
  export declare const setValidChallengeForDid: (did: string, challenge: string) => Promise<"OK" | undefined>;
5
7
  export declare const setValidChallengesForDid: (did: string, challenges: string[]) => Promise<"OK" | undefined>;
6
8
  export declare const invalidateChallengeForDid: (did: string, challenge: string) => Promise<number | undefined>;
@@ -0,0 +1,42 @@
1
+ import type { AuthProviderType } from '@helpers/auth.helpers';
2
+ export declare const RECOVERY_SESSION_TTL_SECS: number;
3
+ export declare const RECOVERY_OTP_TTL_SECS: number;
4
+ export declare const RECOVERY_OTP_SEND_COOLDOWN_SECS = 60;
5
+ export declare const MAX_RECOVERY_OTP_ATTEMPTS = 5;
6
+ export type RecoverySessionScope = 'recover' | 'rebind';
7
+ export interface RecoverySessionRecord {
8
+ scope: RecoverySessionScope;
9
+ authProvider: {
10
+ type: AuthProviderType;
11
+ id: string;
12
+ };
13
+ }
14
+ export interface RecoveryOtpRecord {
15
+ codeHash: string;
16
+ authProvider: {
17
+ type: AuthProviderType;
18
+ id: string;
19
+ };
20
+ }
21
+ export type RecoveryOtpVerificationResult = {
22
+ status: 'ok';
23
+ record: RecoveryOtpRecord;
24
+ } | {
25
+ status: 'mismatch';
26
+ attempts: number;
27
+ } | {
28
+ status: 'locked';
29
+ } | {
30
+ status: 'none';
31
+ };
32
+ export declare const getRecoveryEmailDigest: (email: string) => string;
33
+ export declare const getRecoveryOtpCacheKey: (email: string) => string;
34
+ export declare const getRecoveryOtpAttemptsCacheKey: (email: string) => string;
35
+ export declare const getRecoveryOtpLockCacheKey: (email: string) => string;
36
+ export declare const getRecoveryOtpSendCacheKey: (email: string) => string;
37
+ export declare const getRecoverySessionCacheKey: (token: string) => string;
38
+ export declare const createRecoverySession: (record: RecoverySessionRecord, ttl?: number) => Promise<string>;
39
+ export declare const consumeRecoverySession: (token: string, expectedScope: RecoverySessionScope) => Promise<RecoverySessionRecord | null>;
40
+ export declare const storeRecoveryOtp: (email: string, record: RecoveryOtpRecord) => Promise<void>;
41
+ export declare const verifyRecoveryOtp: (email: string, submittedCodeHash: string) => Promise<RecoveryOtpVerificationResult>;
42
+ export declare const claimRecoveryOtpSendWindow: (email: string) => Promise<boolean>;
@@ -8,6 +8,20 @@ export declare const lcaApiEnvironmentShape: {
8
8
  }>>;
9
9
  PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
10
10
  SEED: z.ZodString;
11
+ SA_SEED_KMS_KEY_ARN: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
12
+ SA_SEED_LOCAL_KEK: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
13
+ SA_SEED_ENCRYPT_WRITES: z.ZodPreprocess<z.ZodOptional<z.ZodPipe<z.ZodEnum<{
14
+ 0: "0";
15
+ 1: "1";
16
+ true: "true";
17
+ false: "false";
18
+ }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>>;
19
+ SA_SEED_ALLOW_LEGACY_READ: z.ZodPreprocess<z.ZodOptional<z.ZodPipe<z.ZodEnum<{
20
+ 0: "0";
21
+ 1: "1";
22
+ true: "true";
23
+ false: "false";
24
+ }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>>;
11
25
  MONGO_URI: z.ZodString;
12
26
  MONGO_DB_NAME: z.ZodString;
13
27
  DOMAIN_NAME: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
@@ -25,6 +39,16 @@ export declare const lcaApiEnvironmentShape: {
25
39
  POSTMARK_ENDORSEMENT_REQUEST_TEMPLATE_ALIAS: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
26
40
  POSTMARK_RECOVERY_EMAIL_CODE_TEMPLATE_ALIAS: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
27
41
  POSTMARK_RECOVERY_KEY_TEMPLATE_ALIAS: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
42
+ ESCROW_RELAY_URL: z.ZodPreprocess<z.ZodOptional<z.ZodURL>>;
43
+ ESCROW_RELAY_AUTH_TOKEN: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
44
+ ESCROW_ENCLAVE_MODE: z.ZodPipe<z.ZodPreprocess<z.ZodOptional<z.ZodString>>, z.ZodOptional<z.ZodEnum<{
45
+ software: "software";
46
+ remote: "remote";
47
+ }>>>;
48
+ ESCROW_ENCLAVE_SOFTWARE_PRIVATE_KEYS_JSON: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
49
+ ESCROW_ENCLAVE_ACTIVE_KEY_ID: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
50
+ ESCROW_HOLD_DURATION_MS: z.ZodPipe<z.ZodPipe<z.ZodPreprocess<z.ZodOptional<z.ZodString>>, z.ZodTransform<number, string | undefined>>, z.ZodNumber>;
51
+ ESCROW_HOLD_RESTART_MIN_AGE_MS: z.ZodPipe<z.ZodPipe<z.ZodPreprocess<z.ZodOptional<z.ZodString>>, z.ZodTransform<number, string | undefined>>, z.ZodNumber>;
28
52
  ANDROID_PUSH_ICON: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
29
53
  REDIS_HOST: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
30
54
  REDIS_PORT: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -64,7 +88,7 @@ export declare const lcaApiEnvironmentShape: {
64
88
  false: "false";
65
89
  }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>>>;
66
90
  };
67
- export declare const lcaApiEnvironmentSchema: z.ZodObject<{
91
+ export declare const lcaApiEnvironmentSchema: z.ZodPipe<z.ZodObject<{
68
92
  NODE_ENV: z.ZodDefault<z.ZodEnum<{
69
93
  development: "development";
70
94
  test: "test";
@@ -72,6 +96,20 @@ export declare const lcaApiEnvironmentSchema: z.ZodObject<{
72
96
  }>>;
73
97
  PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
74
98
  SEED: z.ZodString;
99
+ SA_SEED_KMS_KEY_ARN: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
100
+ SA_SEED_LOCAL_KEK: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
101
+ SA_SEED_ENCRYPT_WRITES: z.ZodPreprocess<z.ZodOptional<z.ZodPipe<z.ZodEnum<{
102
+ 0: "0";
103
+ 1: "1";
104
+ true: "true";
105
+ false: "false";
106
+ }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>>;
107
+ SA_SEED_ALLOW_LEGACY_READ: z.ZodPreprocess<z.ZodOptional<z.ZodPipe<z.ZodEnum<{
108
+ 0: "0";
109
+ 1: "1";
110
+ true: "true";
111
+ false: "false";
112
+ }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>>;
75
113
  MONGO_URI: z.ZodString;
76
114
  MONGO_DB_NAME: z.ZodString;
77
115
  DOMAIN_NAME: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
@@ -89,6 +127,16 @@ export declare const lcaApiEnvironmentSchema: z.ZodObject<{
89
127
  POSTMARK_ENDORSEMENT_REQUEST_TEMPLATE_ALIAS: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
90
128
  POSTMARK_RECOVERY_EMAIL_CODE_TEMPLATE_ALIAS: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
91
129
  POSTMARK_RECOVERY_KEY_TEMPLATE_ALIAS: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
130
+ ESCROW_RELAY_URL: z.ZodPreprocess<z.ZodOptional<z.ZodURL>>;
131
+ ESCROW_RELAY_AUTH_TOKEN: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
132
+ ESCROW_ENCLAVE_MODE: z.ZodPipe<z.ZodPreprocess<z.ZodOptional<z.ZodString>>, z.ZodOptional<z.ZodEnum<{
133
+ software: "software";
134
+ remote: "remote";
135
+ }>>>;
136
+ ESCROW_ENCLAVE_SOFTWARE_PRIVATE_KEYS_JSON: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
137
+ ESCROW_ENCLAVE_ACTIVE_KEY_ID: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
138
+ ESCROW_HOLD_DURATION_MS: z.ZodPipe<z.ZodPipe<z.ZodPreprocess<z.ZodOptional<z.ZodString>>, z.ZodTransform<number, string | undefined>>, z.ZodNumber>;
139
+ ESCROW_HOLD_RESTART_MIN_AGE_MS: z.ZodPipe<z.ZodPipe<z.ZodPreprocess<z.ZodOptional<z.ZodString>>, z.ZodTransform<number, string | undefined>>, z.ZodNumber>;
92
140
  ANDROID_PUSH_ICON: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
93
141
  REDIS_HOST: z.ZodPreprocess<z.ZodOptional<z.ZodString>>;
94
142
  REDIS_PORT: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
@@ -127,20 +175,117 @@ export declare const lcaApiEnvironmentSchema: z.ZodObject<{
127
175
  true: "true";
128
176
  false: "false";
129
177
  }>, z.ZodTransform<boolean, "0" | "1" | "true" | "false">>>>>;
130
- }, z.core.$strip>;
178
+ }, z.core.$strip>, z.ZodTransform<{
179
+ SA_SEED_ENCRYPT_WRITES: boolean;
180
+ SA_SEED_ALLOW_LEGACY_READ: boolean;
181
+ NODE_ENV: "development" | "test" | "production";
182
+ PORT: number;
183
+ SEED: string;
184
+ MONGO_URI: string;
185
+ MONGO_DB_NAME: string;
186
+ ESCROW_HOLD_DURATION_MS: number;
187
+ ESCROW_HOLD_RESTART_MIN_AGE_MS: number;
188
+ IS_OFFLINE: boolean;
189
+ IS_CI: boolean;
190
+ IS_E2E_TEST: boolean;
191
+ SKIP_DIDKIT_NAPI: boolean;
192
+ CI: boolean;
193
+ SA_SEED_KMS_KEY_ARN?: string | undefined;
194
+ SA_SEED_LOCAL_KEK?: string | undefined;
195
+ DOMAIN_NAME?: string | undefined;
196
+ AUTHORIZED_DIDS?: string | undefined;
197
+ LEARN_CLOUD_URL?: string | undefined;
198
+ NETWORK_BRAIN_SERVICE_URL?: string | undefined;
199
+ GOOGLE_APPLICATION_CREDENTIAL?: string | undefined;
200
+ OPENAI_API_KEY?: string | undefined;
201
+ METABASE_SECRET_KEY?: string | undefined;
202
+ SCOUTS_SSO_CLIENT_SECRET?: string | undefined;
203
+ POSTMARK_SERVER_TOKEN?: string | undefined;
204
+ POSTMARK_FROM_EMAIL?: string | undefined;
205
+ POSTMARK_BRAND_NAME?: string | undefined;
206
+ POSTMARK_LOGIN_CODE_TEMPLATE_ALIAS?: string | undefined;
207
+ POSTMARK_ENDORSEMENT_REQUEST_TEMPLATE_ALIAS?: string | undefined;
208
+ POSTMARK_RECOVERY_EMAIL_CODE_TEMPLATE_ALIAS?: string | undefined;
209
+ POSTMARK_RECOVERY_KEY_TEMPLATE_ALIAS?: string | undefined;
210
+ ESCROW_RELAY_URL?: string | undefined;
211
+ ESCROW_RELAY_AUTH_TOKEN?: string | undefined;
212
+ ESCROW_ENCLAVE_MODE?: "software" | "remote" | undefined;
213
+ ESCROW_ENCLAVE_SOFTWARE_PRIVATE_KEYS_JSON?: string | undefined;
214
+ ESCROW_ENCLAVE_ACTIVE_KEY_ID?: string | undefined;
215
+ ANDROID_PUSH_ICON?: string | undefined;
216
+ REDIS_HOST?: string | undefined;
217
+ REDIS_PORT?: number | undefined;
218
+ NSO_DASHBOARD_ID?: number | undefined;
219
+ GLOBAL_DASHBOARD_ID?: number | undefined;
220
+ TROOP_DASHBOARD_ID?: number | undefined;
221
+ SENTRY_DSN?: string | undefined;
222
+ SENTRY_ENV?: string | undefined;
223
+ }, {
224
+ NODE_ENV: "development" | "test" | "production";
225
+ PORT: number;
226
+ SEED: string;
227
+ MONGO_URI: string;
228
+ MONGO_DB_NAME: string;
229
+ ESCROW_HOLD_DURATION_MS: number;
230
+ ESCROW_HOLD_RESTART_MIN_AGE_MS: number;
231
+ IS_OFFLINE: boolean;
232
+ IS_CI: boolean;
233
+ IS_E2E_TEST: boolean;
234
+ SKIP_DIDKIT_NAPI: boolean;
235
+ CI: boolean;
236
+ SA_SEED_KMS_KEY_ARN?: string | undefined;
237
+ SA_SEED_LOCAL_KEK?: string | undefined;
238
+ SA_SEED_ENCRYPT_WRITES?: boolean | undefined;
239
+ SA_SEED_ALLOW_LEGACY_READ?: boolean | undefined;
240
+ DOMAIN_NAME?: string | undefined;
241
+ AUTHORIZED_DIDS?: string | undefined;
242
+ LEARN_CLOUD_URL?: string | undefined;
243
+ NETWORK_BRAIN_SERVICE_URL?: string | undefined;
244
+ GOOGLE_APPLICATION_CREDENTIAL?: string | undefined;
245
+ OPENAI_API_KEY?: string | undefined;
246
+ METABASE_SECRET_KEY?: string | undefined;
247
+ SCOUTS_SSO_CLIENT_SECRET?: string | undefined;
248
+ POSTMARK_SERVER_TOKEN?: string | undefined;
249
+ POSTMARK_FROM_EMAIL?: string | undefined;
250
+ POSTMARK_BRAND_NAME?: string | undefined;
251
+ POSTMARK_LOGIN_CODE_TEMPLATE_ALIAS?: string | undefined;
252
+ POSTMARK_ENDORSEMENT_REQUEST_TEMPLATE_ALIAS?: string | undefined;
253
+ POSTMARK_RECOVERY_EMAIL_CODE_TEMPLATE_ALIAS?: string | undefined;
254
+ POSTMARK_RECOVERY_KEY_TEMPLATE_ALIAS?: string | undefined;
255
+ ESCROW_RELAY_URL?: string | undefined;
256
+ ESCROW_RELAY_AUTH_TOKEN?: string | undefined;
257
+ ESCROW_ENCLAVE_MODE?: "software" | "remote" | undefined;
258
+ ESCROW_ENCLAVE_SOFTWARE_PRIVATE_KEYS_JSON?: string | undefined;
259
+ ESCROW_ENCLAVE_ACTIVE_KEY_ID?: string | undefined;
260
+ ANDROID_PUSH_ICON?: string | undefined;
261
+ REDIS_HOST?: string | undefined;
262
+ REDIS_PORT?: number | undefined;
263
+ NSO_DASHBOARD_ID?: number | undefined;
264
+ GLOBAL_DASHBOARD_ID?: number | undefined;
265
+ TROOP_DASHBOARD_ID?: number | undefined;
266
+ SENTRY_DSN?: string | undefined;
267
+ SENTRY_ENV?: string | undefined;
268
+ }>>;
131
269
  export type LcaApiEnvironment = z.output<typeof lcaApiEnvironmentSchema>;
132
270
  export declare const parseLcaApiEnvironment: (raw: Record<string, unknown>, source?: string) => LcaApiEnvironment;
271
+ export declare const parseEscrowPrivateKeys: (serialized: string) => Record<string, string>;
133
272
  export declare const environment: {
273
+ SA_SEED_ENCRYPT_WRITES: boolean;
274
+ SA_SEED_ALLOW_LEGACY_READ: boolean;
134
275
  NODE_ENV: "development" | "test" | "production";
135
276
  PORT: number;
136
277
  SEED: string;
137
278
  MONGO_URI: string;
138
279
  MONGO_DB_NAME: string;
280
+ ESCROW_HOLD_DURATION_MS: number;
281
+ ESCROW_HOLD_RESTART_MIN_AGE_MS: number;
139
282
  IS_OFFLINE: boolean;
140
283
  IS_CI: boolean;
141
284
  IS_E2E_TEST: boolean;
142
285
  SKIP_DIDKIT_NAPI: boolean;
143
286
  CI: boolean;
287
+ SA_SEED_KMS_KEY_ARN?: string | undefined;
288
+ SA_SEED_LOCAL_KEK?: string | undefined;
144
289
  DOMAIN_NAME?: string | undefined;
145
290
  AUTHORIZED_DIDS?: string | undefined;
146
291
  LEARN_CLOUD_URL?: string | undefined;
@@ -156,6 +301,11 @@ export declare const environment: {
156
301
  POSTMARK_ENDORSEMENT_REQUEST_TEMPLATE_ALIAS?: string | undefined;
157
302
  POSTMARK_RECOVERY_EMAIL_CODE_TEMPLATE_ALIAS?: string | undefined;
158
303
  POSTMARK_RECOVERY_KEY_TEMPLATE_ALIAS?: string | undefined;
304
+ ESCROW_RELAY_URL?: string | undefined;
305
+ ESCROW_RELAY_AUTH_TOKEN?: string | undefined;
306
+ ESCROW_ENCLAVE_MODE?: "software" | "remote" | undefined;
307
+ ESCROW_ENCLAVE_SOFTWARE_PRIVATE_KEYS_JSON?: string | undefined;
308
+ ESCROW_ENCLAVE_ACTIVE_KEY_ID?: string | undefined;
159
309
  ANDROID_PUSH_ICON?: string | undefined;
160
310
  REDIS_HOST?: string | undefined;
161
311
  REDIS_PORT?: number | undefined;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export declare const checkRateLimit: (key: string, maxAttempts: number, windowSeconds: number, prefix?: string) => Promise<boolean>;
2
+ export declare const getRateLimitCount: (key: string, prefix?: string) => Promise<number>;
3
+ export declare const clearRateLimit: (key: string, prefix?: string) => Promise<void>;
4
+ export declare const decrementRateLimit: (key: string, prefix?: string) => Promise<void>;