@learncard/sss-key-manager 0.1.21 → 0.1.23

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.
package/README.md CHANGED
@@ -8,14 +8,14 @@ This package provides a secure, self-hosted alternative to Web3Auth Single Facto
8
8
 
9
9
  ## Features
10
10
 
11
- - **Key Splitting**: Split ed25519 private keys into 3 shares with 2-of-3 threshold
12
- - **Device Storage**: Encrypted local storage using AES-GCM with IndexedDB
13
- - **Server Storage**: Encrypted auth share stored on server with envelope encryption
14
- - **Recovery Methods**:
15
- - Password-based (Argon2id KDF)
16
- - Passkey/WebAuthn PRF (coming soon)
17
- - Backup file export/import
18
- - **Migration**: Seamless migration from Web3Auth SFA
11
+ - **Key Splitting**: Split ed25519 private keys into 3 shares with 2-of-3 threshold
12
+ - **Device Storage**: Encrypted local storage using AES-GCM with IndexedDB
13
+ - **Server Storage**: Encrypted auth share stored on server with envelope encryption
14
+ - **Recovery Methods**:
15
+ - Password-based (Argon2id KDF)
16
+ - Passkey/WebAuthn PRF (coming soon)
17
+ - Backup file export/import
18
+ - **Migration**: Seamless migration from Web3Auth SFA
19
19
 
20
20
  ## Installation
21
21
 
@@ -115,19 +115,19 @@ const privateKey = await keyManager.recover({
115
115
 
116
116
  ## Security Model
117
117
 
118
- - **Device Share**: Encrypted with non-extractable AES-GCM key stored in IndexedDB
119
- - **Auth Share**: Server-side envelope encryption (DEK + KMS-encrypted DEK)
120
- - **Recovery Share**: Password-based uses Argon2id KDF with secure parameters
121
- - **Threshold**: Any 2 of 3 shares can reconstruct the key
118
+ - **Device Share**: Encrypted with non-extractable AES-GCM key stored in IndexedDB
119
+ - **Auth Share**: Server-side envelope encryption (DEK + KMS-encrypted DEK)
120
+ - **Recovery Share**: Password-based uses Argon2id KDF with secure parameters
121
+ - **Threshold**: Any 2 of 3 shares can reconstruct the key
122
122
 
123
123
  ## Auth Provider Support
124
124
 
125
125
  The package is designed to work with any authentication provider:
126
126
 
127
- - Firebase Authentication (default for production)
128
- - SuperTokens (recommended for self-hosting/local dev)
129
- - Keycloak (enterprise SSO)
130
- - Any OIDC-compliant provider
127
+ - Firebase Authentication (default for production)
128
+ - SuperTokens (recommended for self-hosting/local dev)
129
+ - Keycloak (enterprise SSO)
130
+ - Any OIDC-compliant provider
131
131
 
132
132
  ## API Reference
133
133
 
@@ -167,6 +167,147 @@ Exports an encrypted backup file.
167
167
 
168
168
  Returns the current security level based on configured recovery methods.
169
169
 
170
+ ## Four shares
171
+
172
+ `SSS_TOTAL_SHARES = 4` and `SSS_THRESHOLD = 2`: any two shares reconstruct the private key, while one share alone reveals nothing about it.
173
+
174
+ | Share | Where it lives | Purpose |
175
+ | -------------- | ---------------------------------------------------------------------------------- | ---------------------------------- |
176
+ | Device share | Local IndexedDB | Same-device sign-in |
177
+ | Auth share | LearnCard API server, encrypted at rest | Authenticated sign-in and recovery |
178
+ | Recovery share | Passkey-protected server record, offline phrase, or password-encrypted backup file | User-controlled recovery |
179
+ | Email share | Recovery email backup, encrypted before delivery | Optional additional recovery path |
180
+
181
+ Passkeys protect the recovery share using a key derived from WebAuthn PRF output. A recovery phrase encodes the share as a mnemonic; a backup file encrypts it using Argon2id password derivation and AES-GCM, with its salt and KDF parameters stored in the file.
182
+
183
+ ### Server-side encryption
184
+
185
+ The `lca-api` service derives an AES-256-GCM key from its server `SEED` using HKDF-SHA256, with salt `lca-auth-share-encryption` and info `v1`. Each auth-share encryption uses a fresh 12-byte IV; the stored ciphertext includes the 16-byte authentication tag.
186
+
187
+ This implementation encrypts the share directly with the derived key. It does **not** generate a per-share Data Encryption Key (DEK) wrapped by a separate Key Encryption Key (KEK): the `encryptedDek` field holds the format marker `server-v1`, not a wrapped DEK. Password backup encryption and server auth-share encryption are separate mechanisms.
188
+
189
+ ## Key Types
190
+
191
+ ### ContactMethod
192
+
193
+ Identifies a user by their primary contact method:
194
+
195
+ ```typescript
196
+ type ContactMethodType = 'email' | 'phone';
197
+
198
+ interface ContactMethod {
199
+ type: ContactMethodType;
200
+ value: string;
201
+ }
202
+ ```
203
+
204
+ ### SecurityLevel
205
+
206
+ Describes how well-protected a user's key is:
207
+
208
+ ```typescript
209
+ type SecurityLevel = 'basic' | 'enhanced' | 'advanced';
210
+ ```
211
+
212
+ - **basic** — device + server share only (no recovery method)
213
+ - **enhanced** — at least one recovery method configured
214
+ - **advanced** — multiple recovery methods configured
215
+
216
+ ### RecoveryInput
217
+
218
+ What the user provides to recover their key:
219
+
220
+ ```typescript
221
+ type RecoveryInput =
222
+ | { method: 'passkey'; credentialId: string }
223
+ | { method: 'phrase'; phrase: string }
224
+ | { method: 'backup'; fileContents: string; password: string }
225
+ | { method: 'email'; emailShare: string };
226
+ ```
227
+
228
+ ### RecoverySetupInput
229
+
230
+ What the user provides to set up a new recovery method:
231
+
232
+ ```typescript
233
+ type RecoverySetupInput =
234
+ | { method: 'passkey' }
235
+ | { method: 'phrase' }
236
+ | { method: 'backup'; password: string; did: string }
237
+ | { method: 'email' };
238
+ ```
239
+
240
+ ### BackupFile
241
+
242
+ The JSON structure of a downloadable backup file:
243
+
244
+ ```typescript
245
+ interface BackupFile {
246
+ version: 1;
247
+ createdAt: string;
248
+ primaryDid: string;
249
+ shareVersion?: number;
250
+ encryptedShare: {
251
+ ciphertext: string;
252
+ iv: string;
253
+ salt: string;
254
+ kdfParams: {
255
+ algorithm: 'argon2id';
256
+ timeCost: number;
257
+ memoryCost: number;
258
+ parallelism: number;
259
+ };
260
+ };
261
+ }
262
+ ```
263
+
264
+ ## SSSStrategy
265
+
266
+ The main class that implements `KeyDerivationStrategy`. It is typically instantiated by the AuthCoordinator, not directly by application code.
267
+
268
+ ### Configuration
269
+
270
+ ```typescript
271
+ interface SSSStrategyConfig {
272
+ serverUrl: string;
273
+ enableEmailBackupShare?: boolean;
274
+ }
275
+ ```
276
+
277
+ | Option | Default | Description |
278
+ | ------------------------ | ------- | ---------------------------------------------------------------------- |
279
+ | `serverUrl` | — | Base URL of the lca-api server (e.g., `https://api.example.com/api`) |
280
+ | `enableEmailBackupShare` | `false` | Automatically send a backup share to the user's email during key setup |
281
+
282
+ ### Key Methods
283
+
284
+ | Method | Purpose |
285
+ | -------------------------------------------------- | --------------------------------------------------------------------- |
286
+ | `fetchServerKeyStatus(token, providerType)` | Check if a key record exists on the server for the authenticated user |
287
+ | `setupNewKey(token, providerType, signVp)` | Generate a new key, split it, store shares on device and server |
288
+ | `reconstructKey(token, providerType)` | Reconstruct the key from device share + auth share |
289
+ | `recoverKey(token, providerType, input)` | Recover the key using a recovery method + auth share |
290
+ | `hasLocalKey()` | Check if a device share exists in IndexedDB |
291
+ | `clearLocalKey()` | Remove the device share from IndexedDB |
292
+ | `setupRecoveryMethod(params)` | Set up a new recovery method (passkey, phrase, backup, email) |
293
+ | `getAvailableRecoveryMethods(token, providerType)` | List the user's configured recovery methods |
294
+
295
+ ## API Client
296
+
297
+ The `api-client.ts` module provides a typed fetch wrapper for all lca-api `/keys/*` and `/qr-login/*` routes. It is used internally by the SSS strategy but can also be used directly:
298
+
299
+ ```typescript
300
+ import { createApiClient } from '@learncard/sss-key-manager';
301
+
302
+ const client = createApiClient({ serverUrl: 'https://api.example.com/api' });
303
+
304
+ const status = await client.getAuthShare({
305
+ authToken: token,
306
+ providerType: 'firebase',
307
+ contactMethod: { type: 'email', value: 'user@example.com' },
308
+ });
309
+ ```
310
+
170
311
  ## License
171
312
 
172
313
  MIT
@@ -66,6 +66,8 @@ var require_types_cjs_development = __commonJS({
66
66
  AgeRatingValidator: /* @__PURE__ */ __name(() => AgeRatingValidator, "AgeRatingValidator"),
67
67
  AlignmentTargetTypeValidator: /* @__PURE__ */ __name(() => AlignmentTargetTypeValidator, "AlignmentTargetTypeValidator"),
68
68
  AlignmentValidator: /* @__PURE__ */ __name(() => AlignmentValidator, "AlignmentValidator"),
69
+ AllocateCredentialRefreshInputValidator: /* @__PURE__ */ __name(() => AllocateCredentialRefreshInputValidator, "AllocateCredentialRefreshInputValidator"),
70
+ AllocateCredentialRefreshResultValidator: /* @__PURE__ */ __name(() => AllocateCredentialRefreshResultValidator, "AllocateCredentialRefreshResultValidator"),
69
71
  AllocateCredentialStatusInputValidator: /* @__PURE__ */ __name(() => AllocateCredentialStatusInputValidator, "AllocateCredentialStatusInputValidator"),
70
72
  AllocatedBitstringStatusListEntryValidator: /* @__PURE__ */ __name(() => AllocatedBitstringStatusListEntryValidator, "AllocatedBitstringStatusListEntryValidator"),
71
73
  AllowConnectionRequestsEnum: /* @__PURE__ */ __name(() => AllowConnectionRequestsEnum, "AllowConnectionRequestsEnum"),
@@ -115,6 +117,7 @@ var require_types_cjs_development = __commonJS({
115
117
  ConsentFlowContractValidator: /* @__PURE__ */ __name(() => ConsentFlowContractValidator, "ConsentFlowContractValidator"),
116
118
  ConsentFlowDataForDidQueryValidator: /* @__PURE__ */ __name(() => ConsentFlowDataForDidQueryValidator, "ConsentFlowDataForDidQueryValidator"),
117
119
  ConsentFlowDataQueryValidator: /* @__PURE__ */ __name(() => ConsentFlowDataQueryValidator, "ConsentFlowDataQueryValidator"),
120
+ ConsentFlowGuardianApprovalValidator: /* @__PURE__ */ __name(() => ConsentFlowGuardianApprovalValidator, "ConsentFlowGuardianApprovalValidator"),
118
121
  ConsentFlowTermValidator: /* @__PURE__ */ __name(() => ConsentFlowTermValidator, "ConsentFlowTermValidator"),
119
122
  ConsentFlowTermsQueryValidator: /* @__PURE__ */ __name(() => ConsentFlowTermsQueryValidator, "ConsentFlowTermsQueryValidator"),
120
123
  ConsentFlowTermsStatusValidator: /* @__PURE__ */ __name(() => ConsentFlowTermsStatusValidator, "ConsentFlowTermsStatusValidator"),
@@ -147,6 +150,16 @@ var require_types_cjs_development = __commonJS({
147
150
  CredentialInfoValidator: /* @__PURE__ */ __name(() => CredentialInfoValidator, "CredentialInfoValidator"),
148
151
  CredentialNameRefValidator: /* @__PURE__ */ __name(() => CredentialNameRefValidator, "CredentialNameRefValidator"),
149
152
  CredentialRecordValidator: /* @__PURE__ */ __name(() => CredentialRecordValidator, "CredentialRecordValidator"),
153
+ CredentialRefreshChallengeValidator: /* @__PURE__ */ __name(() => CredentialRefreshChallengeValidator, "CredentialRefreshChallengeValidator"),
154
+ CredentialRefreshFailedResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshFailedResultValidator, "CredentialRefreshFailedResultValidator"),
155
+ CredentialRefreshFailureCodeValidator: /* @__PURE__ */ __name(() => CredentialRefreshFailureCodeValidator, "CredentialRefreshFailureCodeValidator"),
156
+ CredentialRefreshResponseEnvelopeValidator: /* @__PURE__ */ __name(() => CredentialRefreshResponseEnvelopeValidator, "CredentialRefreshResponseEnvelopeValidator"),
157
+ CredentialRefreshResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshResultValidator, "CredentialRefreshResultValidator"),
158
+ CredentialRefreshSigningModeValidator: /* @__PURE__ */ __name(() => CredentialRefreshSigningModeValidator, "CredentialRefreshSigningModeValidator"),
159
+ CredentialRefreshUnchangedResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshUnchangedResultValidator, "CredentialRefreshUnchangedResultValidator"),
160
+ CredentialRefreshUnsupportedResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshUnsupportedResultValidator, "CredentialRefreshUnsupportedResultValidator"),
161
+ CredentialRefreshUpdatedResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshUpdatedResultValidator, "CredentialRefreshUpdatedResultValidator"),
162
+ CredentialRefreshVersionMetadataValidator: /* @__PURE__ */ __name(() => CredentialRefreshVersionMetadataValidator, "CredentialRefreshVersionMetadataValidator"),
150
163
  CredentialSchemaValidator: /* @__PURE__ */ __name(() => CredentialSchemaValidator, "CredentialSchemaValidator"),
151
164
  CredentialStatusValidator: /* @__PURE__ */ __name(() => CredentialStatusValidator, "CredentialStatusValidator"),
152
165
  CredentialSubjectValidator: /* @__PURE__ */ __name(() => CredentialSubjectValidator, "CredentialSubjectValidator"),
@@ -167,6 +180,8 @@ var require_types_cjs_development = __commonJS({
167
180
  GeoCoordinatesValidator: /* @__PURE__ */ __name(() => GeoCoordinatesValidator, "GeoCoordinatesValidator"),
168
181
  GetCounterEventValidator: /* @__PURE__ */ __name(() => GetCounterEventValidator, "GetCounterEventValidator"),
169
182
  GetCountersEventValidator: /* @__PURE__ */ __name(() => GetCountersEventValidator, "GetCountersEventValidator"),
183
+ GetCredentialRefreshHistoryInputValidator: /* @__PURE__ */ __name(() => GetCredentialRefreshHistoryInputValidator, "GetCredentialRefreshHistoryInputValidator"),
184
+ GetCredentialRefreshHistoryResultValidator: /* @__PURE__ */ __name(() => GetCredentialRefreshHistoryResultValidator, "GetCredentialRefreshHistoryResultValidator"),
170
185
  GetFullSkillTreeInputValidator: /* @__PURE__ */ __name(() => GetFullSkillTreeInputValidator, "GetFullSkillTreeInputValidator"),
171
186
  GetFullSkillTreeResultValidator: /* @__PURE__ */ __name(() => GetFullSkillTreeResultValidator, "GetFullSkillTreeResultValidator"),
172
187
  GetSkillPathInputValidator: /* @__PURE__ */ __name(() => GetSkillPathInputValidator, "GetSkillPathInputValidator"),
@@ -191,6 +206,7 @@ var require_types_cjs_development = __commonJS({
191
206
  JWEValidator: /* @__PURE__ */ __name(() => JWEValidator, "JWEValidator"),
192
207
  JWKValidator: /* @__PURE__ */ __name(() => JWKValidator, "JWKValidator"),
193
208
  JWKWithPrivateKeyValidator: /* @__PURE__ */ __name(() => JWKWithPrivateKeyValidator, "JWKWithPrivateKeyValidator"),
209
+ JweCredentialRefreshEnvelopeValidator: /* @__PURE__ */ __name(() => JweCredentialRefreshEnvelopeValidator, "JweCredentialRefreshEnvelopeValidator"),
194
210
  KnownAchievementTypeValidator: /* @__PURE__ */ __name(() => KnownAchievementTypeValidator, "KnownAchievementTypeValidator"),
195
211
  LCNAuthedProfileValidator: /* @__PURE__ */ __name(() => LCNAuthedProfileValidator, "LCNAuthedProfileValidator"),
196
212
  LCNBoostClaimLinkOptionsValidator: /* @__PURE__ */ __name(() => LCNBoostClaimLinkOptionsValidator, "LCNBoostClaimLinkOptionsValidator"),
@@ -228,7 +244,9 @@ var require_types_cjs_development = __commonJS({
228
244
  LCNSigningAuthorityValidator: /* @__PURE__ */ __name(() => LCNSigningAuthorityValidator, "LCNSigningAuthorityValidator"),
229
245
  LCNVisibleProfileValidator: /* @__PURE__ */ __name(() => LCNVisibleProfileValidator, "LCNVisibleProfileValidator"),
230
246
  LaunchTypeValidator: /* @__PURE__ */ __name(() => LaunchTypeValidator, "LaunchTypeValidator"),
247
+ LearnCardRefreshAuthorizationValidator: /* @__PURE__ */ __name(() => LearnCardRefreshAuthorizationValidator, "LearnCardRefreshAuthorizationValidator"),
231
248
  LinkProviderFrameworkInputValidator: /* @__PURE__ */ __name(() => LinkProviderFrameworkInputValidator, "LinkProviderFrameworkInputValidator"),
249
+ ManagedCredentialRefreshServiceValidator: /* @__PURE__ */ __name(() => ManagedCredentialRefreshServiceValidator, "ManagedCredentialRefreshServiceValidator"),
232
250
  PaginatedAppStoreListingsValidator: /* @__PURE__ */ __name(() => PaginatedAppStoreListingsValidator, "PaginatedAppStoreListingsValidator"),
233
251
  PaginatedBoostRecipientsValidator: /* @__PURE__ */ __name(() => PaginatedBoostRecipientsValidator, "PaginatedBoostRecipientsValidator"),
234
252
  PaginatedBoostRecipientsWithChildrenValidator: /* @__PURE__ */ __name(() => PaginatedBoostRecipientsWithChildrenValidator, "PaginatedBoostRecipientsWithChildrenValidator"),
@@ -258,6 +276,12 @@ var require_types_cjs_development = __commonJS({
258
276
  ProfileVisibilityEnum: /* @__PURE__ */ __name(() => ProfileVisibilityEnum, "ProfileVisibilityEnum"),
259
277
  PromotionLevelValidator: /* @__PURE__ */ __name(() => PromotionLevelValidator, "PromotionLevelValidator"),
260
278
  ProofValidator: /* @__PURE__ */ __name(() => ProofValidator, "ProofValidator"),
279
+ PublicCredentialRefreshEnvelopeValidator: /* @__PURE__ */ __name(() => PublicCredentialRefreshEnvelopeValidator, "PublicCredentialRefreshEnvelopeValidator"),
280
+ PublishCredentialRefreshInputValidator: /* @__PURE__ */ __name(() => PublishCredentialRefreshInputValidator, "PublishCredentialRefreshInputValidator"),
281
+ PublishCredentialRefreshNotificationValidator: /* @__PURE__ */ __name(() => PublishCredentialRefreshNotificationValidator, "PublishCredentialRefreshNotificationValidator"),
282
+ PublishCredentialRefreshResultValidator: /* @__PURE__ */ __name(() => PublishCredentialRefreshResultValidator, "PublishCredentialRefreshResultValidator"),
283
+ PublishIssuerSignedRefreshValidator: /* @__PURE__ */ __name(() => PublishIssuerSignedRefreshValidator, "PublishIssuerSignedRefreshValidator"),
284
+ PublishSigningAuthorityRefreshValidator: /* @__PURE__ */ __name(() => PublishSigningAuthorityRefreshValidator, "PublishSigningAuthorityRefreshValidator"),
261
285
  RefreshServiceValidator: /* @__PURE__ */ __name(() => RefreshServiceValidator, "RefreshServiceValidator"),
262
286
  RegExpValidator: /* @__PURE__ */ __name(() => RegExpValidator, "RegExpValidator"),
263
287
  RelatedValidator: /* @__PURE__ */ __name(() => RelatedValidator, "RelatedValidator"),
@@ -298,11 +322,13 @@ var require_types_cjs_development = __commonJS({
298
322
  SkillTreeNodeInputValidator: /* @__PURE__ */ __name(() => SkillTreeNodeInputValidator, "SkillTreeNodeInputValidator"),
299
323
  SkillTreeNodeValidator: /* @__PURE__ */ __name(() => SkillTreeNodeValidator, "SkillTreeNodeValidator"),
300
324
  SkillValidator: /* @__PURE__ */ __name(() => SkillValidator, "SkillValidator"),
325
+ StandardCredentialRefreshServiceValidator: /* @__PURE__ */ __name(() => StandardCredentialRefreshServiceValidator, "StandardCredentialRefreshServiceValidator"),
301
326
  StatusCheckEntryValidator: /* @__PURE__ */ __name(() => StatusCheckEntryValidator, "StatusCheckEntryValidator"),
302
327
  StoredCredentialEnvelopeValidator: /* @__PURE__ */ __name(() => StoredCredentialEnvelopeValidator, "StoredCredentialEnvelopeValidator"),
303
328
  StringQuery: /* @__PURE__ */ __name(() => StringQuery, "StringQuery"),
304
329
  SummaryCredentialDataValidator: /* @__PURE__ */ __name(() => SummaryCredentialDataValidator, "SummaryCredentialDataValidator"),
305
330
  SummaryCredentialKeywordValidator: /* @__PURE__ */ __name(() => SummaryCredentialKeywordValidator, "SummaryCredentialKeywordValidator"),
331
+ SupportedCredentialRefreshServiceValidator: /* @__PURE__ */ __name(() => SupportedCredentialRefreshServiceValidator, "SupportedCredentialRefreshServiceValidator"),
306
332
  SyncFrameworkInputValidator: /* @__PURE__ */ __name(() => SyncFrameworkInputValidator, "SyncFrameworkInputValidator"),
307
333
  TagValidator: /* @__PURE__ */ __name(() => TagValidator, "TagValidator"),
308
334
  TemplateRenderMethodValidator: /* @__PURE__ */ __name(() => TemplateRenderMethodValidator, "TemplateRenderMethodValidator"),
@@ -15738,14 +15764,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15738
15764
  }).catchall(external_exports.any());
15739
15765
  var ProofValidator = external_exports.object({
15740
15766
  type: external_exports.string(),
15741
- created: external_exports.string(),
15767
+ created: external_exports.string().optional(),
15742
15768
  challenge: external_exports.string().optional(),
15743
15769
  domain: external_exports.string().optional(),
15744
15770
  nonce: external_exports.string().optional(),
15745
15771
  proofPurpose: external_exports.string(),
15746
15772
  verificationMethod: external_exports.string(),
15747
15773
  jws: external_exports.string().optional()
15748
- }).catchall(external_exports.any());
15774
+ }).catchall(external_exports.any()).superRefine((proof, ctx) => {
15775
+ if (proof.created === void 0 && (proof.type !== "DataIntegrityProof" || proof.cryptosuite !== "ecdsa-rdfc-2019")) {
15776
+ ctx.addIssue({
15777
+ code: "custom",
15778
+ path: ["created"],
15779
+ message: "created is required for this proof suite"
15780
+ });
15781
+ }
15782
+ });
15749
15783
  var VCValidator = UnsignedVCValidator.extend({
15750
15784
  proof: ProofValidator.or(ProofValidator.array())
15751
15785
  });
@@ -16506,7 +16540,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16506
16540
  webhookUrl: external_exports.string().url().optional().describe("Webhook URL to receive claim notifications"),
16507
16541
  suppressDelivery: external_exports.boolean().optional().describe("If true, returns claimUrl without sending email/SMS"),
16508
16542
  branding: SendBrandingOptionsValidator.optional().describe("Branding for email/SMS delivery"),
16509
- guardianEmail: external_exports.string().email().optional().describe("Guardian email that must approve before student can claim")
16543
+ guardianEmail: external_exports.string().email().optional().describe("Guardian email that must approve before student can claim"),
16544
+ expiresInDays: external_exports.number().int().min(1).max(720).optional().describe(
16545
+ "How many days the credential stays claimable in the Universal Inbox (default 30). Does not change the credential validity period."
16546
+ )
16510
16547
  });
16511
16548
  var SendBoostInputValidator = external_exports.object({
16512
16549
  type: external_exports.literal("boost"),
@@ -16627,15 +16664,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16627
16664
  var PaginatedConsentFlowDataValidator = PaginationResponseValidator.extend({
16628
16665
  records: ConsentFlowContractDataValidator.array()
16629
16666
  });
16630
- var ConsentFlowContractDataForDidValidator = external_exports.object({
16631
- credentials: external_exports.object({ category: external_exports.string(), uri: external_exports.string() }).array(),
16632
- personal: external_exports.record(external_exports.string(), external_exports.string()).default({}),
16633
- date: external_exports.string(),
16634
- contractUri: external_exports.string()
16635
- });
16636
- var PaginatedConsentFlowDataForDidValidator = PaginationResponseValidator.extend({
16637
- records: ConsentFlowContractDataForDidValidator.array()
16638
- });
16639
16667
  var ConsentFlowTermValidator = external_exports.object({
16640
16668
  sharing: external_exports.boolean().optional(),
16641
16669
  shared: external_exports.string().array().optional(),
@@ -16669,6 +16697,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16669
16697
  status: ConsentFlowTermsStatusValidator
16670
16698
  }).array()
16671
16699
  });
16700
+ var ConsentFlowGuardianApprovalValidator = external_exports.object({
16701
+ guardianProfileId: external_exports.string(),
16702
+ guardianDid: external_exports.string(),
16703
+ approvedAt: external_exports.string().datetime(),
16704
+ contractUpdatedAt: external_exports.string()
16705
+ });
16706
+ var ConsentFlowContractDataForDidValidator = external_exports.object({
16707
+ credentials: external_exports.object({ category: external_exports.string(), uri: external_exports.string() }).array(),
16708
+ personal: external_exports.record(external_exports.string(), external_exports.string()).default({}),
16709
+ date: external_exports.string(),
16710
+ createdAt: external_exports.string().optional(),
16711
+ contractUpdatedAt: external_exports.string(),
16712
+ contractExpiresAt: external_exports.string().optional(),
16713
+ reasonForAccessing: external_exports.string().optional(),
16714
+ guardian: external_exports.object({
16715
+ required: external_exports.boolean(),
16716
+ approved: external_exports.boolean(),
16717
+ approval: ConsentFlowGuardianApprovalValidator.optional()
16718
+ }),
16719
+ contractUri: external_exports.string(),
16720
+ termsUri: external_exports.string(),
16721
+ status: ConsentFlowTermsStatusValidator,
16722
+ expiresAt: external_exports.string().optional(),
16723
+ terms: ConsentFlowTermsValidator
16724
+ });
16725
+ var PaginatedConsentFlowDataForDidValidator = PaginationResponseValidator.extend({
16726
+ records: ConsentFlowContractDataForDidValidator.array()
16727
+ });
16672
16728
  var ConsentFlowContractQueryValidator = external_exports.object({
16673
16729
  read: external_exports.object({
16674
16730
  anonymize: external_exports.boolean().optional(),
@@ -16729,6 +16785,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16729
16785
  expiresAt: external_exports.string().optional(),
16730
16786
  oneTime: external_exports.boolean().optional(),
16731
16787
  terms: ConsentFlowTermsValidator.optional(),
16788
+ guardianApproval: ConsentFlowGuardianApprovalValidator.optional(),
16732
16789
  id: external_exports.string(),
16733
16790
  action: ConsentFlowTransactionActionValidator,
16734
16791
  date: external_exports.string(),
@@ -16801,7 +16858,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16801
16858
  "APP_NOTIFICATION",
16802
16859
  "CREDENTIAL_REVOKED",
16803
16860
  "CREDENTIAL_SUSPENDED",
16804
- "CREDENTIAL_UNSUSPENDED"
16861
+ "CREDENTIAL_UNSUSPENDED",
16862
+ "CREDENTIAL_REFRESHED"
16805
16863
  ]);
16806
16864
  var LCNNotificationMessageValidator = external_exports.object({
16807
16865
  title: external_exports.string().optional(),
@@ -16933,12 +16991,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16933
16991
  });
16934
16992
  var InboxCredentialValidator = external_exports.object({
16935
16993
  id: external_exports.string(),
16936
- credential: external_exports.string(),
16994
+ credential: external_exports.string().optional(),
16937
16995
  isSigned: external_exports.boolean(),
16938
16996
  currentStatus: LCNInboxStatusEnumValidator,
16939
16997
  isAccepted: external_exports.boolean().optional(),
16940
16998
  expiresAt: external_exports.string(),
16941
16999
  createdAt: external_exports.string(),
17000
+ finalizedAt: external_exports.string().optional(),
17001
+ expiredAt: external_exports.string().optional(),
17002
+ credentialName: external_exports.string().optional(),
17003
+ achievementType: external_exports.string().optional(),
16942
17004
  issuerDid: external_exports.string(),
16943
17005
  webhookUrl: external_exports.string().optional(),
16944
17006
  boostUri: external_exports.string().optional(),
@@ -16992,7 +17054,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16992
17054
  "The signing authority to use for the credential. If not provided, the users default signing authority will be used if the credential is not signed."
16993
17055
  ),
16994
17056
  webhookUrl: external_exports.string().url().optional().describe("The webhook URL to receive credential issuance events."),
16995
- expiresInDays: external_exports.number().min(1).max(365).optional().describe("The number of days the credential will be valid for."),
17057
+ expiresInDays: external_exports.number().int().min(1).max(720).optional().describe(
17058
+ "How many days the encrypted inbox payload remains claimable. This does not change the credential validity period."
17059
+ ),
16996
17060
  templateData: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe(
16997
17061
  "Template data to render into the boost credential template using Mustache syntax. Only used when boostUri is provided."
16998
17062
  ),
@@ -17055,6 +17119,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17055
17119
  credential: VCValidator.or(VPValidator).or(UnsignedVCValidator).or(CredentialNameRefValidator).describe("The credential to issue, or a { name } reference to resolve a boost template."),
17056
17120
  configuration: external_exports.object({
17057
17121
  publishableKey: external_exports.string(),
17122
+ expiresInDays: external_exports.number().int().min(1).max(720).optional().describe(
17123
+ "Inbox claim window in days. Defaults to 720; use a shorter window for sensitive records."
17124
+ ),
17058
17125
  signingAuthorityName: external_exports.string().optional(),
17059
17126
  listingId: external_exports.string().optional(),
17060
17127
  listingSlug: external_exports.string().optional()
@@ -17692,6 +17759,191 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17692
17759
  const result = inAppMessagesFlagValidator.safeParse(raw);
17693
17760
  return result.success ? result.data : EMPTY_IN_APP_MESSAGES_FLAG;
17694
17761
  }, "parseInAppMessagesFlag");
17762
+ var LearnCardRefreshAuthorizationValidator = external_exports.object({ type: external_exports.literal("LearnCardDIDAuth") }).catchall(external_exports.any());
17763
+ var ManagedCredentialRefreshServiceValidator = external_exports.object({
17764
+ id: external_exports.string().min(1),
17765
+ type: external_exports.literal("LearnCardCredentialRefresh2026"),
17766
+ authorization: LearnCardRefreshAuthorizationValidator.optional()
17767
+ }).catchall(external_exports.any());
17768
+ var StandardCredentialRefreshServiceValidator = external_exports.object({
17769
+ id: external_exports.string().min(1),
17770
+ type: external_exports.literal("1EdTechCredentialRefresh")
17771
+ }).catchall(external_exports.any());
17772
+ var SupportedCredentialRefreshServiceValidator = external_exports.union([
17773
+ ManagedCredentialRefreshServiceValidator,
17774
+ StandardCredentialRefreshServiceValidator
17775
+ ]);
17776
+ var AllocateCredentialRefreshInputValidator = external_exports.object({
17777
+ holder: external_exports.object({
17778
+ profileId: external_exports.string().optional(),
17779
+ did: external_exports.string().min(1)
17780
+ }),
17781
+ credentialId: external_exports.string().min(1)
17782
+ });
17783
+ var AllocateCredentialRefreshResultValidator = external_exports.object({
17784
+ refreshId: external_exports.string().min(1),
17785
+ refreshService: ManagedCredentialRefreshServiceValidator.extend({
17786
+ authorization: LearnCardRefreshAuthorizationValidator
17787
+ })
17788
+ });
17789
+ var CredentialRefreshSigningModeValidator = external_exports.enum(["issuer-signed", "signing-authority"]);
17790
+ var PublishCredentialRefreshBaseFields = {
17791
+ refreshId: external_exports.string().min(1),
17792
+ notifyHolder: external_exports.boolean().optional(),
17793
+ updateSummary: external_exports.string().optional(),
17794
+ idempotencyKey: external_exports.string().optional()
17795
+ };
17796
+ var PublishIssuerSignedRefreshValidator = external_exports.object({
17797
+ ...PublishCredentialRefreshBaseFields,
17798
+ mode: external_exports.literal("issuer-signed"),
17799
+ signedCredential: VCValidator
17800
+ });
17801
+ var PublishSigningAuthorityRefreshValidator = external_exports.object({
17802
+ ...PublishCredentialRefreshBaseFields,
17803
+ mode: external_exports.literal("signing-authority"),
17804
+ credential: UnsignedVCValidator,
17805
+ signingAuthority: external_exports.object({
17806
+ type: external_exports.string().min(1)
17807
+ }).catchall(external_exports.any())
17808
+ });
17809
+ var PublishCredentialRefreshInputValidator = external_exports.object({
17810
+ ...PublishCredentialRefreshBaseFields,
17811
+ mode: CredentialRefreshSigningModeValidator,
17812
+ signedCredential: VCValidator.optional(),
17813
+ credential: UnsignedVCValidator.optional(),
17814
+ signingAuthority: external_exports.object({
17815
+ type: external_exports.string().min(1)
17816
+ }).catchall(external_exports.any()).optional()
17817
+ }).superRefine((input, ctx) => {
17818
+ if (input.mode === "issuer-signed" && !input.signedCredential) {
17819
+ ctx.addIssue({
17820
+ code: "custom",
17821
+ path: ["signedCredential"],
17822
+ message: "signedCredential is required for issuer-signed publication"
17823
+ });
17824
+ }
17825
+ if (input.mode === "issuer-signed" && (input.credential !== void 0 || input.signingAuthority !== void 0)) {
17826
+ ctx.addIssue({
17827
+ code: "custom",
17828
+ path: ["mode"],
17829
+ message: "issuer-signed publication cannot include signing-authority fields"
17830
+ });
17831
+ }
17832
+ if (input.mode === "signing-authority") {
17833
+ if (input.signedCredential !== void 0) {
17834
+ ctx.addIssue({
17835
+ code: "custom",
17836
+ path: ["signedCredential"],
17837
+ message: "signing-authority publication cannot include signedCredential"
17838
+ });
17839
+ }
17840
+ if (!input.credential) {
17841
+ ctx.addIssue({
17842
+ code: "custom",
17843
+ path: ["credential"],
17844
+ message: "credential is required for signing-authority publication"
17845
+ });
17846
+ }
17847
+ if (!input.signingAuthority) {
17848
+ ctx.addIssue({
17849
+ code: "custom",
17850
+ path: ["signingAuthority"],
17851
+ message: "signingAuthority is required for signing-authority publication"
17852
+ });
17853
+ }
17854
+ }
17855
+ });
17856
+ var PublishCredentialRefreshNotificationValidator = external_exports.enum([
17857
+ "queued",
17858
+ "suppressed",
17859
+ "not-applicable",
17860
+ /** Publication succeeded, but the post-commit notification enqueue must be retried. */
17861
+ "delivery-failed"
17862
+ ]);
17863
+ var PublishCredentialRefreshResultValidator = external_exports.object({
17864
+ refreshId: external_exports.string().min(1),
17865
+ version: external_exports.number().int().positive(),
17866
+ publishedAt: external_exports.string().min(1),
17867
+ notification: PublishCredentialRefreshNotificationValidator
17868
+ });
17869
+ var CredentialRefreshVersionMetadataValidator = external_exports.object({
17870
+ version: external_exports.number().int().positive(),
17871
+ publishedAt: external_exports.string().min(1),
17872
+ effectiveAt: external_exports.string().optional(),
17873
+ etag: external_exports.string().optional(),
17874
+ signingMode: CredentialRefreshSigningModeValidator.optional(),
17875
+ updateSummary: external_exports.string().optional()
17876
+ });
17877
+ var GetCredentialRefreshHistoryInputValidator = external_exports.object({
17878
+ refreshId: external_exports.string().min(1),
17879
+ cursor: external_exports.string().optional(),
17880
+ limit: external_exports.number().int().positive().optional()
17881
+ });
17882
+ var GetCredentialRefreshHistoryResultValidator = external_exports.object({
17883
+ records: CredentialRefreshVersionMetadataValidator.array(),
17884
+ hasMore: external_exports.boolean(),
17885
+ cursor: external_exports.string().optional()
17886
+ });
17887
+ var CredentialRefreshChallengeValidator = external_exports.object({
17888
+ challenge: external_exports.string().min(1),
17889
+ expiresAt: external_exports.string().min(1),
17890
+ domain: external_exports.string().optional(),
17891
+ scheme: external_exports.literal("LearnCardDIDAuth").optional()
17892
+ });
17893
+ var PublicCredentialRefreshEnvelopeValidator = external_exports.object({
17894
+ format: external_exports.literal("vc"),
17895
+ credential: VCValidator,
17896
+ etag: external_exports.string().optional()
17897
+ });
17898
+ var JweCredentialRefreshEnvelopeValidator = external_exports.object({
17899
+ format: external_exports.literal("jwe"),
17900
+ jwe: JWEValidator,
17901
+ etag: external_exports.string().optional(),
17902
+ /** Present on LearnCard-managed endpoints; optional for interoperable JWE services. */
17903
+ version: external_exports.number().int().positive().optional()
17904
+ });
17905
+ var CredentialRefreshResponseEnvelopeValidator = external_exports.discriminatedUnion("format", [
17906
+ PublicCredentialRefreshEnvelopeValidator,
17907
+ JweCredentialRefreshEnvelopeValidator
17908
+ ]);
17909
+ var CredentialRefreshFailureCodeValidator = external_exports.enum([
17910
+ "UNAVAILABLE",
17911
+ "TIMEOUT",
17912
+ "UNSUPPORTED_SERVICE",
17913
+ "UNAUTHORIZED",
17914
+ "MALFORMED_RESPONSE",
17915
+ "INVALID_PROOF",
17916
+ "ISSUER_MISMATCH",
17917
+ "ID_MISMATCH",
17918
+ "ROLLBACK",
17919
+ "REVOKED",
17920
+ "UNSAFE_ENDPOINT"
17921
+ ]);
17922
+ var CredentialRefreshUpdatedResultValidator = external_exports.object({
17923
+ status: external_exports.literal("updated"),
17924
+ credential: VCValidator,
17925
+ etag: external_exports.string().optional(),
17926
+ managedVersion: external_exports.number().int().positive().optional()
17927
+ });
17928
+ var CredentialRefreshUnchangedResultValidator = external_exports.object({
17929
+ status: external_exports.literal("unchanged"),
17930
+ checkedAt: external_exports.string().min(1),
17931
+ etag: external_exports.string().optional()
17932
+ });
17933
+ var CredentialRefreshUnsupportedResultValidator = external_exports.object({
17934
+ status: external_exports.literal("unsupported")
17935
+ });
17936
+ var CredentialRefreshFailedResultValidator = external_exports.object({
17937
+ status: external_exports.literal("failed"),
17938
+ code: CredentialRefreshFailureCodeValidator,
17939
+ retryable: external_exports.boolean()
17940
+ });
17941
+ var CredentialRefreshResultValidator = external_exports.discriminatedUnion("status", [
17942
+ CredentialRefreshUpdatedResultValidator,
17943
+ CredentialRefreshUnchangedResultValidator,
17944
+ CredentialRefreshUnsupportedResultValidator,
17945
+ CredentialRefreshFailedResultValidator
17946
+ ]);
17695
17947
  }
17696
17948
  });
17697
17949